diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff4654ebf21a1..14e7fb37f1e78 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,10 +141,6 @@ All docstrings are written inline above the methods or types they are associated 4. check the output in `doc/_build/html/` to make sure the changes are correct; 5. commit your changes and open a pull request. -> **Note** -> -> Currently there are a large number of docstrings found in `base/docs/helpdb/Base.jl`. When any of these docstrings are modified please move them out of this file and place them above the most appropriate definition in one of the `base/` source files. - #### Adding a new docstring to `base/` The steps required to add a new docstring are listed below: @@ -196,7 +192,7 @@ The Julia community uses [GitHub issues](https://github.com/JuliaLang/julia/issu Note: These instructions are for adding to or improving functionality in the base library. Before getting started, it can be helpful to discuss the proposed changes or additions on the [Julia Discourse forum](https://discourse.julialang.org) or in a GitHub issue---it's possible your proposed change belongs in a package rather than the core language. Also, keep in mind that changing stuff in the base can potentially break a lot of things. Finally, because of the time required to build Julia, note that it's usually faster to develop your code in stand-alone files, get it working, and then migrate it into the base libraries. -Add new code to Julia's base libraries as follows: +Add new code to Julia's base libraries as follows (this is the "basic" approach; see a more efficient approach in the next section): 1. Edit the appropriate file in the `base/` directory, or add new files if necessary. Create tests for your functionality and add them to files in the `test/` directory. If you're editing C or Scheme code, most likely it lives in `src/` or one of its subdirectories, although some aspects of Julia's REPL initialization live in `ui/`. @@ -218,6 +214,55 @@ or with the `runtests.jl` script, e.g. to run `test/bitarray.jl` and `test/math. Make sure that [Travis](http://www.travis-ci.org) greenlights the pull request with a [`Good to merge` message](http://blog.travis-ci.com/2012-09-04-pull-requests-just-got-even-more-awesome). +#### Modifying base more efficiently with Revise.jl + +[Revise](https://github.com/timholy/Revise.jl) is a package that +tracks changes in source files and automatically updates function +definitions in your running Julia session. Using it, you can make +extensive changes to Base without needing to rebuild in order to test +your changes. + +Here is the standard procedure: + +1. If you are planning changes to any types or macros, make those + changes, commit them, and build julia using `make`. (This is + necessary because `Revise` cannot handle changes to type + definitions or macros.) By making a git commit, you "shield" these + changes from the `git stash` procedure described below. Unless it's + required to get Julia to build, you do not have to add any + functionality based on the new types, just the type definitions + themselves. + +2. Start a Julia REPL session. Then issue the following commands: + +```julia +using Revise # if you aren't launching it in your .juliarc.jl +Revise.track(Base) +``` + +3. Edit files in `base/`, save your edits, and test the + functionality. Once you are satisfied that things work as desired, + make another commit and rebuild julia. + +Should you for some reason need to quit and restart your REPL session +before finishing your changes, the procedure above will fail because +`Revise.track` +[cannot detect changes in source files that occurred after Julia was built](https://github.com/JuliaLang/julia/issues/23448)---it +will only detect changes to source files that occur after tracking is +initiated. Consequently, any changes made prior to +`Revise.track(Base)` will not be incorporated into your new REPL +session. You can work around this by temporarily reverting all source +files to their original state. From somewhere in the `julia` +directory, start your REPL session and do the following: + +```julia +shell> git stash # ensure that the code in `base/` matches its state when you built julia + +julia> Revise.track(Base) # Revise's source code cache is synchronized with what's running + +shell> git stash pop # restore any in-progress changes (will now be tracked) +``` + ### Code Formatting Guidelines #### General Formatting Guidelines for Julia code contributions diff --git a/Makefile b/Makefile index 3ce18c5d0d42f..fd04142297f50 100644 --- a/Makefile +++ b/Makefile @@ -187,6 +187,7 @@ CORE_SRCS := $(addprefix $(JULIAHOME)/, \ base/array.jl \ base/bool.jl \ base/associative.jl \ + base/codevalidation.jl \ base/error.jl \ base/essentials.jl \ base/generator.jl \ @@ -209,7 +210,7 @@ BASE_SRCS := $(sort $(shell find $(JULIAHOME)/base -name \*.jl) $(shell find $(B $(build_private_libdir)/inference.ji: $(CORE_SRCS) | $(build_private_libdir) @$(call PRINT_JULIA, cd $(JULIAHOME)/base && \ - $(call spawn,$(JULIA_EXECUTABLE)) -C $(JULIA_CPU_TARGET) --output-ji $(call cygpath_w,$@) \ + $(call spawn,$(JULIA_EXECUTABLE)) -C "$(JULIA_CPU_TARGET)" --output-ji $(call cygpath_w,$@) \ --startup-file=no -g0 -O0 coreimg.jl) RELBUILDROOT := $(shell $(JULIAHOME)/contrib/relative_path.sh "$(JULIAHOME)/base" "$(BUILDROOT)/base/") @@ -217,8 +218,8 @@ COMMA:=, define sysimg_builder $$(build_private_libdir)/sys$1.o: $$(build_private_libdir)/inference.ji $$(JULIAHOME)/VERSION $$(BASE_SRCS) @$$(call PRINT_JULIA, cd $$(JULIAHOME)/base && \ - $$(call spawn,$3) $2 -C $$(JULIA_CPU_TARGET) --output-o $$(call cygpath_w,$$@) $$(JULIA_SYSIMG_BUILD_FLAGS) \ - --startup-file=no --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT) \ + $$(call spawn,$3) $2 -C "$$(JULIA_CPU_TARGET)" --output-o $$(call cygpath_w,$$@) $$(JULIA_SYSIMG_BUILD_FLAGS) \ + --startup-file=no --warn-overwrite=yes --sysimage $$(call cygpath_w,$$<) sysimg.jl $$(RELBUILDROOT) \ || { echo '*** This error is usually fixed by running `make clean`. If the error persists$$(COMMA) try `make cleanall`. ***' && false; } ) .SECONDARY: $(build_private_libdir)/sys$1.o endef @@ -550,9 +551,9 @@ test: check-whitespace $(JULIA_BUILD_MODE) @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test default JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) ifeq ($(JULIA_BUILD_MODE),release) -JULIA_SYSIMG=$(build_private_libdir)/sys$(JULIA_LIBSUFFIX).$(SHLIB_EXT) +JULIA_SYSIMG=$(build_private_libdir)/sys$(JULIA_LIBSUFFIX)$(CPUID_TAG).$(SHLIB_EXT) else -JULIA_SYSIMG=$(build_private_libdir)/sys-$(JULIA_BUILD_MODE)$(JULIA_LIBSUFFIX).$(SHLIB_EXT) +JULIA_SYSIMG=$(build_private_libdir)/sys-$(JULIA_BUILD_MODE)$(JULIA_LIBSUFFIX)$(CPUID_TAG).$(SHLIB_EXT) endif testall: check-whitespace $(JULIA_BUILD_MODE) cp $(JULIA_SYSIMG) $(BUILDROOT)/local.$(SHLIB_EXT) && $(JULIA_EXECUTABLE) -J $(call cygpath_w,$(BUILDROOT)/local.$(SHLIB_EXT)) -e 'true' && rm $(BUILDROOT)/local.$(SHLIB_EXT) diff --git a/NEWS.md b/NEWS.md index 65dbd86392592..8b7a481d5bb5a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,10 @@ New language features * Local variables can be tested for being defined using the new `@isdefined variable` macro ([#22281]). + * Destructuring in function arguments: when an expression such as `(x, y)` is used as + a function argument name, the argument is unpacked into local variables `x` and `y` + as in the assignment `(x, y) = arg` ([#6614]). + Language changes ---------------- @@ -19,6 +23,9 @@ Language changes * In string and character literals, backslash `\` may no longer precede unrecognized escape characters ([#22800]). + * Juxtaposing binary, octal, and hexadecimal literals is deprecated, since it can lead to + confusing code such as `0xapi == 0xa * pi` ([#16356]). + * Declaring arguments as `x::ANY` to avoid specialization has been replaced by `@nospecialize x`. ([#22666]). @@ -35,6 +42,10 @@ Language changes * Nested `if` expressions that arise from the keyword `elseif` now use `elseif` as their expression head instead of `if` ([#21774]). + * `let` blocks now parse the same as `for` loops; the first argument is either an + assignment or `block` of assignments, and the second argument is a block of + statements ([#21774]). + * Parsed and lowered forms of type definitions have been synchronized with their new keywords ([#23157]). Expression heads are renamed as follows: @@ -63,6 +74,26 @@ Language changes (`need_to_handle_undef_sparam = Set{Any}(m.sig for m in Test.detect_unbound_args(Base, recursive=true))`) is equal (`==`) to some known set (`expected = Set()`). ([#23117]) + * `const` declarations on local variables were previously ignored. They now give a + warning, so that this syntax can be disallowed or given a new meaning in a + future version ([#5148]). + + * Placing an expression after `catch`, as in `catch f(x)`, is deprecated. + Use `catch; f(x)` instead ([#19987]). + + * In `for i = ...`, if a local variable `i` already existed it would be overwritten + during the loop. This behavior is deprecated, and in the future `for` loop variables + will always be new variables local to the loop ([#22314]). + The old behavior of overwriting an existing variable is available via `for outer i = ...`. + + * In `for i in x`, `x` used to be evaluated in a new scope enclosing the `for` loop. + Now it is evaluated in the scope outside the `for` loop. + + * Variable bindings local to `while` loop bodies are now freshly allocated on each loop iteration, + matching the behavior of `for` loops. + + * Prefix `&` for by-reference arguments to `ccall` has been deprecated in favor of + `Ref` argument types ([#6080]). Breaking changes ---------------- @@ -115,10 +146,15 @@ This section lists changes that do not have deprecation warnings. longer present. Use `first(R)` and `last(R)` to obtain start/stop. ([#20974]) - * The `Diagonal`, `Bidiagonal` and `SymTridiagonal` type definitions have changed from - `Diagonal{T}`, `Bidiagonal{T}` and `SymTridiagonal{T}` to `Diagonal{T,V<:AbstractVector{T}}`, - `Bidiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` - respectively ([#22718], [#22925], [#23035]). + * The `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` type definitions have + changed from `Diagonal{T}`, `Bidiagonal{T}`, `Tridiagonal{T}` and `SymTridiagonal{T}` + to `Diagonal{T,V<:AbstractVector{T}}`, `Bidiagonal{T,V<:AbstractVector{T}}`, + `Tridiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` + respectively ([#22718], [#22925], [#23035], [#23154]). + + * When called with an argument that contains `NaN` elements, `findmin` and `findmax` now return the + first `NaN` found and its corresponding index. Previously, `NaN` elements were ignored. + The new behavior matches that of `min`, `max`, `minimum`, and `maximum`. * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero @@ -140,6 +176,32 @@ This section lists changes that do not have deprecation warnings. * Worker-worker connections are setup lazily for an `:all_to_all` topology. Use keyword arg `lazy=false` to force all connections to be setup during a `addprocs` call. ([#22814]) + * In `joinpath(a, b)` on Windows, if the drive specifications of `a` and `b` do not match, + `joinpath` now returns `b` instead of throwing an `ArgumentError`. `joinpath(path...)` is + defined to be left associative, so if any argument has a drive path which does not match + the drive of the join of the preceding paths, the prior ones are dropped. ([#20912]) + + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now throws a `DomainError` + if `p < 0`, unless `A == one(A)` or `A == -one(A)` (same as for + `^(A::Integer, p::Integer)`) ([#23366]). + + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now promotes the element type in the same + way as `^(A::Integer, p::Integer)`. This means, for instance, that `[1 1; 0 1]^big(1)` + will return a `Matrix{BigInt}` instead of a `Matrix{Int}` ([#23366]). + + * The element type of the input is now preserved in `unique`. Previously the element type + of the output was shrunk to fit the union of the type of each element in the input. + ([#22696]) + + * The `promote` function now raises an error if its arguments are of different types + and if attempting to convert them to a common type fails to change any of their types. + This avoids stack overflows in the common case of definitions like + `f(x, y) = f(promote(x, y)...)` ([#22801]). + + * `findmin`, `findmax`, `indmin`, and `indmax` used to always return linear indices. + They now return `CartesianIndex`es for all but 1-d arrays, and in general return + the `keys` of indexed collections (e.g. dictionaries) ([#22907]). + Library improvements -------------------- @@ -166,7 +228,7 @@ Library improvements * The `crc32c` function for CRC-32c checksums is now exported ([#22274]). - * The output of `versioninfo()` is now controlled with keyword arguments ([#21974]). + * The output of `versioninfo` is now controlled with keyword arguments ([#21974]). * The function `LibGit2.set_remote_url` now always sets both the fetch and push URLs for a git repo. Additionally, the argument order was changed to be consistent with the git @@ -190,13 +252,18 @@ Library improvements * `Char`s can now be concatenated with `String`s and/or other `Char`s using `*` ([#22532]). - * `Diagonal`, `Bidiagonal` and `SymTridiagonal` are now parameterized on the type of the - wrapped vectors, allowing `Diagonal`, `Bidiagonal` and `SymTridiagonal` matrices with - arbitrary `AbstractVector`s ([#22718], [#22925], [#23035]). + * `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` are now parameterized on + the type of the wrapped vectors, allowing `Diagonal`, `Bidiagonal`, `Tridiagonal` and + `SymTridiagonal` matrices with arbitrary `AbstractVector`s + ([#22718], [#22925], [#23035], [#23154]). * Mutating versions of `randperm` and `randcycle` have been added: `randperm!` and `randcycle!` ([#22723]). + * `BigFloat` random numbers can now be generated ([#22720]). + + * REPL Undo via Ctrl-/ and Ctrl-_ + Compiler/Runtime improvements ----------------------------- @@ -255,8 +322,9 @@ Deprecated or removed * `Bidiagonal` constructors now use a `Symbol` (`:U` or `:L`) for the upper/lower argument, instead of a `Bool` or a `Char` ([#22703]). - * `Bidiagonal` and `SymTridiagonal` constructors that automatically converted the input - vectors to the same type are deprecated in favor of explicit conversion ([#22925], [#23035]). + * `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` constructors that automatically + converted the input vectors to the same type are deprecated in favor of explicit + conversion ([#22925], [#23035], [#23154]. * Calling `nfields` on a type to find out how many fields its instances have is deprecated. Use `fieldcount` instead. Use `nfields` only to get the number of fields in a specific object ([#22350]). @@ -300,6 +368,12 @@ Deprecated or removed full path if you need access to executables or libraries in the `JULIA_HOME` directory, e.g. `joinpath(JULIA_HOME, "7z.exe")` for `7z.exe` ([#21540]). + * `sqrtm` has been deprecated in favor of `sqrt` ([#23504]). + + * `expm` has been deprecated in favor of `exp` ([#23233]). + + * `logm` has been deprecated in favor of `log` ([#23505]). + * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). @@ -309,6 +383,54 @@ Deprecated or removed * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` instead ([#23187]). + * `ctranspose` and `ctranspose!` have been deprecated in favor of `adjoint` and `adjoint!`, + respectively ([#23235]). + + * `filter` and `filter!` on dictionaries now pass a single `key=>value` pair to the + argument function, instead of two arguments ([#17886]). + + * `rol`, `rol!`, `ror`, and `ror!` have been deprecated in favor of specialized methods for + `circshift`/`circshift!` ([#23404]). + + * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). + + * The tuple-of-types form of `cfunction`, `cfunction(f, returntype, (types...))`, has been deprecated + in favor of the tuple-type form `cfunction(f, returntype, Tuple{types...})` ([#23066]). + + * `diagm(A::SparseMatrixCSC)` has been deprecated in favor of + `spdiagm(sparsevec(A))` ([#23341]). + + * `diagm(A::BitMatrix)` has been deprecated, use `diagm(vec(A))` instead ([#23373]). + + * `ℯ` (written as `\mscre` or `\euler`) is now the only (by default) exported + name for Euler's number, and the type has changed from `Irrational{:e}` to + `Irrational{:ℯ}` ([#23427]). + + * The mathematical constants `π`, `pi`, `ℯ`, `e`, `γ`, `eulergamma`, `catalan`, `φ` and + `golden` have been have been moved from `Base` to a new module; `Base.MathConstants`. + Only `π`, `pi` and `ℯ` are now exported by default from `Base` ([#23427]). + + * `eu` (previously an alias for `ℯ`) has been deprecated in favor of `ℯ` (or `MathConstants.e`) ([#23427]). + + * `GMP.gmp_version()`, `GMP.GMP_VERSION`, `GMP.gmp_bits_per_limb()`, and `GMP.GMP_BITS_PER_LIBM` + have been renamed to `GMP.version()`, `GMP.VERSION`, `GMP.bits_per_libm()`, and `GMP.BITS_PER_LIBM`, + respectively. Similarly, `MPFR.get_version()`, has been renamed to `MPFR.version()` ([#23323]). Also, + `LinAlg.LAPACK.laver()` has been renamed to `LinAlg.LAPACK.version()` and now returns a `VersionNumber`. + + * `select`, `select!`, `selectperm` and `selectperm!` have been renamed respectively to + `partialsort`, `partialsort!`, `partialsortperm` and `partialsortperm!` ([#23051]). + +Command-line option changes +--------------------------- + + * New option `--warn-overwrite={yes|no}` to control the warning for overwriting method + definitions. The default is `no` ([#23002]). + + * New option `--banner={yes,no}` allows suppressing or forcing the printing of the + startup banner, overriding the default behavior (banner in REPL, no banner otherwise). + The `--quiet` option implies `--banner=no` even in REPL mode but can be overridden by + passing `--quiet` together with `--banner=yes` ([#23342]). + Julia v0.6.0 Release Notes ========================== @@ -780,7 +902,7 @@ Deprecated or removed `pop!(ENV, k, def)`. Be aware that `pop!` returns `k` or `def`, whereas `delete!` returns `ENV` or `def` ([#18012]). - * infix operator `$` has been deprecated in favor of infix `⊻` or function `xor()` ([#18977]). + * infix operator `$` has been deprecated in favor of infix `⊻` or function `xor` ([#18977]). * The single-argument form of `write` (`write(x)`, with implicit `STDOUT` output stream), has been deprecated in favor of the explicit equivalent `write(STDOUT, x)` ([#17654]). @@ -1141,6 +1263,7 @@ Command-line option changes [#22588]: https://github.com/JuliaLang/julia/issues/22588 [#22605]: https://github.com/JuliaLang/julia/issues/22605 [#22666]: https://github.com/JuliaLang/julia/issues/22666 +[#22696]: https://github.com/JuliaLang/julia/issues/22696 [#22703]: https://github.com/JuliaLang/julia/issues/22703 [#22712]: https://github.com/JuliaLang/julia/issues/22712 [#22718]: https://github.com/JuliaLang/julia/issues/22718 @@ -1165,3 +1288,6 @@ Command-line option changes [#23157]: https://github.com/JuliaLang/julia/issues/23157 [#23187]: https://github.com/JuliaLang/julia/issues/23187 [#23207]: https://github.com/JuliaLang/julia/issues/23207 +[#23233]: https://github.com/JuliaLang/julia/issues/23233 +[#23342]: https://github.com/JuliaLang/julia/issues/23342 +[#23404]: https://github.com/JuliaLang/julia/issues/23404 diff --git a/README.md b/README.md index d6b9b28893ded..cf9e3413bf7e0 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,14 @@ Julia is built and tested regularly on the following platforms: | | ARM v7 (32-bit) | | ✓ | Official | | | ARM v8 (64-bit) | | ✓ | Official | | | PowerPC (64-bit) | | | Community | +| | PTX (64-bit) | [✓](http://ci.maleadt.net:8010/) | | [External](https://github.com/JuliaGPU/CUDAnative.jl) | | macOS 10.8+ | x86-64 (64-bit) | ✓ | ✓ | Official | | Windows 7+ | x86-64 (64-bit) | ✓ | ✓ | Official | | | i686 (32-bit) | ✓ | ✓ | Official | | FreeBSD 11.0+ | x86-64 (64-bit) | ✓ | | Community | All systems marked with ✓ for CI are tested using continuous integration for every commit. -Systems with ✓ for binaries have official binaries available on the [downloads](https://julialang.org/downloads) page and are tested regularly. +Systems with ✓ for binaries have official binaries available on the [downloads](https://julialang.org/downloads) page and are tested regularly. The PTX backend needs a source build and the [CUDAnative.jl](https://github.com/JuliaGPU/CUDAnative.jl) package. The systems listed here with neither CI nor official binaries are known to build and work, but ongoing support for those platforms is dependent on community efforts. It's possible that Julia will build and work on other platforms too, and we're always looking to better our platform coverage. If you're using Julia on a platform not listed here, let us know! @@ -87,11 +88,11 @@ Building Julia requires 1.5GiB of disk space and approximately 700MiB of virtual For builds of julia starting with 0.5.0-dev, you can create out-of-tree builds of Julia by specifying `make O= configure` on the command line. This will create a directory mirror, with all of the necessary Makefiles to build Julia, in the specified directory. These builds will share the source files in Julia and `deps/srccache`. Each out-of-tree build directory can have its own `Make.user` file to override the global `Make.user` file in the top-level folder. -If you need to build Julia in an environment that does not allow access to the outside world, use `make -C deps getall` to download all the necessary files. Then, copy the `julia` directory over to the target environment and build with `make`. +If you need to build Julia on a machine without internet access, use `make -C deps getall` to download all the necessary files. Then, copy the `julia` directory over to the target environment and build with `make`. -**Note:** the build process will fail badly if any of the build directory's parent directories have spaces or other shell meta-characters such as `$` or `:` in their names (this is due to a limitation in GNU make). +**Note:** The build process will fail badly if any of the build directory's parent directories have spaces or other shell meta-characters such as `$` or `:` in their names (this is due to a limitation in GNU make). -Once it is built, you can run the `julia` executable using its full path in the directory created above (the `julia` directory), or, to run it from anywhere, either +Once it is built, you can run the `julia` executable using its full path in the directory created above (the `julia` directory). To run julia from anywhere you can: - add an alias (in `bash`: `echo "alias julia='/path/to/install/folder/bin/julia'" >> ~/.bashrc && source ~/.bashrc`), or - add a soft link to the `julia` executable in the `julia` directory to `/usr/local/bin` (or any suitable directory already in your path), or @@ -106,13 +107,11 @@ Now you should be able to run Julia like this: julia -If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. (Errors related to libraries might be caused by old, incompatible libraries sitting around in your PATH. In that case, try moving the `julia` directory earlier in the PATH). +If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. (Errors related to libraries might be caused by old, incompatible libraries sitting around in your PATH. In this case, try moving the `julia` directory earlier in the PATH). -Your first test of Julia should be to determine whether your -build is working properly. From the UNIX/Windows command prompt inside -the `julia` source directory, type `make testall`. You should see output -that lists a series of tests being run; if they complete without -error, you should be in good shape to start using Julia. +Your first test of Julia determines whether your build is working properly. From the UNIX/Windows command prompt inside +the `julia` source directory, type `make testall`. You should see output that lists a series of running tests; +if they complete without error, you should be in good shape to start using Julia. You can read about [getting started](https://docs.julialang.org/en/stable/manual/getting-started/) in the manual. @@ -258,6 +257,9 @@ Some known issues on FreeBSD are: * The x86 architecture does not support threading due to lack of compiler runtime library support, so you may need to set `JULIA_THREADS=0` in your `Make.user` if you're on a 32-bit system. +* The `Pkg` test suite segfaults on FreeBSD 11.1, likely due to a change in FreeBSD's default handling of stack guarding. + See [issue #23328](https://github.com/JuliaLang/julia/issues/23328) for more information. + ### Windows In order to build Julia on Windows, see [README.windows](https://github.com/JuliaLang/julia/blob/master/README.windows.md). @@ -403,26 +405,9 @@ On Windows, double-click `usr/bin/julia.exe`. If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. You can read about [getting started](https://julialang.org/manual/getting-started) in the manual. -The following distributions include julia, but the versions may be out of date due to rapid development: - -* [Alpine Linux](http://pkgs.alpinelinux.org/package/edge/testing/x86_64/julia) -* [Arch Linux](https://www.archlinux.org/packages/community/i686/julia/) -* [Debian GNU/Linux](http://packages.debian.org/sid/julia) -* [Fedora Linux](https://admin.fedoraproject.org/pkgdb/package/julia/), RHEL/CentOS/OEL/Scientific Linux (EPEL) - * [Current stable release for Fedora/EPEL](https://copr.fedoraproject.org/coprs/nalimilan/julia/) - * [Nightly builds for Fedora/EPEL](https://copr.fedoraproject.org/coprs/nalimilan/julia-nightlies/) -* [Gentoo Linux](https://packages.gentoo.org/package/dev-lang/julia) - * Git Package in the [Science overlay](https://wiki.gentoo.org/wiki/Project:Science/Overlay) -* openSUSE - * Stable package for openSUSE: [OBS page](https://build.opensuse.org/package/show/science/julia), [1 Click Install](http://software.opensuse.org/download.html?project=science&package=julia) - * Git package for openSUSE: [OBS page](https://build.opensuse.org/package/show/science/julia-unstable), [1 Click Install](http://software.opensuse.org/download.html?project=science&package=julia-unstable) -* [NixOS](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/compilers/julia) -* Ubuntu - * [Ubuntu](http://packages.ubuntu.com/search?keywords=julia) - * [Nightly builds PPA](https://launchpad.net/~staticfloat/+archive/julianightlies) (depends on the [julia-deps PPA](https://launchpad.net/~staticfloat/+archive/julia-deps/)) -* [MacPorts](https://trac.macports.org/browser/trunk/dports/lang/julia/Portfile) -* [OS X Homebrew Tap](https://github.com/staticfloat/homebrew-julia/) -* [FreeBSD Ports](https://www.freshports.org/lang/julia/) +**Note**: While some system package managers have Julia installers available, +these are not maintained nor endorsed by the Julia project. They may be outdated +and/or unmaintained. We recommend you use the official Julia binaries instead. ## Editor and Terminal Setup @@ -434,8 +419,10 @@ editors. While Julia modes for others such as Textmate, Notepad++, and Kate, are in `contrib/`. -Two major IDEs are supported for Julia: [Juno](http://junolab.org/), -which is based on [Atom](https://atom.io/), and +Three major IDEs are supported for Julia: [Juno](http://junolab.org/) +which is based on [Atom](https://atom.io/), +[julia-vscode](https://github.com/JuliaEditorSupport/julia-vscode) +based on [VS Code](https://code.visualstudio.com/), and [JuliaDT](https://github.com/JuliaComputing/JuliaDT), which is an [Eclipse](http://eclipse.org) plugin. A [Jupyter](http://jupyter.org/) notebooks interface is available through diff --git a/README.windows.md b/README.windows.md index 05a31ee1f3104..92c2443b83914 100644 --- a/README.windows.md +++ b/README.windows.md @@ -80,8 +80,8 @@ The recommended way of compiling Julia from source on Windows is by cross compiling from [Cygwin](http://www.cygwin.com), using versions of the MinGW-w64 compilers available through Cygwin's package manager. - 1. Download and run Cygwin setup for [32 bit](http://cygwin.com/setup-x86.exe) - or [64 bit](http://cygwin.com/setup-x86_64.exe). Note, that you can compile + 1. Download and run Cygwin setup for [32 bit](https://cygwin.com/setup-x86.exe) + or [64 bit](https://cygwin.com/setup-x86_64.exe). Note, that you can compile either 32 or 64 bit Julia from either 32 or 64 bit Cygwin. 64 bit Cygwin has a slightly smaller but often more up-to-date selection of packages. diff --git a/base/abstractarray.jl b/base/abstractarray.jl index a74127c3c770f..c5847e584038c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -69,9 +69,9 @@ function indices(A) map(OneTo, size(A)) end -# Performance optimization: get rid of a branch on `d` in `indices(A, -# d)` for d=1. 1d arrays are heavily used, and the first dimension -# comes up in other applications. +# Performance optimization: get rid of a branch on `d` in `indices(A, d)` +# for d=1. 1d arrays are heavily used, and the first dimension comes up +# in other applications. indices1(A::AbstractArray{<:Any,0}) = OneTo(1) indices1(A::AbstractArray) = (@_inline_meta; indices(A)[1]) indices1(iter) = OneTo(length(iter)) @@ -103,6 +103,13 @@ julia> extrema(b) """ linearindices(A) = (@_inline_meta; OneTo(_length(A))) linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) + +keys(a::AbstractArray) = CartesianRange(indices(a)) +keys(a::AbstractVector) = linearindices(a) + +prevind(::AbstractArray, i::Integer) = Int(i)-1 +nextind(::AbstractArray, i::Integer) = Int(i)+1 + eltype(::Type{<:AbstractArray{E}}) where {E} = E elsize(::AbstractArray{T}) where {T} = sizeof(T) @@ -123,10 +130,42 @@ ndims(::AbstractArray{T,N}) where {T,N} = N ndims(::Type{AbstractArray{T,N}}) where {T,N} = N ndims(::Type{T}) where {T<:AbstractArray} = ndims(supertype(T)) +""" + length(collection) -> Integer + +Return the number of elements in the collection. + +Use [`endof`](@ref) to get the last valid index of an indexable collection. + +# Examples +```jldoctest +julia> length(1:5) +5 + +julia> length([1, 2, 3, 4]) +4 + +julia> length([1 2; 3 4]) +4 +``` +""" length(t::AbstractArray) = (@_inline_meta; prod(size(t))) _length(A::AbstractArray) = (@_inline_meta; prod(map(unsafe_length, indices(A)))) # circumvent missing size _length(A) = (@_inline_meta; length(A)) + +""" + endof(collection) -> Integer + +Returns the last index of the collection. + +# Examples +```jldoctest +julia> endof([1,2,4]) +3 +``` +""" endof(a::AbstractArray) = (@_inline_meta; last(linearindices(a))) + first(a::AbstractArray) = a[first(eachindex(a))] """ @@ -386,47 +425,12 @@ function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) @_inline_meta checkindex(Bool, IA[1], I[1]) & checkbounds_indices(Bool, tail(IA), tail(I)) end -checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true -function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{Any}) - @_inline_meta - checkindex(Bool, 1:1, I[1]) -end function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) @_inline_meta - checkindex(Bool, 1:1, I[1]) & checkbounds_indices(Bool, (), tail(I)) -end -function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any}) - @_inline_meta - checkindex(Bool, IA[1], I[1]) -end -function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any}) - @_inline_meta - checkbounds_linear_indices(Bool, IA, I[1]) -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{Vararg{OneTo}}, i) - @_inline_meta - if checkindex(Bool, IA[1], i) - return true - elseif checkindex(Bool, OneTo(trailingsize(IA)), i) # partial linear indexing - partial_linear_indexing_warning_lookup(length(IA)) - return true # TODO: Return false after the above function is removed in deprecated.jl - end - return false -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, i) - @_inline_meta - checkindex(Bool, IA[1], i) -end -function checkbounds_linear_indices(::Type{Bool}, IA::Tuple{Vararg{OneTo}}, i::Union{Slice,Colon}) - partial_linear_indexing_warning_lookup(length(IA)) - true + checkindex(Bool, OneTo(1), I[1]) & checkbounds_indices(Bool, (), tail(I)) end -function checkbounds_linear_indices(::Type{Bool}, - IA::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, i::Union{Slice,Colon}) - partial_linear_indexing_warning_lookup(length(IA)) - true -end -checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true +checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true +checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) @@ -759,8 +763,11 @@ start(A::AbstractArray) = (@_inline_meta; itr = eachindex(A); (itr, start(itr))) next(A::AbstractArray, i) = (@_propagate_inbounds_meta; (idx, s) = next(i[1], i[2]); (A[idx], (i[1], s))) done(A::AbstractArray, i) = (@_propagate_inbounds_meta; done(i[1], i[2])) +# `eachindex` is mostly an optimization of `keys` +eachindex(itrs...) = keys(itrs...) + # eachindex iterates over all indices. IndexCartesian definitions are later. -eachindex(A::Union{Number,AbstractVector}) = (@_inline_meta(); indices1(A)) +eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) """ eachindex(A...) @@ -829,6 +836,9 @@ end isempty(a::AbstractArray) = (_length(a) == 0) +# keys with an IndexStyle +keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) + ## Conversions ## convert(::Type{AbstractArray{T,N}}, A::AbstractArray{T,N}) where {T,N } = A @@ -973,14 +983,6 @@ function _to_subscript_indices(A::AbstractArray{T,N}, I::Int...) where {T,N} # T end _to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) = __to_subscript_indices(A, indices(A), J, Jrem) -# We allow partial linear indexing deprecation for OneTo arrays -function __to_subscript_indices(A::AbstractArray, ::Tuple{Vararg{OneTo}}, J::Tuple, Jrem::Tuple{}) - @_inline_meta - sz = _remaining_size(J, indices(A)) # compute trailing size (overlapping the final index) - (front(J)..., _unsafe_ind2sub(sz, last(J))...) # (maybe) extend the last index -end -# After the partial linear indexing deprecation is removed, this next method can -# become the new normal. For now, it's limited to non-OneTo arrays. function __to_subscript_indices(A::AbstractArray, ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{}) @_inline_meta @@ -1107,8 +1109,8 @@ 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(Array{promote_typeof(X...)}(length(X)), X) -hcat(X::Number...) = hvcat_fill(Array{promote_typeof(X...)}(1,length(X)), X) +vcat(X::Number...) = hvcat_fill(Vector{promote_typeof(X...)}(length(X)), X) +hcat(X::Number...) = hvcat_fill(Matrix{promote_typeof(X...)}(1,length(X)), X) typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Array{T,1}(length(X)), X) typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Array{T,2}(1,length(X)), X) @@ -1243,7 +1245,7 @@ function cat_t(dims, T::Type, X...) catdims = dims2cat(dims) shape = cat_shape(catdims, (), map(cat_size, X)...) A = cat_similar(X[1], T, shape) - if T <: Number && countnz(catdims) > 1 + if T <: Number && count(!iszero, catdims) > 1 fill!(A, zero(T)) end return _cat(A, shape, catdims, X...) @@ -1346,6 +1348,21 @@ hcat(X...) = cat(Val(2), X...) typed_vcat(T::Type, X...) = cat_t(Val(1), T, X...) typed_hcat(T::Type, X...) = cat_t(Val(2), T, X...) +""" + cat(dims, A...) + +Concatenate the input arrays along the specified dimensions in the iterable `dims`. For +dimensions not in `dims`, all input arrays should have the same size, which will also be the +size of the output array along that dimension. For dimensions in `dims`, the size of the +output array is the sum of the sizes of the input arrays along that dimension. If `dims` is +a single number, the different arrays are tightly stacked along that dimension. If `dims` is +an iterable containing several dimensions, this allows one to construct block diagonal +matrices and their higher-dimensional analogues by simultaneously increasing several +dimensions for every new input array and putting zero blocks elsewhere. For example, +`cat([1,2], matrices...)` builds a block diagonal matrix, i.e. a block matrix with +`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the +diagonal. +""" cat(catdims, A::AbstractArray{T}...) where {T} = cat_t(catdims, T, A...) # The specializations for 1 and 2 inputs are important @@ -1735,7 +1752,7 @@ _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 enumerate(IndexLinear(), ind) + for (i,idx) in pairs(IndexLinear(), ind) sub = ind2sub(inds, idx) for j = 1:N t[j][i] = sub[j] diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index 437fa54204943..a95c81a807fb9 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -178,8 +178,9 @@ circshift(a::AbstractArray, shiftamt::DimsInteger) = circshift!(similar(a), a, s """ circshift(A, shifts) -Circularly shift the data in an array. The second argument is a vector giving the amount to -shift in each dimension. +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. # Examples ```jldoctest @@ -203,6 +204,30 @@ julia> circshift(b, (-1,0)) 3 7 11 15 4 8 12 16 1 5 9 13 + +julia> a = BitArray([true, true, false, false, true]) +5-element BitArray{1}: + true + true + false + false + true + +julia> circshift(a, 1) +5-element BitArray{1}: + true + true + true + false + false + +julia> circshift(a, -1) +5-element BitArray{1}: + true + false + false + true + true ``` See also [`circshift!`](@ref). diff --git a/base/array.jl b/base/array.jl index 300512537ad8d..194f6f48da2f4 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2,6 +2,17 @@ ## array.jl: Dense arrays +""" + DimensionMismatch([msg]) + +The objects called do not have matching dimensionality. Optional argument `msg` is a +descriptive error string. +""" +mutable struct DimensionMismatch <: Exception + msg::AbstractString +end +DimensionMismatch() = DimensionMismatch("") + ## Type aliases for convenience ## """ AbstractVector{T} @@ -98,8 +109,28 @@ size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val( asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) +""" + Base.isbitsunion(::Type{T}) + +Return whether a type is an "is-bits" Union type, meaning each type included in a Union is `isbits`. +""" +isbitsunion(u::Union) = ccall(:jl_array_store_unboxed, Cint, (Any,), u) == Cint(1) +isbitsunion(x) = false + +""" + Base.bitsunionsize(U::Union) + +For a Union of `isbits` types, return the size of the largest type; assumes `Base.isbitsunion(U) == true` +""" +function bitsunionsize(u::Union) + sz = Ref{Csize_t}(0) + algn = Ref{Csize_t}(0) + @assert ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), u, sz, algn) == Cint(1) + return sz[] +end + length(a::Array) = arraylen(a) -elsize(a::Array{T}) where {T} = isbits(T) ? sizeof(T) : sizeof(Ptr) +elsize(a::Array{T}) where {T} = isbits(T) ? sizeof(T) : (isbitsunion(T) ? bitsunionsize(T) : sizeof(Ptr)) sizeof(a::Array) = Core.sizeof(a) function isassigned(a::Array, i::Int...) @@ -111,6 +142,16 @@ end ## copy ## +""" + unsafe_copy!(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_copy!(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. @@ -119,9 +160,27 @@ function unsafe_copy!(dest::Ptr{T}, src::Ptr{T}, n) where T return dest end +""" + unsafe_copy!(dest::Array, do, src::Array, so, N) + +Copy `N` elements from a source array to a destination, starting at offset `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. +""" function unsafe_copy!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T if isbits(T) unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n) + elseif isbitsunion(T) + ccall(:memmove, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + pointer(dest, doffs), pointer(src, soffs), n * Base.bitsunionsize(T)) + # copy selector bytes + ccall(:memmove, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + convert(Ptr{UInt8}, pointer(dest)) + length(dest) * Base.bitsunionsize(T) + doffs - 1, + convert(Ptr{UInt8}, pointer(src)) + length(src) * Base.bitsunionsize(T) + soffs - 1, + n) else ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), dest, pointer(dest, doffs), src, pointer(src, soffs), n) @@ -129,6 +188,12 @@ function unsafe_copy!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T return dest end +""" + copy!(dest, do, src, so, N) + +Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at +offset `do`. Returns `dest`. +""" function copy!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T n == 0 && return dest n > 0 || throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) @@ -140,10 +205,17 @@ end copy!(dest::Array{T}, src::Array{T}) where {T} = copy!(dest, 1, src, 1, length(src)) +""" + 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. +""" copy(a::T) where {T<:Array} = ccall(:jl_array_copy, Ref{T}, (Any,), a) function reinterpret(::Type{T}, a::Array{S,1}) where T where S - nel = Int(div(length(a)*sizeof(S),sizeof(T))) + nel = Int(div(length(a) * sizeof(S), sizeof(T))) # TODO: maybe check that remainder is zero? return reinterpret(T, a, (nel,)) end @@ -162,7 +234,7 @@ function reinterpret(::Type{T}, a::Array{S}, dims::NTuple{N,Int}) where T where end isbits(T) || throwbits(S, T, T) isbits(S) || throwbits(S, T, S) - nel = div(length(a)*sizeof(S),sizeof(T)) + nel = div(length(a) * sizeof(S), sizeof(T)) if prod(dims) != nel _throw_dmrsa(dims, nel) end @@ -260,7 +332,6 @@ function fill!(a::Array{T}, x) where T<:Union{Integer,AbstractFloat} return a end - """ fill(x, dims) @@ -284,6 +355,92 @@ dims)` will return an array filled with the result of evaluating `Foo()` once. fill(v, dims::Dims) = fill!(Array{typeof(v)}(dims), v) fill(v, dims::Integer...) = fill!(Array{typeof(v)}(dims...), v) +""" + zeros([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) + +Create an array of all zeros with the same layout as `A`, element type `T` and size `dims`. +The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. +For convenience `dims` may also be passed in variadic form. + +# Examples +```jldoctest +julia> zeros(1) +1-element Array{Float64,1}: + 0.0 + +julia> zeros(Int8, 2, 3) +2×3 Array{Int8,2}: + 0 0 0 + 0 0 0 + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> zeros(A) +2×2 Array{Int64,2}: + 0 0 + 0 0 + +julia> zeros(A, Float64) +2×2 Array{Float64,2}: + 0.0 0.0 + 0.0 0.0 + +julia> zeros(A, Bool, (3,)) +3-element Array{Bool,1}: + false + false + false +``` +See also [`ones`](@ref), [`similar`](@ref). +""" +function zeros end + +""" + ones([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) + +Create an array of all ones with the same layout as `A`, element type `T` and size `dims`. +The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. +For convenience `dims` may also be passed in variadic form. + +# Examples +```jldoctest +julia> ones(Complex128, 2, 3) +2×3 Array{Complex{Float64},2}: + 1.0+0.0im 1.0+0.0im 1.0+0.0im + 1.0+0.0im 1.0+0.0im 1.0+0.0im + +julia> ones(1,2) +1×2 Array{Float64,2}: + 1.0 1.0 + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> ones(A) +2×2 Array{Int64,2}: + 1 1 + 1 1 + +julia> ones(A, Float64) +2×2 Array{Float64,2}: + 1.0 1.0 + 1.0 1.0 + +julia> ones(A, Bool, (3,)) +3-element Array{Bool,1}: + true + true + true +``` +See also [`zeros`](@ref), [`similar`](@ref). +""" +function ones end + for (fname, felt) in ((:zeros,:zero), (:ones,:one)) @eval begin # allow signature of similar @@ -405,6 +562,12 @@ convert(::Type{Array{T,n}}, x::AbstractArray{S,n}) where {T,n,S} = copy!(Array{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 should make copies + +if module_name(@__MODULE__) === :Base # avoid method overwrite +(::Type{T})(x::T) where {T<:Array} = copy(x) +end + ## copying iterators to containers """ @@ -582,9 +745,28 @@ done(a::Array,i) = (@_inline_meta; i == length(a)+1) ## 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, ...)`. + +# 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 + # This is more complicated than it needs to be in order to get Win64 through bootstrap -getindex(A::Array, i1::Int) = arrayref(A, i1) -getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref(A, i1, i2, I...)) # TODO: REMOVE FOR #14770 +@eval getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1) +@eval getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)) # TODO: REMOVE FOR #14770 # Faster contiguous indexing using copy! for UnitRange and Colon function getindex(A::Array, I::UnitRange{Int}) @@ -612,8 +794,18 @@ function getindex(A::Array{S}, I::Range{Int}) where S end ## Indexing: setindex! ## -setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset(A, convert(T,x)::T, i1) -setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = (@_inline_meta; arrayset(A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 + +""" + 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)`. +""" +function setindex! end + +@eval setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) +@eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = + (@_inline_meta; arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1, i2, I...)) # TODO: REMOVE FOR #14770 # These are redundant with the abstract fallbacks but needed for bootstrap function setindex!(A::Array, x, I::AbstractVector{Int}) @@ -686,6 +878,29 @@ _deleteat!(a::Vector, i::Integer, delta::Integer) = ## Dequeue functionality ## +""" + push!(collection, items...) -> collection + +Insert one or more `items` at the end of `collection`. + +# Examples +```jldoctest +julia> push!([1, 2, 3], 4, 5, 6) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +Use [`append!`](@ref) to add all the elements of another collection to +`collection`. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, +5, 6])`. +""" +function push! end + function push!(a::Array{T,1}, item) where T # convert first so we don't grow the array if the assignment won't work itemT = convert(T, item) @@ -696,10 +911,37 @@ end function push!(a::Array{Any,1}, @nospecialize item) _growend!(a, 1) - arrayset(a, item, length(a)) + arrayset(true, a, item, length(a)) return a end +""" + append!(collection, collection2) -> collection. + +Add the elements of `collection2` to the end of `collection`. + +# Examples +```jldoctest +julia> append!([1],[2,3]) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> append!([1, 2, 3], [4, 5, 6]) +6-element Array{Int64,1}: + 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 is of the preceding example is equivalent to +`push!([1, 2, 3], 4, 5, 6)`. +""" function append!(a::Array{<:Any,1}, items::AbstractVector) itemindices = eachindex(items) n = length(itemindices) @@ -777,7 +1019,6 @@ function _prepend!(a, ::IteratorSize, iter) a end - """ resize!(a::Vector, n::Integer) -> Vector @@ -821,11 +1062,53 @@ function resize!(a::Vector, nl::Integer) return a end +""" + sizehint!(s, n) + +Suggest that collection `s` reserve capacity for at least `n` elements. This can improve performance. +""" +function sizehint! end + function sizehint!(a::Vector, sz::Integer) ccall(:jl_array_sizehint, Void, (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. + +# Examples +```jldoctest +julia> A=[1, 2, 3] +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> pop!(A) +3 + +julia> A +2-element Array{Int64,1}: + 1 + 2 + +julia> S = Set([1, 2]) +Set([2, 1]) + +julia> pop!(S) +2 + +julia> S +Set([1]) + +julia> pop!(Dict(1=>2)) +1 => 2 +``` +""" function pop!(a::Vector) if isempty(a) throw(ArgumentError("array must be non-empty")) @@ -859,6 +1142,34 @@ function unshift!(a::Array{T,1}, item) where T return a end +""" + shift!(collection) -> item + +Remove the first `item` from `collection`. + +# Examples +```jldoctest +julia> A = [1, 2, 3, 4, 5, 6] +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 + +julia> shift!(A) +1 + +julia> A +5-element Array{Int64,1}: + 2 + 3 + 4 + 5 + 6 +``` +""" function shift!(a::Vector) if isempty(a) throw(ArgumentError("array must be non-empty")) @@ -1147,6 +1458,46 @@ function ==(a::Arr, b::Arr) where Arr <: BitIntegerArray{1} :memcmp, Int32, (Ptr{Void}, Ptr{Void}, UInt), a, b, sizeof(eltype(Arr)) * len) end +""" + reverse(v [, start=1 [, stop=length(v) ]] ) + +Return a copy of `v` reversed from start to stop. + +# Examples +```jldoctest +julia> A = collect(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> reverse(A) +5-element Array{Int64,1}: + 5 + 4 + 3 + 2 + 1 + +julia> reverse(A, 1, 4) +5-element Array{Int64,1}: + 4 + 3 + 2 + 1 + 5 + +julia> reverse(A, 3, 5) +5-element Array{Int64,1}: + 1 + 2 + 5 + 4 + 3 +``` +""" function reverse(A::AbstractVector, s=first(linearindices(A)), n=last(linearindices(A))) B = similar(A) for i = first(linearindices(A)):s-1 @@ -1165,6 +1516,11 @@ function reverseind(a::AbstractVector, i::Integer) first(li) + last(li) - i end +""" + reverse!(v [, start=1 [, stop=length(v) ]]) -> v + +In-place version of [`reverse`](@ref). +""" function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearindices(v))) liv = linearindices(v) if n <= s # empty case; ok @@ -1181,7 +1537,6 @@ function reverse!(v::AbstractVector, s=first(linearindices(v)), n=last(linearind return v end - # concatenations of homogeneous combinations of vectors, horizontal and vertical vcat() = Array{Any,1}(0) @@ -1206,6 +1561,9 @@ function vcat(arrays::Vector{T}...) where T ptr = pointer(arr) if isbits(T) elsz = Core.sizeof(T) + elseif isbitsunion(T) + elsz = bitsunionsize(T) + selptr = convert(Ptr{UInt8}, ptr) + n * elsz else elsz = Core.sizeof(Ptr{Void}) end @@ -1215,6 +1573,13 @@ function vcat(arrays::Vector{T}...) where T if isbits(T) ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), ptr, a, nba) + elseif isbitsunion(T) + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + ptr, a, nba) + # copy selector bytes + ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), + selptr, convert(Ptr{UInt8}, pointer(a)) + nba, na) + selptr += na else ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), arr, ptr, a, pointer(a), na) @@ -1226,7 +1591,6 @@ end cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x))) - ## find ## """ @@ -1249,10 +1613,13 @@ julia> findnext(A,3) ``` """ function findnext(A, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if A[i] != 0 return i end + i = nextind(A, i) end return 0 end @@ -1299,10 +1666,13 @@ julia> findnext(A,4,3) ``` """ function findnext(A, v, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if A[i] == v return i end + i = nextind(A, i) end return 0 end @@ -1348,10 +1718,13 @@ julia> findnext(isodd, A, 2) ``` """ function findnext(testf::Function, A, start::Integer) - for i = start:length(A) + l = endof(A) + i = start + while i <= l if testf(A[i]) return i end + i = nextind(A, i) end return 0 end @@ -1398,8 +1771,10 @@ julia> findprev(A,1) ``` """ function findprev(A, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 A[i] != 0 && return i + i = prevind(A, i) end return 0 end @@ -1429,7 +1804,7 @@ julia> findlast(A) 0 ``` """ -findlast(A) = findprev(A, length(A)) +findlast(A) = findprev(A, endof(A)) """ findprev(A, v, i::Integer) @@ -1451,8 +1826,10 @@ julia> findprev(A, 1, 1) ``` """ function findprev(A, v, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 A[i] == v && return i + i = prevind(A, i) end return 0 end @@ -1480,7 +1857,7 @@ julia> findlast(A,3) 0 ``` """ -findlast(A, v) = findprev(A, v, length(A)) +findlast(A, v) = findprev(A, v, endof(A)) """ findprev(predicate::Function, A, i::Integer) @@ -1503,8 +1880,10 @@ julia> findprev(isodd, A, 3) ``` """ function findprev(testf::Function, A, start::Integer) - for i = start:-1:1 + i = start + while i >= 1 testf(A[i]) && return i + i = prevind(A, i) end return 0 end @@ -1529,7 +1908,7 @@ julia> findlast(x -> x > 5, A) 0 ``` """ -findlast(testf::Function, A) = findprev(testf, A, length(A)) +findlast(testf::Function, A) = findprev(testf, A, endof(A)) """ find(f::Function, A) @@ -1594,14 +1973,14 @@ julia> find(zeros(3)) ``` """ function find(A) - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = Vector{Int}(nnzA) - count = 1 + cnt = 1 inds = _index_remapper(A) for (i,a) in enumerate(A) if a != 0 - I[count] = inds[i] - count += 1 + I[cnt] = inds[i] + cnt += 1 end end return I @@ -1640,15 +2019,15 @@ julia> findn(A) ``` """ function findn(A::AbstractMatrix) - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = similar(A, Int, nnzA) J = similar(A, Int, nnzA) - count = 1 + cnt = 1 for j=indices(A,2), i=indices(A,1) if A[i,j] != 0 - I[count] = i - J[count] = j - count += 1 + I[cnt] = i + J[cnt] = j + cnt += 1 end end return (I, J) @@ -1673,19 +2052,19 @@ julia> findnz(A) ``` """ function findnz(A::AbstractMatrix{T}) where T - nnzA = countnz(A) + nnzA = count(t -> t != 0, A) I = zeros(Int, nnzA) J = zeros(Int, nnzA) NZs = Array{T,1}(nnzA) - count = 1 + cnt = 1 if nnzA > 0 for j=indices(A,2), i=indices(A,1) Aij = A[i,j] if Aij != 0 - I[count] = i - J[count] = j - NZs[count] = Aij - count += 1 + I[cnt] = i + J[cnt] = j + NZs[cnt] = Aij + cnt += 1 end end end @@ -1696,8 +2075,9 @@ end findmax(itr) -> (x, index) Returns the maximum element of the collection `itr` and its index. If there are multiple -maximal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `max`. +maximal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `max`. The collection must not be empty. @@ -1710,21 +2090,21 @@ julia> findmax([1,7,7,6]) (7, 2) julia> findmax([1,7,7,NaN]) -(7.0, 2) +(NaN, 4) ``` """ function findmax(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) - ai, s = next(a, s) - i += 1 - ai != ai && continue # assume x != x => x is a NaN - if m != m || isless(m, ai) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) + m != m && break + (i, ai), s = next(p, s) + if ai != ai || isless(m, ai) m = ai mi = i end @@ -1736,8 +2116,9 @@ end findmin(itr) -> (x, index) Returns the minimum element of the collection `itr` and its index. If there are multiple -minimal elements, then the first one will be returned. `NaN` values are ignored, unless -all elements are `NaN`. Other than the treatment of `NaN`, the result is in line with `min`. +minimal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `min`. The collection must not be empty. @@ -1750,21 +2131,21 @@ julia> findmin([7,1,1,6]) (1, 2) julia> findmin([7,1,1,NaN]) -(1.0, 2) +(NaN, 4) ``` """ function findmin(a) if isempty(a) throw(ArgumentError("collection must be non-empty")) end - s = start(a) - mi = i = 1 - m, s = next(a, s) - while !done(a, s) - ai, s = next(a, s) - i += 1 - ai != ai && continue - if m != m || isless(ai, m) + p = pairs(a) + s = start(p) + (mi, m), s = next(p, s) + i = mi + while !done(p, s) + m != m && break + (i, ai), s = next(p, s) + if ai != ai || isless(ai, m) m = ai mi = i end @@ -1776,8 +2157,7 @@ end indmax(itr) -> Integer Returns the index of the maximum element in a collection. If there are multiple maximal -elements, then the first one will be returned. `NaN` values are ignored, unless all -elements are `NaN`. +elements, then the first one will be returned. The collection must not be empty. @@ -1790,7 +2170,7 @@ julia> indmax([1,7,7,6]) 2 julia> indmax([1,7,7,NaN]) -2 +4 ``` """ indmax(a) = findmax(a)[2] @@ -1799,8 +2179,7 @@ indmax(a) = findmax(a)[2] indmin(itr) -> Integer Returns the index of the minimum element in a collection. If there are multiple minimal -elements, then the first one will be returned. `NaN` values are ignored, unless all -elements are `NaN`. +elements, then the first one will be returned. The collection must not be empty. @@ -1813,7 +2192,7 @@ julia> indmin([7,1,1,6]) 2 julia> indmin([7,1,1,NaN]) -2 +4 ``` """ indmin(a) = findmin(a)[2] diff --git a/base/associative.jl b/base/associative.jl index 1d4f48bda6aa5..18c4de2ec7915 100644 --- a/base/associative.jl +++ b/base/associative.jl @@ -2,6 +2,16 @@ # generic operations on associative collections +""" + KeyError(key) + +An indexing operation into an `Associative` (`Dict`) or `Set` like object tried to access or +delete a non-existent element. +""" +mutable struct KeyError <: Exception + key +end + const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ haskey(d::Associative, k) = in(k,keys(d)) @@ -59,11 +69,18 @@ end in(k, v::KeyIterator) = 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::Associative) -Return an iterator over all keys in a collection. +Return an iterator over all keys in an associative collection. `collect(keys(a))` returns an array of keys. Since the keys are stored internally in a hash table, the order in which they are returned may vary. @@ -84,7 +101,6 @@ julia> collect(keys(a)) ``` """ keys(a::Associative) = KeyIterator(a) -eachindex(a::Associative) = KeyIterator(a) """ values(a::Associative) @@ -111,6 +127,15 @@ julia> collect(values(a)) """ values(a::Associative) = 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. +""" +pairs(collection) = Generator(=>, keys(collection), values(collection)) + function copy(a::Associative) b = similar(a) for (k,v) in a @@ -307,7 +332,7 @@ end filter!(f, d::Associative) Update `d`, removing elements for which `f` is `false`. -The function `f` is passed two arguments (key and value). +The function `f` is passed `key=>value` pairs. # Example ```jldoctest @@ -317,7 +342,7 @@ Dict{Int64,String} with 3 entries: 3 => "c" 1 => "a" -julia> filter!((x,y)->isodd(x), d) +julia> filter!(p->isodd(p.first), d) Dict{Int64,String} with 2 entries: 3 => "c" 1 => "a" @@ -325,10 +350,14 @@ Dict{Int64,String} with 2 entries: """ function filter!(f, d::Associative) badkeys = Vector{keytype(d)}(0) - for (k,v) in d - # don't delete!(d, k) here, since associative types - # may not support mutation during iteration - f(k,v) || push!(badkeys, k) + try + for (k,v) in d + # don't delete!(d, k) here, since associative types + # may not support mutation during iteration + f(k => v) || push!(badkeys, k) + end + catch e + return filter!_dict_deprecation(e, f, d) end for k in badkeys delete!(d, k) @@ -336,11 +365,29 @@ function filter!(f, d::Associative) return d end +function filter!_dict_deprecation(e, f, d::Associative) + if isa(e, MethodError) && e.f === f + depwarn("In `filter!(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter!) + badkeys = Vector{keytype(d)}(0) + for (k,v) in d + # don't delete!(d, k) here, since associative types + # may not support mutation during iteration + f(k, v) || push!(badkeys, k) + end + for k in badkeys + delete!(d, k) + end + else + rethrow(e) + end + return d +end + """ filter(f, d::Associative) Return a copy of `d`, removing elements for which `f` is `false`. -The function `f` is passed two arguments (key and value). +The function `f` is passed `key=>value` pairs. # Examples ```jldoctest @@ -349,7 +396,7 @@ Dict{Int64,String} with 2 entries: 2 => "b" 1 => "a" -julia> filter((x,y)->isodd(x), d) +julia> filter(p->isodd(p.first), d) Dict{Int64,String} with 1 entry: 1 => "a" ``` @@ -357,9 +404,22 @@ Dict{Int64,String} with 1 entry: function filter(f, d::Associative) # don't just do filter!(f, copy(d)): avoid making a whole copy of d df = similar(d) - for (k,v) in d - if f(k,v) - df[k] = v + try + for (k, v) in d + if f(k => v) + df[k] = v + end + end + catch e + if isa(e, MethodError) && e.f === f + depwarn("In `filter(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter) + for (k, v) in d + if f(k, v) + df[k] = v + end + end + else + rethrow(e) end end return df diff --git a/base/asyncmap.jl b/base/asyncmap.jl index 19846ab7a0ced..7394182bb48b8 100644 --- a/base/asyncmap.jl +++ b/base/asyncmap.jl @@ -15,7 +15,7 @@ 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 less than the current number +task started if the value of `ntasks_func` is less than the current number of tasks. If `batch_size` is specified, the collection is processed in batch mode. `f` must @@ -285,7 +285,7 @@ Returns 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 +[`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note @@ -360,7 +360,7 @@ end 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 +[`asyncmap`](@ref). If `batch_size` is specified, `f` must be a function which operates on an array of argument tuples. !!! note @@ -415,7 +415,7 @@ 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 +Like [`asyncmap`](@ref), but stores output in `results` rather than returning a collection. """ function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) diff --git a/base/atomics.jl b/base/atomics.jl index 38f8a0e243178..51c6964a13e16 100644 --- a/base/atomics.jl +++ b/base/atomics.jl @@ -345,20 +345,23 @@ for typ in atomictypes irt = Base.libllvm_version >= v"3.6" ? "$ilt, $ilt*" : "$ilt*" @eval getindex(x::Atomic{$typ}) = llvmcall($""" - %rv = load atomic $rt %0 acquire, align $(alignment(typ)) + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = load atomic $rt %ptr acquire, align $(alignment(typ)) ret $lt %rv """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) @eval setindex!(x::Atomic{$typ}, v::$typ) = llvmcall($""" - store atomic $lt %1, $lt* %0 release, align $(alignment(typ)) + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + store atomic $lt %1, $lt* %ptr release, align $(alignment(typ)) ret void - """, Void, Tuple{Ptr{$typ},$typ}, unsafe_convert(Ptr{$typ}, x), v) + """, Void, 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) = llvmcall($""" - %rs = cmpxchg $lt* %0, $lt %1, $lt %2 acq_rel acquire + %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}, @@ -366,7 +369,7 @@ for typ in atomictypes else @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = llvmcall($""" - %iptr = bitcast $lt* %0 to $ilt* + %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 @@ -387,14 +390,15 @@ for typ in atomictypes if typ <: Integer @eval $fn(x::Atomic{$typ}, v::$typ) = llvmcall($""" - %rv = atomicrmw $rmw $lt* %0, $lt %1 acq_rel + %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) = llvmcall($""" - %iptr = bitcast $lt* %0 to $ilt* + %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 diff --git a/base/base.jl b/base/base.jl deleted file mode 100644 index 1130730737476..0000000000000 --- a/base/base.jl +++ /dev/null @@ -1,153 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - SystemError(prefix::AbstractString, [errno::Int32]) - -A system call failed with an error code (in the `errno` global variable). -""" -mutable struct SystemError <: Exception - prefix::AbstractString - 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()) -end - -""" - ParseError(msg) - -The expression passed to the `parse` function could not be interpreted as a valid Julia -expression. -""" -mutable struct ParseError <: Exception - msg::AbstractString -end - -""" - ArgumentError(msg) - -The parameters to a function call do not match a valid signature. Argument `msg` is a -descriptive error string. -""" -mutable struct ArgumentError <: Exception - msg::AbstractString -end - -""" - KeyError(key) - -An indexing operation into an `Associative` (`Dict`) or `Set` like object tried to access or -delete a non-existent element. -""" -mutable struct KeyError <: Exception - key -end - -""" - 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. -""" -mutable struct MethodError <: Exception - f - args - world::UInt - MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) -end -MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax(UInt)) - -""" - EOFError() - -No more data was available to read from a file or stream. -""" -mutable struct EOFError <: Exception end - -""" - DimensionMismatch([msg]) - -The objects called do not have matching dimensionality. Optional argument `msg` is a -descriptive error string. -""" -mutable struct DimensionMismatch <: Exception - msg::AbstractString -end -DimensionMismatch() = DimensionMismatch("") - -""" - AssertionError([msg]) - -The asserted condition did not evaluate to `true`. -Optional argument `msg` is a descriptive error string. -""" -mutable struct AssertionError <: Exception - msg::AbstractString - AssertionError() = new("") - AssertionError(msg) = new(msg) -end - -#Generic wrapping of arbitrary exceptions -#Subtypes should put the exception in an 'error' field -abstract type WrappedException <: Exception end - -""" - LoadError(file::AbstractString, line::Int, error) - -An error occurred while `include`ing, `require`ing, or `using` a file. The error specifics -should be available in the `.error` field. -""" -mutable struct LoadError <: WrappedException - file::AbstractString - line::Int - error -end - -""" - InitError(mod::Symbol, error) - -An error occurred when running a module's `__init__` function. The actual error thrown is -available in the `.error` field. -""" -mutable struct InitError <: WrappedException - mod::Symbol - error -end - -ccall(:jl_get_system_hooks, Void, ()) - - -==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) -==(w::WeakRef, v) = isequal(w.value, v) -==(w, v::WeakRef) = isequal(w, v.value) - -function finalizer(@nospecialize(o), @nospecialize(f)) - if isimmutable(o) - error("objects of type ", typeof(o), " cannot be finalized") - end - ccall(:jl_gc_add_finalizer_th, Void, (Ptr{Void}, Any, Any), - Core.getptls(), o, f) -end -function finalizer(o::T, f::Ptr{Void}) where T - @_inline_meta - if isimmutable(T) - error("objects of type ", T, " cannot be finalized") - end - ccall(:jl_gc_add_ptr_finalizer, Void, (Ptr{Void}, Any, Ptr{Void}), - Core.getptls(), o, f) -end - -finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), - Core.getptls(), o) - -gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) -gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 - -struct Nullable{T} - hasvalue::Bool - value::T - - Nullable{T}() where {T} = new(false) - Nullable{T}(value::T, hasvalue::Bool=true) where {T} = new(hasvalue, value) -end diff --git a/base/bitarray.jl b/base/bitarray.jl index 7d69bcd7e3a63..71c2702a34d59 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -555,6 +555,10 @@ reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims) BitArray(A::AbstractArray{<:Any,N}) where {N} = convert(BitArray{N}, A) +if module_name(@__MODULE__) === :Base # avoid method overwrite +(::Type{T})(x::T) where {T<:BitArray} = copy(x) +end + """ BitArray(itr) @@ -1474,149 +1478,28 @@ details and examples. """ (>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i)) -""" - rol!(dest::BitVector, src::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation on `src` and puts the result into `dest`. -`i` controls how far to rotate the bits. -""" -function rol!(dest::BitVector, src::BitVector, i::Integer) - 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 : copy!(dest, src)) - i < 0 && return ror!(dest, src, -i) - Bc = (src === dest ? copy(src.chunks) : src.chunks) - copy_chunks!(dest.chunks, 1, Bc, i+1, n-i) - copy_chunks!(dest.chunks, n-i+1, Bc, 1, i) - return dest -end - -""" - rol!(B::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation in-place on `B`. -`i` controls how far to rotate the bits. -""" -rol!(B::BitVector, i::Integer) = rol!(B, B, i) - -""" - rol(B::BitVector, i::Integer) -> BitVector - -Performs a left rotation operation, returning a new `BitVector`. -`i` controls how far to rotate the bits. -See also [`rol!`](@ref). - -# Examples -```jldoctest -julia> A = BitArray([true, true, false, false, true]) -5-element BitArray{1}: - true - true - false - false - true - -julia> rol(A,1) -5-element BitArray{1}: - true - false - false - true - true - -julia> rol(A,2) -5-element BitArray{1}: - false - false - true - true - true - -julia> rol(A,5) -5-element BitArray{1}: - true - true - false - false - true -``` -""" -rol(B::BitVector, i::Integer) = rol!(similar(B), B, i) - -""" - ror!(dest::BitVector, src::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation on `src` and puts the result into `dest`. -`i` controls how far to rotate the bits. -""" -function ror!(dest::BitVector, src::BitVector, i::Integer) +function circshift!(dest::BitVector, src::BitVector, i::Integer) 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 : copy!(dest, src)) - i < 0 && return rol!(dest, src, -i) Bc = (src === dest ? copy(src.chunks) : src.chunks) - copy_chunks!(dest.chunks, i+1, Bc, 1, n-i) - copy_chunks!(dest.chunks, 1, Bc, n-i+1, i) + 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 -""" - ror!(B::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation in-place on `B`. -`i` controls how far to rotate the bits. -""" -ror!(B::BitVector, i::Integer) = ror!(B, B, i) - -""" - ror(B::BitVector, i::Integer) -> BitVector - -Performs a right rotation operation on `B`, returning a new `BitVector`. -`i` controls how far to rotate the bits. -See also [`ror!`](@ref). - -# Examples -```jldoctest -julia> A = BitArray([true, true, false, false, true]) -5-element BitArray{1}: - true - true - false - false - true - -julia> ror(A,1) -5-element BitArray{1}: - true - true - true - false - false - -julia> ror(A,2) -5-element BitArray{1}: - false - true - true - true - false - -julia> ror(A,5) -5-element BitArray{1}: - true - true - false - false - true -``` -""" -ror(B::BitVector, i::Integer) = ror!(similar(B), B, i) +circshift!(B::BitVector, i::Integer) = circshift!(B, B, i) -## countnz & find ## +## count & find ## -function countnz(B::BitArray) +function count(B::BitArray) n = 0 Bc = B.chunks @inbounds for i = 1:length(Bc) @@ -1624,7 +1507,6 @@ function countnz(B::BitArray) end return n end -count(B::BitArray) = countnz(B) # returns the index of the next non-zero element, or 0 if all zeros function findnext(B::BitArray, start::Integer) @@ -1778,7 +1660,7 @@ end function find(B::BitArray) l = length(B) - nnzB = countnz(B) + nnzB = count(B) I = Vector{Int}(nnzB) nnzB == 0 && return I Bc = B.chunks @@ -1812,15 +1694,15 @@ end findn(B::BitVector) = find(B) function findn(B::BitMatrix) - nnzB = countnz(B) + nnzB = count(B) I = Vector{Int}(nnzB) J = Vector{Int}(nnzB) - count = 1 + cnt = 1 for j = 1:size(B,2), i = 1:size(B,1) if B[i,j] - I[count] = i - J[count] = j - count += 1 + I[cnt] = i + J[cnt] = j + cnt += 1 end end return I, J @@ -1834,7 +1716,7 @@ end ## Reductions ## sum(A::BitArray, region) = reducedim(+, A, region) -sum(B::BitArray) = countnz(B) +sum(B::BitArray) = count(B) function all(B::BitArray) isempty(B) && return true diff --git a/base/bool.jl b/base/bool.jl index 2e78c2f824fcb..be60d2758337e 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -95,18 +95,17 @@ isone(x::Bool) = x ^(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 -function *(x::Bool, y::T)::promote_type(Bool,T) where T<:Number +# 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 -function *(x::Bool, y::T)::promote_type(Bool,T) where T<:Unsigned - return ifelse(x, y, zero(y)) -end -*(y::Number, x::Bool) = x * y +*(y::AbstractFloat, x::Bool) = x * y div(x::Bool, y::Bool) = y ? x : throw(DivideError()) fld(x::Bool, y::Bool) = div(x,y) diff --git a/base/boot.jl b/base/boot.jl index db7cdcbd1159e..fe360e128d31a 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -135,7 +135,7 @@ export ErrorException, BoundsError, DivideError, DomainError, Exception, InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, - TypeError, + TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, # AST representation Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, GlobalRef, NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, @@ -244,6 +244,40 @@ struct OverflowError <: Exception msg end +mutable struct ArgumentError <: Exception + msg::AbstractString +end + +mutable struct MethodError <: Exception + f + args + world::UInt + MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) +end +const typemax_UInt = ccall(:jl_typemax_uint, Any, (Any,), UInt) +MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax_UInt) + +mutable struct AssertionError <: Exception + msg::AbstractString + AssertionError() = new("") + AssertionError(msg) = new(msg) +end + +#Generic wrapping of arbitrary exceptions +#Subtypes should put the exception in an 'error' field +abstract type WrappedException <: Exception end + +mutable struct LoadError <: WrappedException + file::AbstractString + line::Int + error +end + +mutable struct InitError <: WrappedException + mod::Symbol + error +end + abstract type DirectIndexString <: AbstractString end String(s::String) = s # no constructor yet @@ -297,17 +331,17 @@ VecElement(arg::T) where {T} = VecElement{T}(arg) # used by lowering of splicing unquote splicedexpr(hd::Symbol, args::Array{Any,1}) = (e=Expr(hd); e.args=args; e) -_new(typ::Symbol, argty::Symbol) = eval(Core, :((::Type{$typ})(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) +_new(typ::Symbol, argty::Symbol) = eval(Core, :($typ(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) _new(:LabelNode, :Int) _new(:GotoNode, :Int) _new(:NewvarNode, :SlotNumber) _new(:QuoteNode, :Any) _new(:SSAValue, :Int) -eval(Core, :((::Type{LineNumberNode})(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) -eval(Core, :((::Type{LineNumberNode})(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) -eval(Core, :((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) -eval(Core, :((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n)))) -eval(Core, :((::Type{TypedSlot})(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) +eval(Core, :(LineNumberNode(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) +eval(Core, :(LineNumberNode(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) +eval(Core, :(GlobalRef(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) +eval(Core, :(SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n)))) +eval(Core, :(TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports) @@ -355,6 +389,7 @@ function Symbol(a::Array{UInt8,1}) ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), a), Intrinsics.arraylen(a)) end +Symbol(s::Symbol) = s # docsystem basics macro doc(x...) @@ -403,4 +438,6 @@ show(@nospecialize a) = show(STDOUT, a) print(@nospecialize a...) = print(STDOUT, a...) println(@nospecialize a...) = println(STDOUT, a...) +gcuse(@nospecialize a) = ccall(:jl_gc_use, Void, (Any,), a) + ccall(:jl_set_istopmod, Void, (Any, Bool), Core, true) diff --git a/base/broadcast.jl b/base/broadcast.jl index 1b847aa7ddb6d..2ec5d451512bd 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -564,8 +564,8 @@ function __dot__(x::Expr) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) elseif x.head == :$ x.args[1] - elseif x.head == :let # don't add dots to "let x=... assignments - Expr(:let, dotargs[1], map(undot, dotargs[2:end])...) + 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) && diff --git a/base/c.jl b/base/c.jl index bd9a37ac5eaab..c57ce27d6130b 100644 --- a/base/c.jl +++ b/base/c.jl @@ -4,6 +4,18 @@ import Core.Intrinsics: cglobal, bitcast +""" + cglobal((symbol, library) [, type=Void]) + +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{Void}` if no `Type` argument is +supplied. +The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), +respectively. +""" +cglobal + """ cfunction(f::Function, returntype::Type, argtypes::Type) -> Ptr{Void} @@ -87,6 +99,17 @@ convert(::Type{Ptr{Cwchar_t}}, p::Cwstring) = bitcast(Ptr{Cwchar_t}, p) # construction from untyped pointers convert(::Type{T}, p::Ptr{Void}) where {T<:Union{Cstring,Cwstring}} = bitcast(T, p) +""" + pointer(array [, index]) + +Get the native address of an array or string element. Be careful to ensure that a Julia +reference to `a` exists as long as this pointer will be used. This function is "unsafe" like +`unsafe_convert`. + +Calling `Ref(array[, index])` is generally preferable to this function. +""" +function pointer end + pointer(p::Cstring) = convert(Ptr{UInt8}, p) pointer(p::Cwstring) = convert(Ptr{Cwchar_t}, p) diff --git a/base/cartesian.jl b/base/cartesian.jl index 6762b4db15461..b944617726345 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -302,6 +302,7 @@ function lreplace!(str::AbstractString, r::LReplace) pat = r.pat_str j = start(pat) matching = false + local istart::Int while !done(str, i) cstr, i = next(str, i) if !matching diff --git a/base/channels.jl b/base/channels.jl index 5c73db99e5be2..f862f00c6274b 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -196,8 +196,8 @@ julia> take!(c) julia> put!(c,1); ERROR: foo Stacktrace: - [1] check_channel_state(::Channel{Any}) at ./channels.jl:131 - [2] put!(::Channel{Any}, ::Int64) at ./channels.jl:261 + [1] check_channel_state at ./channels.jl:132 [inlined] + [2] put!(::Channel{Any}, ::Int64) at ./channels.jl:263 ``` """ function bind(c::Channel, task::Task) diff --git a/base/client.jl b/base/client.jl index c46d5acccc95c..7f8c5b11370b2 100644 --- a/base/client.jl +++ b/base/client.jl @@ -251,9 +251,9 @@ function process_options(opts::JLOptions) length(idxs) > 0 && deleteat!(ARGS, idxs[1]) end repl = true + quiet = (opts.quiet != 0) startup = (opts.startupfile != 2) history_file = (opts.historyfile != 0) - quiet = (opts.quiet != 0) color_set = (opts.color != 0) global have_color = (opts.color == 1) global is_interactive = (opts.isinteractive != 0) @@ -381,6 +381,7 @@ function _start() @eval Main include(x) = $include(Main, x) try (quiet,repl,startup,color_set,history_file) = process_options(opts) + banner = opts.banner == 1 local term global active_repl @@ -388,12 +389,15 @@ function _start() if repl if !isa(STDIN,TTY) global is_interactive |= !isa(STDIN, Union{File, IOStream}) + banner |= opts.banner != 0 && is_interactive color_set || (global have_color = false) else - term = Terminals.TTYTerminal(get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb"), STDIN, STDOUT, STDERR) + term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") + term = Terminals.TTYTerminal(term_env, STDIN, STDOUT, STDERR) global is_interactive = true + banner |= opts.banner != 0 color_set || (global have_color = Terminals.hascolor(term)) - quiet || REPL.banner(term,term) + banner && REPL.banner(term,term) if term.term_type == "dumb" active_repl = REPL.BasicREPL(term) quiet || warn("Terminal not fully functional") @@ -406,6 +410,8 @@ function _start() # REPLDisplay pushdisplay(REPL.REPLDisplay(active_repl)) end + else + banner |= opts.banner != 0 && is_interactive end if repl diff --git a/base/codevalidation.jl b/base/codevalidation.jl index 7605a1654af16..433eaa7bab5a6 100644 --- a/base/codevalidation.jl +++ b/base/codevalidation.jl @@ -18,7 +18,7 @@ const VALID_EXPR_HEADS = ObjectIdDict( :enter => 1:1, :leave => 1:1, :inbounds => 1:1, - :boundscheck => 1:1, + :boundscheck => 0:0, :copyast => 1:1, :meta => 0:typemax(Int), :global => 1:1, @@ -27,8 +27,6 @@ const VALID_EXPR_HEADS = ObjectIdDict( :simdloop => 0:0 ) -const ASSIGNED_FLAG = 0x02 - # @enum isn't defined yet, otherwise I'd use it for this const INVALID_EXPR_HEAD = "invalid expression head" const INVALID_EXPR_NARGS = "invalid number of expression args" @@ -41,7 +39,6 @@ const SLOTTYPES_MISMATCH = "length(slotnames) != length(slottypes)" const SLOTTYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo slottypes field is not `nothing`" const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes" const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues" -const INVALID_ASSIGNMENT_SLOTFLAG = "slot has wrong assignment slotflag setting (bit flag 2 not set)" const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)" const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments" const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments" @@ -76,9 +73,6 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ push!(errors, InvalidCodeError(INVALID_LVALUE, lhs)) elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums) n = lhs.id - if isassigned(c.slotflags, n) && !is_flag_set(c.slotflags[n], ASSIGNED_FLAG) - push!(errors, InvalidCodeError(INVALID_ASSIGNMENT_SLOTFLAG, lhs)) - end push!(lhs_slotnums, n) end if !is_valid_rvalue(rhs) diff --git a/base/complex.jl b/base/complex.jl index 64b80ae149076..0664f9679c248 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -499,9 +499,9 @@ julia> rad2deg(angle(-1 - im)) angle(z::Complex) = atan2(imag(z), real(z)) function log(z::Complex{T}) where T<:AbstractFloat - const T1::T = 1.25 - const T2::T = 3 - const ln2::T = log(convert(T,2)) #0.6931471805599453 + T1::T = 1.25 + T2::T = 3 + ln2::T = log(convert(T,2)) #0.6931471805599453 x, y = reim(z) ρ, k = ssqs(x,y) ax = abs(x) @@ -835,7 +835,7 @@ function cosh(z::Complex) end function tanh(z::Complex{T}) where T<:AbstractFloat - const Ω = prevfloat(typemax(T)) + Ω = prevfloat(typemax(T)) ξ, η = reim(z) if isnan(ξ) && η==0 return Complex(ξ, η) end if 4*abs(ξ) > asinh(Ω) #Overflow? @@ -880,9 +880,9 @@ function acosh(z::Complex) end function atanh(z::Complex{T}) where T<:AbstractFloat - const Ω = prevfloat(typemax(T)) - const θ = sqrt(Ω)/4 - const ρ = 1/θ + Ω = prevfloat(typemax(T)) + θ = sqrt(Ω)/4 + ρ = 1/θ x, y = reim(z) ax = abs(x) ay = abs(y) diff --git a/base/coreio.jl b/base/coreio.jl index b15fca1ea1003..fcc6bd519e355 100644 --- a/base/coreio.jl +++ b/base/coreio.jl @@ -1,6 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -show(x) = show(STDOUT::IO, x) print(xs...) = print(STDOUT::IO, xs...) println(xs...) = println(STDOUT::IO, xs...) println(io::IO) = print(io, '\n') diff --git a/base/dSFMT.jl b/base/dSFMT.jl deleted file mode 100644 index 11dbf0a079f4d..0000000000000 --- a/base/dSFMT.jl +++ /dev/null @@ -1,174 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -module dSFMT - -import Base: copy, copy!, ==, hash - -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!, - win32_SystemFunction036! - -"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 -"Jump polynomial for 10^20 steps for dSFMT with exponent 19937" -const JPOLY1e21 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" - -mutable struct DSFMT_state - val::Vector{Int32} - - DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) = - new(length(val) == JN32 ? val : throw(DomainError(length(val), string("Expected length ", JN32, '.')))) -end - -copy!(dst::DSFMT_state, src::DSFMT_state) = (copy!(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) - -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), - Void, - (Ptr{Void}, UInt32,), - s.val, seed) -end - -function dsfmt_init_by_array(s::DSFMT_state, seed::Vector{UInt32}) - ccall((:dsfmt_init_by_array,:libdSFMT), - Void, - (Ptr{Void}, 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), - Void, - (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), - Void, - (Ptr{Void}, 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), - Void, - (Ptr{Void}, Ptr{Float64}, Int), - s.val, A, n) -end - -# dSFMT jump -function dsfmt_jump(s::DSFMT_state, jp::AbstractString) - val = s.val - nval = length(val) - index = val[nval - 1] - work = zeros(UInt64, JN32 >> 1) - dsfmt = Vector{UInt64}(nval >> 1) - ccall(:memcpy, Ptr{Void}, (Ptr{UInt64}, Ptr{Int32}, Csize_t), - dsfmt, val, (nval - 1) * sizeof(Int32)) - dsfmt[end] = UInt64(N*2) - - for c in jp - bits = parse(UInt8,c,16) - for j in 1:4 - (bits & 0x01) != 0x00 && dsfmt_jump_add!(work, dsfmt) - bits = bits >> 0x01 - dsfmt_jump_next_state!(dsfmt) - end - end - - work[end] = index - return DSFMT_state(reinterpret(Int32, work)) -end - -function dsfmt_jump_add!(dest::Vector{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 - -## Windows entropy - -if Sys.iswindows() - function win32_SystemFunction036!(a::Array) - ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Void}, UInt32), a, sizeof(a)) - end -end - -end # module diff --git a/base/datafmt.jl b/base/datafmt.jl index 746da60f004b3..573350853bbfd 100644 --- a/base/datafmt.jl +++ b/base/datafmt.jl @@ -616,6 +616,11 @@ function dlm_parse(dbuff::String, eol::D, dlm::D, qchar::D, cchar::D, return (nrows, ncols) end +""" + readcsv(source, [T::Type]; options...) + +Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally defined by `T`. +""" readcsv(io; opts...) = readdlm(io, ','; opts...) readcsv(io, T::Type; opts...) = readdlm(io, ',', T; opts...) diff --git a/base/dates/types.jl b/base/dates/types.jl index 12e3e62bf25f2..5294b50cbfdd7 100644 --- a/base/dates/types.jl +++ b/base/dates/types.jl @@ -340,7 +340,7 @@ Base.typemin(::Union{Time, Type{Time}}) = Time(0) Base.eltype(::Type{T}) where {T<:Period} = T 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(Base.promote_noncircular(x, y)...) +Base.isless(x::TimeType, y::TimeType) = isless(promote(x, y)...) (==)(x::T, y::T) where {T<:TimeType} = (==)(value(x), value(y)) function ==(a::Time, b::Time) return hour(a) == hour(b) && minute(a) == minute(b) && diff --git a/base/deprecated.jl b/base/deprecated.jl index a9a5c93e269e5..d1a3d05b7820e 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -88,7 +88,7 @@ function firstcaller(bt::Array{Ptr{Void},1}, funcsyms) lkup = StackTraces.UNKNOWN for frame in bt lkups = StackTraces.lookup(frame) - for lkup in lkups + for outer lkup in lkups if lkup == StackTraces.UNKNOWN continue end @@ -220,11 +220,12 @@ for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, :tan, :tanh, :tand, :atan, :atanh, :atand, :sinpi, :cosc, :ceil, :floor, :trunc, :round, :log1p, :expm1, :abs, :abs2, - :log, :log2, :log10, :exp, :exp2, :exp10, :sinc, :cospi, + :log2, :log10, :exp2, :exp10, :sinc, :cospi, :cos, :cosh, :cosd, :acos, :acosd, :cot, :coth, :cotd, :acot, :acotd, :sec, :sech, :secd, :asech, :csc, :csch, :cscd, :acsch) + @eval import .Math: $f @eval @deprecate $f(A::SparseMatrixCSC) $f.(A) end @@ -247,18 +248,19 @@ for f in ( # base/special/trig.jl :sinpi, :cospi, :sinc, :cosc, # base/special/log.jl - :log, :log1p, + :log1p, # base/special/gamma.jl :gamma, :lfact, # base/math.jl - :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp, :exp2, + :cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp2, :expm1, :exp10, :sin, :cos, :tan, :asin, :acos, :acosh, :atanh, - #=:log,=# :log2, :log10, :lgamma, #=:log1p,=# :sqrt, + :log2, :log10, :lgamma, #=:log1p,=# # base/floatfuncs.jl :abs, :abs2, :angle, :isnan, :isinf, :isfinite, # base/complex.jl :cis, ) + @eval import .Math: $f @eval @dep_vectorize_1arg Number $f end # base/fastmath.jl @@ -267,13 +269,15 @@ for f in ( :acos_fast, :acosh_fast, :angle_fast, :asin_fast, :asinh_fast, :cosh_fast, :exp10_fast, :exp2_fast, :exp_fast, :expm1_fast, :lgamma_fast, :log10_fast, :log1p_fast, :log2_fast, :log_fast, :sin_fast, :sinh_fast, :sqrt_fast, :tan_fast, :tanh_fast ) - @eval FastMath Base.@dep_vectorize_1arg Number $f + @eval import .FastMath: $f + @eval @dep_vectorize_1arg Number $f end for f in ( :trunc, :floor, :ceil, :round, # base/floatfuncs.jl :rad2deg, :deg2rad, :exponent, :significand, # base/math.jl :sind, :cosd, :tand, :asind, :acosd, :atand, :asecd, :acscd, :acotd, # base/special/trig.jl ) + @eval import .Math: $f @eval @dep_vectorize_1arg Real $f end # base/complex.jl @@ -312,11 +316,13 @@ for f in ( # base/math.jl :log, :hypot, :atan2, ) + @eval import .Math: $f @eval @dep_vectorize_2arg Number $f end # base/fastmath.jl for f in (:pow_fast, :atan2_fast, :hypot_fast, :max_fast, :min_fast, :minmax_fast) - @eval FastMath Base.@dep_vectorize_2arg Number $f + @eval import .FastMath: $f + @eval @dep_vectorize_2arg Number $f end for f in ( :max, :min, # base/math.jl @@ -556,7 +562,7 @@ function gen_broadcast_function_sparse(genbody::Function, f::Function, is_first_ body = genbody(f, is_first_sparse) @eval let local _F_ - function _F_{Tv,Ti}(B::SparseMatrixCSC{Tv,Ti}, A_1, A_2) + function _F_(B::SparseMatrixCSC{Tv,Ti}, A_1, A_2) where {Tv,Ti} $body end _F_ @@ -737,6 +743,7 @@ end for f in (:sec, :sech, :secd, :asec, :asech, :csc, :csch, :cscd, :acsc, :acsch, :cot, :coth, :cotd, :acot, :acoth) + @eval import .Math: $f @eval @deprecate $f(A::AbstractArray{<:Number}) $f.(A) end @@ -746,6 +753,7 @@ end @deprecate complex(A::AbstractArray, B::AbstractArray) complex.(A, B) # Deprecate manually vectorized clamp methods in favor of compact broadcast syntax +import .Math: clamp @deprecate clamp(A::AbstractArray, lo, hi) clamp.(A, lo, hi) # Deprecate manually vectorized round methods in favor of compact broadcast syntax @@ -845,7 +853,7 @@ end @deprecate ~(A::AbstractArray) .~A @deprecate ~(B::BitArray) .~B -function frexp(A::Array{<:AbstractFloat}) +function Math.frexp(A::Array{<:AbstractFloat}) depwarn(string("`frexp(x::Array)` is discontinued. Though not a direct replacement, ", "consider using dot-syntax to `broadcast` scalar `frexp` over `Array`s ", "instead, for example `frexp.(rand(4))`."), :frexp) @@ -1055,46 +1063,6 @@ isempty(::Task) = error("isempty not defined for Tasks") export @test_approx_eq end -# Deprecate partial linear indexing -function partial_linear_indexing_warning_lookup(nidxs_remaining) - # We need to figure out how many indices were passed for a sensible deprecation warning - opts = JLOptions() - if opts.depwarn > 0 - # Find the caller -- this is very expensive so we don't want to do it twice - bt = backtrace() - found = false - call = StackTraces.UNKNOWN - caller = StackTraces.UNKNOWN - for frame in bt - lkups = StackTraces.lookup(frame) - for caller in lkups - if caller == StackTraces.UNKNOWN - continue - end - found && @goto found - if caller.func in (:getindex, :setindex!, :view) - found = true - call = caller - end - end - end - @label found - fn = "`reshape`" - if call != StackTraces.UNKNOWN && !isnull(call.linfo) - # Try to grab the number of dimensions in the parent array - mi = get(call.linfo) - args = mi.specTypes.parameters - if length(args) >= 2 && args[2] <: AbstractArray - fn = "`reshape(A, Val{$(ndims(args[2]) - nidxs_remaining + 1)})`" - end - end - _depwarn("Partial linear indexing is deprecated. Use $fn to make the dimensionality of the array match the number of indices.", opts, bt, caller) - end -end -function partial_linear_indexing_warning(n) - depwarn("Partial linear indexing is deprecated. Use `reshape(A, Val{$n})` to make the dimensionality of the array match the number of indices.", (:getindex, :setindex!, :view)) -end - # Deprecate Array(T, dims...) in favor of proper type constructors @deprecate Array(::Type{T}, d::NTuple{N,Int}) where {T,N} Array{T}(d) @deprecate Array(::Type{T}, d::Int...) where {T} Array{T}(d...) @@ -1263,11 +1231,11 @@ end @noinline zero_arg_matrix_constructor(prefix::String) = depwarn("$prefix() is deprecated, use $prefix(0, 0) instead.", :zero_arg_matrix_constructor) -function (::Type{Matrix{T}})() where T +function Matrix{T}() where T zero_arg_matrix_constructor("Matrix{T}") return Matrix{T}(0, 0) end -function (::Type{Matrix})() +function Matrix() zero_arg_matrix_constructor("Matrix") return Matrix(0, 0) end @@ -1275,6 +1243,7 @@ end for name in ("alnum", "alpha", "cntrl", "digit", "number", "graph", "lower", "print", "punct", "space", "upper", "xdigit") f = Symbol("is",name) + @eval import .UTF8proc: $f @eval @deprecate ($f)(s::AbstractString) all($f, s) end @@ -1307,9 +1276,6 @@ end end end -# PR #16984 -@deprecate MersenneTwister() MersenneTwister(0) - # #19635 for fname in (:ones, :zeros) @eval @deprecate ($fname)(T::Type, arr) ($fname)(T, size(arr)) @@ -1339,19 +1305,21 @@ next(p::Union{Process, ProcessChain}, i::Int) = (getindex(p, i), i + 1) return i == 1 ? getfield(p, p.openstream) : p end +import .LinAlg: cond @deprecate cond(F::LinAlg.LU, p::Integer) cond(full(F), p) # PR #21359 +import .Random: srand @deprecate srand(r::MersenneTwister, filename::AbstractString, n::Integer=4) srand(r, read!(filename, Array{UInt32}(Int(n)))) @deprecate srand(filename::AbstractString, n::Integer=4) srand(read!(filename, Array{UInt32}(Int(n)))) @deprecate MersenneTwister(filename::AbstractString) srand(MersenneTwister(0), read!(filename, Array{UInt32}(Int(4)))) - # PR #21974 @deprecate versioninfo(verbose::Bool) versioninfo(verbose=verbose) @deprecate versioninfo(io::IO, verbose::Bool) versioninfo(io, verbose=verbose) # PR #22188 +import .LinAlg: cholfact, cholfact! @deprecate cholfact!(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}}) cholfact!(Hermitian(A, uplo), Val(false)) @deprecate cholfact!(A::StridedMatrix, uplo::Symbol) cholfact!(Hermitian(A, uplo)) @deprecate cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}}) cholfact(Hermitian(A, uplo), Val(false)) @@ -1360,6 +1328,7 @@ end @deprecate cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{true}}; tol = 0.0) cholfact(Hermitian(A, uplo), Val(true), tol = tol) # PR #22245 +import .LinAlg: isposdef, isposdef! @deprecate isposdef(A::AbstractMatrix, UL::Symbol) isposdef(Hermitian(A, UL)) @deprecate isposdef!(A::StridedMatrix, UL::Symbol) isposdef!(Hermitian(A, UL)) @@ -1422,7 +1391,7 @@ end module Operators for op in [:!, :(!=), :(!==), :%, :&, :*, :+, :-, :/, ://, :<, :<:, :<<, :(<=), :<|, :(==), :(===), :>, :>:, :(>=), :>>, :>>>, :\, :^, :colon, - :ctranspose, :getindex, :hcat, :hvcat, :setindex!, :transpose, :vcat, + :adjoint, :getindex, :hcat, :hvcat, :setindex!, :transpose, :vcat, :xor, :|, :|>, :~, :×, :÷, :∈, :∉, :∋, :∌, :∘, :√, :∛, :∩, :∪, :≠, :≤, :≥, :⊆, :⊈, :⊊, :⊻, :⋅] if isdefined(Base, op) @@ -1469,6 +1438,7 @@ export conv, conv2, deconv, filt, filt!, xcorr @deprecate cov(X::AbstractVecOrMat, Y::AbstractVecOrMat, vardim::Int, corrected::Bool) cov(X, Y, vardim, corrected=corrected) # bkfact +import .LinAlg: bkfact, bkfact! function bkfact(A::StridedMatrix, uplo::Symbol, symmetric::Bool = issymmetric(A), rook::Bool = false) depwarn("bkfact with uplo and symmetric arguments deprecated. Please use bkfact($(symmetric ? "Symmetric(" : "Hermitian(")A, :$uplo))", :bkfact) @@ -1502,6 +1472,7 @@ end @deprecate literal_pow(a, b, ::Type{Val{N}}) where {N} literal_pow(a, b, Val(N)) false @eval IteratorsMD @deprecate split(t, V::Type{Val{n}}) where {n} split(t, Val(n)) false @deprecate sqrtm(A::UpperTriangular{T},::Type{Val{realmatrix}}) where {T,realmatrix} sqrtm(A, Val(realmatrix)) +import .LinAlg: lufact, lufact!, qrfact, qrfact!, cholfact, cholfact! @deprecate lufact(A::AbstractMatrix, ::Type{Val{false}}) lufact(A, Val(false)) @deprecate lufact(A::AbstractMatrix, ::Type{Val{true}}) lufact(A, Val(true)) @deprecate lufact!(A::AbstractMatrix, ::Type{Val{false}}) lufact!(A, Val(false)) @@ -1653,6 +1624,24 @@ function SymTridiagonal(dv::AbstractVector{T}, ev::AbstractVector{S}) where {T,S SymTridiagonal(convert(Vector{R}, dv), convert(Vector{R}, ev)) end +# PR #23154 +# also uncomment constructor tests in test/linalg/tridiag.jl +function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} + depwarn(string("Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) ", + "where {Tl, Td, Tu} is deprecated; convert all vectors to the same type instead."), :Tridiagonal) + Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) +end + +# deprecate sqrtm in favor of sqrt +@deprecate sqrtm sqrt + +# deprecate expm in favor of exp +@deprecate expm! exp! +@deprecate expm exp + +# deprecate logm in favor of log +@deprecate logm log + # PR #23092 @eval LibGit2 begin function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) @@ -1677,6 +1666,7 @@ function hex2num(s::AbstractString) end return reinterpret(Float64, parse(UInt64, s, 16)) end +export hex2num @deprecate num2hex(x::Union{Float16,Float32,Float64}) hex(reinterpret(Unsigned, x), sizeof(x)*2) @deprecate num2hex(n::Integer) hex(n, sizeof(n)*2) @@ -1684,6 +1674,86 @@ end # PR #22742: change in isapprox semantics @deprecate rtoldefault(x,y) rtoldefault(x,y,0) false +# PR #23235 +@deprecate ctranspose adjoint +@deprecate ctranspose! adjoint! + +@deprecate convert(::Type{Vector{UInt8}}, s::AbstractString) Vector{UInt8}(s) +@deprecate convert(::Type{Array{UInt8}}, s::AbstractString) Vector{UInt8}(s) +@deprecate convert(::Type{Vector{Char}}, s::AbstractString) Vector{Char}(s) +@deprecate convert(::Type{Symbol}, s::AbstractString) Symbol(s) +@deprecate convert(::Type{String}, s::Symbol) String(s) +@deprecate convert(::Type{String}, v::Vector{UInt8}) String(v) +@deprecate convert(::Type{S}, g::UTF8proc.GraphemeIterator) where {S<:AbstractString} convert(S, g.s) + +# Issue #19923 +@deprecate ror circshift +@deprecate ror! circshift! +@deprecate rol(B, i) circshift(B, -i) +@deprecate rol!(dest, src, i) circshift!(dest, src, -i) +@deprecate rol!(B, i) circshift!(B, -i) + +# issue #5148, PR #23259 +# warning for `const` on locals should be changed to an error in julia-syntax.scm + +# issue #17886 +# deprecations for filter[!] with 2-arg functions are in associative.jl + +# PR #23066 +@deprecate cfunction(f, r, a::Tuple) cfunction(f, r, Tuple{a...}) + +# PR 23341 +import .LinAlg: diagm +@deprecate diagm(A::SparseMatrixCSC) spdiagm(sparsevec(A)) + +# PR #23373 +@deprecate diagm(A::BitMatrix) diagm(vec(A)) + +# PR 23341 +@eval GMP @deprecate gmp_version() version() false +@eval GMP @Base.deprecate_binding GMP_VERSION VERSION false +@eval GMP @deprecate gmp_bits_per_limb() bits_per_limb() false +@eval GMP @Base.deprecate_binding GMP_BITS_PER_LIMB BITS_PER_LIMB false +@eval MPFR @deprecate get_version() version() false +@eval LinAlg.LAPACK @deprecate laver() version() false + +# PR #23427 +@deprecate_binding e ℯ +@deprecate_binding eu ℯ +@deprecate_binding γ MathConstants.γ +@deprecate_binding eulergamma MathConstants.eulergamma +@deprecate_binding catalan MathConstants.catalan +@deprecate_binding φ MathConstants.φ +@deprecate_binding golden MathConstants.golden + +# PR #23271 +function IOContext(io::IO; kws...) + depwarn("IOContext(io, k=v, ...) is deprecated, use IOContext(io, :k => v, ...) instead.", :IOContext) + IOContext(io, (k=>v for (k, v) in kws)...) +end + +@deprecate IOContext(io::IO, key, value) IOContext(io, key=>value) + +# PR #23485 +export countnz +function countnz(x) + depwarn("countnz(x) is deprecated, use either count(!iszero, x) or count(t -> t != 0, x) instead.", :countnz) + return count(t -> t != 0, x) +end + +# issue #22791 +@deprecate select partialsort +@deprecate select! partialsort! +@deprecate selectperm partialsortperm +@deprecate selectperm! partialsortperm! + +@deprecate promote_noncircular promote false + +import .Iterators.enumerate + +@deprecate enumerate(i::IndexLinear, A::AbstractArray) pairs(i, A) +@deprecate enumerate(i::IndexCartesian, A::AbstractArray) pairs(i, A) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/dict.jl b/base/dict.jl index e0ddcaaee649e..10c326b1358a1 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -437,7 +437,50 @@ function setindex!(h::Dict{K,V}, v0, key::K) where V where K 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!(h::Dict{K,V}, key0, default) where {K,V} = get!(()->default, h, key0) + +""" + get!(f::Function, 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: +```julia +get!(dict, key) do + # default value calculated here + time() +end +``` +""" +get!(f::Function, collection, key) + function get!(default::Callable, h::Dict{K,V}, key0) where V where K key = convert(K, key0) if !isequal(key, key0) @@ -480,11 +523,47 @@ function getindex(h::Dict{K,V}, key) where V where K 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. + +# 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) return (index < 0) ? default : h.vals[index]::V end +""" + get(f::Function, 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(::Function, collection, key) + function get(default::Callable, h::Dict{K,V}, key) where V where K index = ht_keyindex(h, key) return (index < 0) ? default() : h.vals[index]::V @@ -545,6 +624,30 @@ function pop!(h::Dict, 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: + [1] pop!(::Dict{String,Int64}, ::String) at ./dict.jl:539 + +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 @@ -569,6 +672,25 @@ function _delete!(h::Dict, index) return h end +""" + delete!(collection, key) + +Delete the mapping for the given key in a collection, 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 +``` +""" +delete!(collection, key) + function delete!(h::Dict, key) index = ht_keyindex(h, key) if index > 0 @@ -599,17 +721,23 @@ length(t::Dict) = t.count next(v::KeyIterator{<:Dict}, i) = (v.dict.keys[i], skip_deleted(v.dict,i+1)) next(v::ValueIterator{<:Dict}, i) = (v.dict.vals[i], skip_deleted(v.dict,i+1)) -# For these Associative types, it is safe to implement filter! -# by deleting keys during iteration. -function filter!(f, d::Union{ObjectIdDict,Dict}) - for (k,v) in d - if !f(k,v) - delete!(d,k) +function filter_in_one_pass!(f, d::Associative) + try + for (k, v) in d + if !f(k => v) + delete!(d, k) + end end + catch e + return filter!_dict_deprecation(e, f, d) end return d end +# For these Associative types, it is safe to implement filter! +# by deleting keys during iteration. +filter!(f, d::Union{ObjectIdDict,Dict}) = filter_in_one_pass!(f, d) + struct ImmutableDict{K,V} <: Associative{K,V} parent::ImmutableDict{K,V} key::K diff --git a/base/distributed/macros.jl b/base/distributed/macros.jl index aa3f52cedc2d4..d3b72cb791682 100644 --- a/base/distributed/macros.jl +++ b/base/distributed/macros.jl @@ -123,7 +123,7 @@ function extract_imports!(imports, ex::Expr) if Meta.isexpr(ex, (:import, :using)) return push!(imports, ex.args[1]) elseif Meta.isexpr(ex, :let) - return extract_imports!(imports, ex.args[1]) + return extract_imports!(imports, ex.args[2]) elseif Meta.isexpr(ex, (:toplevel, :block)) for i in eachindex(ex.args) extract_imports!(imports, ex.args[i]) diff --git a/base/distributed/remotecall.jl b/base/distributed/remotecall.jl index 4ce1a318a604f..1e87ba22b6698 100644 --- a/base/distributed/remotecall.jl +++ b/base/distributed/remotecall.jl @@ -94,7 +94,7 @@ RemoteChannel(pid::Integer=myid()) = RemoteChannel{Channel{Any}}(pid, RRID()) """ RemoteChannel(f::Function, pid::Integer=myid()) -Create references to remote channels of a specific size and type. `f()` is a function that +Create references to remote channels of a specific size and type. `f` is a function that when executed on `pid` must return an implementation of an `AbstractChannel`. For example, `RemoteChannel(()->Channel{Int}(10), pid)`, will return a reference to a @@ -551,7 +551,7 @@ end take!(rr::RemoteChannel, args...) Fetch value(s) from a [`RemoteChannel`](@ref) `rr`, -removing the value(s) in the processs. +removing the value(s) in the process. """ take!(rr::RemoteChannel, args...) = call_on_owner(take_ref, rr, myid(), args...) diff --git a/base/distributed/workerpool.jl b/base/distributed/workerpool.jl index 0406f80bbe3b4..7aedd63c844c4 100644 --- a/base/distributed/workerpool.jl +++ b/base/distributed/workerpool.jl @@ -190,7 +190,7 @@ const _default_worker_pool = Ref{Nullable}(Nullable{WorkerPool}()) """ default_worker_pool() -`WorkerPool` containing idle `workers()` - used by `remote(f)` and [`pmap`](@ref) (by default). +`WorkerPool` containing idle `workers` - used by `remote(f)` and [`pmap`](@ref) (by default). """ function default_worker_pool() # On workers retrieve the default worker pool from the master when accessed diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 40680e42c4972..6dec34c97e38a 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -132,7 +132,7 @@ kw"primitive type" """ `macro` defines a method to include generated code in the final body of a program. A macro maps a tuple of arguments to a returned expression, and the resulting expression -is compiled directly rather than requiring a runtime `eval()` call. Macro arguments may +is compiled directly rather than requiring a runtime `eval` call. Macro arguments may include expressions, literal values, and symbols. For example: macro sayhello(name) @@ -189,7 +189,7 @@ modify the global variable `z`: julia> z 6 -Without the `global` declaration in `foo()`, a new local variable would have been +Without the `global` declaration in `foo`, a new local variable would have been created inside foo(), and the `z` in the global scope would have remained equal to `3`. """ kw"global" @@ -682,6 +682,27 @@ A variable referring to the last computed value, automatically set at the intera """ kw"ans" +""" + 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 + +""" + Void + +A type with no fields that is the type [`nothing`](@ref). +""" +Void + """ nothing @@ -697,18 +718,6 @@ The singleton type containing only the value `Union{}`. """ Core.TypeofBottom -""" - 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 - """ Function @@ -727,4 +736,663 @@ 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. +""" +ErrorException + +""" + UndefRefError() + +The item or field is not defined for the given object. +""" +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 +julia> A = ones(7); + +julia> A[8] +ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] +Stacktrace: + [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:763 + +julia> B = ones(2, 3); + +julia> B[2, 4] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:764 + +julia> B[9] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:763 +``` +""" +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: convert(Float64, 1 + 2im) +Stacktrace: + [1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:37 +``` +""" +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 will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 + [2] sqrt at ./math.jl:462 [inlined] + [3] sqrt(::Int64) at ./math.jl:472 +``` +""" +DomainError + +""" + Task(func) + +Create a `Task` (i.e. coroutine) to execute the given function (which must be +callable with no arguments). The task exits when this function returns. + +# Examples +```jldoctest +julia> a() = det(rand(1000, 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. +""" +nfields + +""" + UndefVarError(var::Symbol) + +A symbol in the current scope is not defined. +""" +UndefVarError + +""" + 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). +""" +InterruptException + +""" + applicable(f, args...) -> Bool + +Determine whether the given generic function has a method applicable to the given arguments. + +# 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). + +# Examples +```jldoctest +julia> f(x::Real) = x^2; + +julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); + +julia> f(2) +5 +``` +""" +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`. +""" +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: + [1] div(::Int64, ::Int64) at ./int.jl:220 +``` +""" +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. +""" +Bool + +for bit in (16, 32, 64) + @eval begin + """ + Float$($bit) <: AbstractFloat + + $($bit)-bit floating point number type. + """ + $(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(x...) -> Symbol + +Create a `Symbol` by concatenating the string representations of the arguments together. +""" +Symbol + +""" + tuple(xs...) + +Construct a tuple of the given objects. + +# Examples +```jldoctest +julia> tuple(1, 'a', pi) +(1, 'a', π = 3.1415926535897...) +``` +""" +tuple + +""" + getfield(value, name::Symbol) + +Extract a named field from a `value` of composite type. The syntax `a.b` calls +`getfield(a, :b)`. + +# Examples +```jldoctest +julia> a = 1//2 +1//2 + +julia> getfield(a, :num) +1 +``` +""" +getfield + +""" + setfield!(value, name::Symbol, x) + +Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls +`setfield!(a, :b, c)`. +""" +setfield! + +""" + typeof(x) + +Get the concrete type of `x`. +""" +typeof + +""" + isdefined(m::Module, s::Symbol) + isdefined(object, s::Symbol) + isdefined(object, index::Int) + +Tests whether an assignable location is defined. The arguments can be a module and a symbol +or a composite object and field name (as a symbol) or index. +""" +isdefined + + +""" + Vector{T}(n) + +Construct an uninitialized [`Vector{T}`](@ref) of length `n`. + +# Examples +```julia-repl +julia> Vector{Float64}(3) +3-element Array{Float64,1}: + 6.90966e-310 + 6.90966e-310 + 6.90966e-310 +``` +""" +Vector{T}(n) + +""" + Matrix{T}(m, n) + +Construct an uninitialized [`Matrix{T}`](@ref) of size `m`×`n`. + +# Examples +```julia-repl +julia> Matrix{Float64}(2, 3) +2×3 Array{Float64,2}: + 6.93517e-310 6.93517e-310 6.93517e-310 + 6.93517e-310 6.93517e-310 1.29396e-320 +``` +""" +Matrix{T}(m, n) + +""" + Array{T}(dims) + Array{T,N}(dims) + +Construct an uninitialized `N`-dimensional [`Array`](@ref) +containing elements of type `T`. `N` can either be supplied explicitly, +as in `Array{T,N}(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`. + +# Examples +```julia-repl +julia> A = Array{Float64,2}(2, 3) # N given explicitly +2×3 Array{Float64,2}: + 6.90198e-310 6.90198e-310 6.90198e-310 + 6.90198e-310 6.90198e-310 0.0 + +julia> B = Array{Float64}(2) # N determined by the input +2-element Array{Float64,1}: + 1.87103e-320 + 0.0 +``` +""" +Array{T,N}(dims) + +""" + +(x, y...) + +Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. +""" +(+)(x, y...) + +""" + -(x) + +Unary minus operator. +""" +-(x) + +""" + -(x, y) + +Subtraction operator. +""" +-(x, y) + +""" + *(x, y...) + +Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. +""" +(*)(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. +""" +/(x, y) + +""" + ArgumentError(msg) + +The parameters to a function call do not match a valid signature. Argument `msg` is a +descriptive error string. +""" +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. +""" +AssertionError + +""" + LoadError(file::AbstractString, line::Int, error) + +An error occurred while `include`ing, `require`ing, or `using` a file. The error specifics +should be available in the `.error` field. +""" +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{AbstractString, Int64} + +julia> 1 :: IntOrString +1 + +julia> "Hello!" :: IntOrString +"Hello!" + +julia> 1.0 :: IntOrString +ERROR: TypeError: typeassert: expected Union{AbstractString, Int64}, got Float64 +``` +""" +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 + +""" + :: + +With the `::`-operator type annotations are attached to expressions and variables in programs. +See the manual section on [Type Declarations](@ref). + +Outside of declarations `::` is used to assert that expressions and variables in programs have a given type. + +# Examples +```jldoctest +julia> (1+2)::AbstractFloat +ERROR: TypeError: typeassert: expected AbstractFloat, got Int64 + +julia> (1+2)::Int +3 +``` +""" +kw"::" + +""" + Vararg{T,N} + +The last parameter of a tuple type [`Tuple`](@ref) can be the special type `Vararg`, which denotes any +number of trailing elements. The type `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. +`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.) + +# Examples +```jldoctest +julia> mytupletype = Tuple{AbstractString,Vararg{Int}} +Tuple{AbstractString,Vararg{Int64,N} where N} + +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...} + +Tuples are an abstraction of the arguments of a function – without the function itself. The salient aspects of +a function's arguments are their order and their types. Therefore a tuple type is similar to a parameterized +immutable type where each parameter is the type of one field. Tuple types may have any number of parameters. + +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. + +See the manual section on [Tuple Types](@ref). +""" +Tuple + end diff --git a/base/docs/helpdb.jl b/base/docs/helpdb.jl deleted file mode 100644 index 5e2a4fe44b28a..0000000000000 --- a/base/docs/helpdb.jl +++ /dev/null @@ -1,3 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -include(joinpath("helpdb", "Base.jl")) diff --git a/base/docs/helpdb/Base.jl b/base/docs/helpdb/Base.jl deleted file mode 100644 index 616c7ca0dd268..0000000000000 --- a/base/docs/helpdb/Base.jl +++ /dev/null @@ -1,2199 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# Base - -""" - 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 Array{Float64,2}: - 0.0 0.0 0.0 - 0.0 0.0 0.0 - -julia> fill!(A, 2.) -2×3 Array{Float64,2}: - 2.0 2.0 2.0 - 2.0 2.0 2.0 - -julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(3), a); a[1] = 2; A -3-element Array{Array{Int64,1},1}: - [2, 1, 1] - [2, 1, 1] - [2, 1, 1] - -julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(3), f()) -3-element Array{Int64,1}: - 1 - 1 - 1 -``` -""" -fill! - -""" - read!(stream::IO, array::Union{Array, BitArray}) - read!(filename::AbstractString, array::Union{Array, BitArray}) - -Read binary data from an I/O stream or file, filling in `array`. -""" -read! - -""" - pointer(array [, index]) - -Get the native address of an array or string element. Be careful to ensure that a Julia -reference to `a` exists as long as this pointer will be used. This function is "unsafe" like -`unsafe_convert`. - -Calling `Ref(array[, index])` is generally preferable to this function. -""" -pointer - -""" - precision(num::AbstractFloat) - -Get the precision of a floating point number, as defined by the effective number of bits in -the mantissa. -""" -precision - -""" - -(x) - -Unary minus operator. -""" --(x) - -""" - -(x, y) - -Subtraction operator. -""" --(x, y) - -""" - bits(n) - -A string giving the literal bit representation of a number. - -# Examples -```jldoctest -julia> bits(4) -"0000000000000000000000000000000000000000000000000000000000000100" - -julia> bits(2.2) -"0100000000000001100110011001100110011001100110011001100110011010" -``` -""" -bits - -""" - 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, ...)`. - -# Examples -```jldoctest -julia> A = Dict("a" => 1, "b" => 2) -Dict{String,Int64} with 2 entries: - "b" => 2 - "a" => 1 - -julia> getindex(A, "a") -1 -``` -""" -getindex(collection, key...) - -""" - 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`. -""" -cconvert - -""" - unsafe_copy!(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. -""" -unsafe_copy!{T}(dest::Ptr{T}, src::Ptr{T}, N) - -""" - unsafe_copy!(dest::Array, do, src::Array, so, N) - -Copy `N` elements from a source array to a destination, starting at offset `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. -""" -unsafe_copy!(dest::Array, d, src::Array, so, N) - -""" - 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) - -""" - Mmap.mmap(io::Union{IOStream,AbstractString,Mmap.AnonymousMmap}[, type::Type{Array{T,N}}, dims, offset]; grow::Bool=true, shared::Bool=true) - Mmap.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` 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) -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.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). -""" -Mmap.mmap(io, ::Type, dims, offset) - -""" - Mmap.mmap(io, BitArray, [dims, offset]) - -Create a `BitArray` 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.mmap), but -the byte representation is different. - -**Example**: `B = Mmap.mmap(s, BitArray, (25,30000))` - -This would create a 25-by-30000 `BitArray`, linked to the file associated with stream `s`. -""" -Mmap.mmap(io, ::BitArray, dims, offset) - -""" - sizeof(T) - -Size, in bytes, of the canonical binary representation of the given DataType `T`, if any. - -# Examples -```jldoctest -julia> sizeof(Float32) -4 - -julia> sizeof(Complex128) -16 -``` - -If `T` does not have a specific size, an error is thrown. - -```jldoctest -julia> sizeof(Base.LinAlg.LU) -ERROR: argument is an abstract type; size is indeterminate -Stacktrace: - [1] sizeof(::Type{T} where T) at ./essentials.jl:150 -``` -""" -sizeof(::Type) - -""" - ReadOnlyMemoryError() - -An operation tried to write to memory that is read-only. -""" -ReadOnlyMemoryError - -""" - ceil([T,] x, [digits, [base]]) - -`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. - -`digits` and `base` work as for [`round`](@ref). -""" -ceil - -""" - oftype(x, y) - -Convert `y` to the type of `x` (`convert(typeof(x), y)`). -""" -oftype - -""" - push!(collection, items...) -> collection - -Insert one or more `items` at the end of `collection`. - -# Examples -```jldoctest -julia> push!([1, 2, 3], 4, 5, 6) -6-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - 6 -``` - -Use [`append!`](@ref) to add all the elements of another collection to -`collection`. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, -5, 6])`. -""" -push! - -""" - promote(xs...) - -Convert all arguments to their common promotion type (if any), and return them all (as a tuple). - -# Examples -```jldoctest -julia> promote(Int8(1), Float16(4.5), Float32(4.1)) -(1.0f0, 4.5f0, 4.1f0) -``` -""" -promote - -""" - fd(stream) - -Returns 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 - -""" - ones([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) - -Create an array of all ones with the same layout as `A`, element type `T` and size `dims`. -The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. -For convenience `dims` may also be passed in variadic form. - -# Examples -```jldoctest -julia> ones(Complex128, 2, 3) -2×3 Array{Complex{Float64},2}: - 1.0+0.0im 1.0+0.0im 1.0+0.0im - 1.0+0.0im 1.0+0.0im 1.0+0.0im - -julia> ones(1,2) -1×2 Array{Float64,2}: - 1.0 1.0 - -julia> A = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> ones(A) -2×2 Array{Int64,2}: - 1 1 - 1 1 - -julia> ones(A, Float64) -2×2 Array{Float64,2}: - 1.0 1.0 - 1.0 1.0 - -julia> ones(A, Bool, (3,)) -3-element Array{Bool,1}: - true - true - true -``` -See also [`zeros`](@ref), [`similar`](@ref). -""" -ones - -""" - randsubseq!(S, A, p) - -Like [`randsubseq`](@ref), but the results are stored in `S` -(which is resized as needed). -""" -randsubseq! - -""" - /(x, y) - -Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives -floating-point results for integer arguments. -""" -Base.:(/) - -""" - dump(x) - -Show every part of the representation of a value. -""" -dump - -""" - tuple(xs...) - -Construct a tuple of the given objects. - -# Examples -```jldoctest -julia> tuple(1, 'a', pi) -(1, 'a', π = 3.1415926535897...) -``` -""" -tuple - -""" - eachmatch(r::Regex, s::AbstractString[, overlap::Bool=false]) - -Search for all matches of a the regular expression `r` in `s` and return a 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. -""" -eachmatch - -""" - 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. -""" -truncate - -""" - exp10(x) - -Compute ``10^x``. - -# Examples -```jldoctest -julia> exp10(2) -100.0 - -julia> exp10(0.2) -1.5848931924611136 -``` -""" -exp10 - -""" - select(v, k, [by=,] [lt=,] [rev=false]) - -Variant of [`select!`](@ref) which copies `v` before partially sorting it, thereby returning the -same thing as `select!` but leaving `v` unmodified. -""" -select - -""" - 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 - -""" - Mmap.Anonymous(name, readonly, create) - -Create an `IO`-like object for creating zeroed-out mmapped-memory that is not tied to a file -for use in `Mmap.mmap`. Used by `SharedArray` for creating shared memory arrays. -""" -Mmap.Anonymous - -""" - floor([T,] x, [digits, [base]]) - -`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. - -`digits` and `base` work as for [`round`](@ref). -""" -floor - -""" - ErrorException(msg) - -Generic error type. The error message, in the `.msg` field, may provide more specific details. -""" -ErrorException - -""" - reverse(v [, start=1 [, stop=length(v) ]] ) - -Return a copy of `v` reversed from start to stop. - -# Examples -```jldoctest -julia> A = collect(1:5) -5-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - -julia> reverse(A) -5-element Array{Int64,1}: - 5 - 4 - 3 - 2 - 1 - -julia> reverse(A, 1, 4) -5-element Array{Int64,1}: - 4 - 3 - 2 - 1 - 5 - -julia> reverse(A, 3, 5) -5-element Array{Int64,1}: - 1 - 2 - 5 - 4 - 3 -``` -""" -reverse - -""" - reverse!(v [, start=1 [, stop=length(v) ]]) -> v - -In-place version of [`reverse`](@ref). -""" -reverse! - -""" - UndefRefError() - -The item or field is not defined for the given object. -""" -UndefRefError - -""" - append!(collection, collection2) -> collection. - -Add the elements of `collection2` to the end of `collection`. - -# Examples -```jldoctest -julia> append!([1],[2,3]) -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> append!([1, 2, 3], [4, 5, 6]) -6-element Array{Int64,1}: - 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 is of the preceding example is equivalent to -`push!([1, 2, 3], 4, 5, 6)`. -""" -append! - -""" - skip(s, offset) - -Seek a stream relative to the current position. -""" -skip - -""" - 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 - -""" - getfield(value, name::Symbol) - -Extract a named field from a `value` of composite type. The syntax `a.b` calls -`getfield(a, :b)`. - -# Examples -```jldoctest -julia> a = 1//2 -1//2 - -julia> getfield(a, :num) -1 -``` -""" -getfield - -""" - select!(v, k, [by=,] [lt=,] [rev=false]) - -Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and -`rev` 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 via a non-stable -algorithm. 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 `select!` does not fully sort the input -array. - -# Examples -```jldoctest -julia> a = [1, 2, 4, 3, 4] -5-element Array{Int64,1}: - 1 - 2 - 4 - 3 - 4 - -julia> select!(a, 4) -4 - -julia> a -5-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 4 - -julia> a = [1, 2, 4, 3, 4] -5-element Array{Int64,1}: - 1 - 2 - 4 - 3 - 4 - -julia> select!(a, 4, rev=true) -2 - -julia> a -5-element Array{Int64,1}: - 4 - 4 - 3 - 2 - 1 -``` -""" -select! - -""" - 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) - -""" - union(s1,s2...) - ∪(s1,s2...) - -Construct the union of two or more sets. Maintains order with arrays. - -# Examples -```jldoctest -julia> union([1, 2], [3, 4]) -4-element Array{Int64,1}: - 1 - 2 - 3 - 4 - -julia> union([1, 2], [2, 4]) -3-element Array{Int64,1}: - 1 - 2 - 4 - -julia> union([4, 2], [1, 2]) -3-element Array{Int64,1}: - 4 - 2 - 1 -``` -""" -union - -""" - realmax(T) - -The highest finite value representable by the given floating-point DataType `T`. - -# Examples -```jldoctest -julia> realmax(Float16) -Float16(6.55e4) - -julia> realmax(Float32) -3.4028235f38 -``` -""" -realmax - -""" - serialize(stream, 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. In -general, this process will not work if the reading and writing are done by different -versions of Julia, or an instance of Julia with a different system image. `Ptr` values are -serialized as all-zero bit patterns (`NULL`). -""" -serialize - -""" - typemin(T) - -The lowest value representable by the given (real) numeric DataType `T`. - -# Examples -```jldoctest -julia> typemin(Float16) --Inf16 - -julia> typemin(Float32) --Inf32 -``` -""" -typemin - -""" - typeof(x) - -Get the concrete type of `x`. -""" -typeof - -""" - trunc([T,] x, [digits, [base]]) - -`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value -is less than or equal to `x`. - -`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is -not representable. - -`digits` and `base` work as for [`round`](@ref). -""" -trunc - -""" - 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) -""" -unsafe_convert - -""" - seek(s, pos) - -Seek a stream to the given position. -""" -seek - -""" - cglobal((symbol, library) [, type=Void]) - -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{Void}` if no `Type` argument is -supplied. -The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), -respectively. -""" -cglobal - -""" - endof(collection) -> Integer - -Returns the last index of the collection. - -# Examples -```jldoctest -julia> endof([1,2,4]) -3 -``` -""" -endof - -""" - next(iter, state) -> item, state - -For a given iterable object and iteration state, return the current item and the next iteration state. - -# Examples -```jldoctest -julia> next(1:5, 3) -(3, 4) - -julia> next(1:5, 5) -(5, 6) -``` -""" -next - -""" - sizehint!(s, n) - -Suggest that collection `s` reserve capacity for at least `n` elements. This can improve performance. -""" -sizehint! - -""" - OutOfMemoryError() - -An operation allocated too much memory for either the system or the garbage collector to -handle properly. -""" -OutOfMemoryError - -""" - finalize(x) - -Immediately run finalizers registered for object `x`. -""" -finalize - -""" - BoundsError([a],[i]) - -An indexing operation into an array, `a`, tried to access an out-of-bounds element at index `i`. - -# Examples -```jldoctest -julia> A = ones(7); - -julia> A[8] -ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] -Stacktrace: - [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:586 - -julia> B = ones(2, 3); - -julia> B[2, 4] -ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] -Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:587 - -julia> B[9] -ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] -Stacktrace: - [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:586 -``` -""" -BoundsError - -""" - 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). - -# Examples -```jldoctest -julia> f(x::Real) = x^2; - -julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); - -julia> f(2) -5 -``` -""" -invoke - -""" - parse(str, start; greedy=true, raise=true) - -Parse the expression string and return an expression (which could later be passed to eval -for execution). `start` is the index of the first character to start parsing. 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. - -```jldoctest -julia> parse("x = 3, y = 5", 7) -(:(y = 5), 13) - -julia> parse("x = 3, y = 5", 5) -(:((3, y) = 5), 13) -``` -""" -parse(str, start) - -""" - parse(str; raise=true) - -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. - -```jldoctest -julia> parse("x = 3") -:(x = 3) - -julia> parse("x = ") -:($(Expr(:incomplete, "incomplete: premature end of input"))) - -julia> parse("1.0.2") -ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"") -Stacktrace: -[...] - -julia> parse("1.0.2"; raise = false) -:($(Expr(:error, "invalid numeric constant \"1.0.\""))) -``` -""" -parse(str) - -""" - parse(type, str, [base]) - -Parse a string as a number. If the type is an integer type, then a base can be specified -(the default is 10). If the type is a floating point type, the string is parsed as a decimal -floating point number. If the string does not contain a valid number, an error is raised. - -```jldoctest -julia> parse(Int, "1234") -1234 - -julia> parse(Int, "1234", 5) -194 - -julia> parse(Int, "afc", 16) -2812 - -julia> parse(Float64, "1.2e-3") -0.0012 -``` -""" -parse(T::Type, str, base=Int) - -""" - position(s) - -Get the current position of a stream. -""" -position - -""" - selectperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) - -Return a partial permutation of the vector `v`, according to the order specified by -`by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values -if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index -(Integer), an array of the first `k` indices is returned; if `k` is a range, an array of -those indices is returned. Note that the handling of integer values for `k` is different -from [`select`](@ref) in that it returns a vector of `k` elements instead of just the `k` th -element. Also note that this is equivalent to, but more efficient than, calling -`sortperm(...)[k]`. -""" -selectperm - -""" - reinterpret(type, A) - -Change the type-interpretation of a block of memory. -For arrays, this constructs an array with the same binary data as the given -array, but with the specified element type. -For example, -`reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a -[`Float32`](@ref). - -!!! warning - - It is not allowed to `reinterpret` an array to an element type with a larger alignment then - the alignment of the array. For a normal `Array`, this is the alignment of its element type. - For a reinterpreted array, this is the alignment of the `Array` it was reinterpreted from. - For example, `reinterpret(UInt32, UInt8[0, 0, 0, 0])` is not allowed but - `reinterpret(UInt32, reinterpret(UInt8, Float32[1.0]))` is allowed. - -# Examples -```jldoctest -julia> reinterpret(Float32, UInt32(7)) -1.0f-44 - -julia> reinterpret(Float32, UInt32[1 2 3 4 5]) -1×5 Array{Float32,2}: - 1.4013f-45 2.8026f-45 4.2039f-45 5.60519f-45 7.00649f-45 -``` -""" -reinterpret - -""" - bswap(n) - -Byte-swap an integer. Flip the bits of its binary representation. - -# Examples -```jldoctest -julia> a = bswap(4) -288230376151711744 - -julia> bswap(a) -4 - -julia> bin(1) -"1" - -julia> bin(bswap(1)) -"100000000000000000000000000000000000000000000000000000000" -``` -""" -bswap - -""" - delete!(collection, key) - -Delete the mapping for the given key in a collection, 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 -``` -""" -delete! - -""" - big(x) - -Convert a number to a maximum precision representation (typically [`BigInt`](@ref) or -`BigFloat`). See [`BigFloat`](@ref) for information about some pitfalls with floating-point numbers. -""" -big - -""" - typejoin(T, S) - -Compute a type that contains both `T` and `S`. -""" -typejoin - -""" - selectperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) - -Like [`selectperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` -(the default), ix is initialized to contain the values `1:length(ix)`. -""" -selectperm! - -""" - precompile(f,args::Tuple{Vararg{Any}}) - -Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it. -""" -precompile - -""" - 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. - -# 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) - -""" - get(f::Function, 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 - -""" - Mmap.sync!(array) - -Forces synchronization between the in-memory version of a memory-mapped `Array` or -`BitArray` and the on-disk version. -""" -Mmap.sync! - -""" - hash(x[, h::UInt]) - -Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The -optional second argument `h` is a 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 `==` (hence -`isequal`) to guarantee the property mentioned above. -""" -hash - -""" - read(stream::IO, T) - -Read a single value of type `T` from `stream`, in canonical binary representation. - - read(stream::IO, String) - -Read the entirety of `stream`, as a String. -""" -read(stream, t) - -""" - shift!(collection) -> item - -Remove the first `item` from `collection`. - -# Examples -```jldoctest -julia> A = [1, 2, 3, 4, 5, 6] -6-element Array{Int64,1}: - 1 - 2 - 3 - 4 - 5 - 6 - -julia> shift!(A) -1 - -julia> A -5-element Array{Int64,1}: - 2 - 3 - 4 - 5 - 6 -``` -""" -shift! - -""" - spawn(command) - -Run a command object asynchronously, returning the resulting `Process` object. -""" -spawn - -""" - isdefined(m::Module, s::Symbol) - isdefined(object, s::Symbol) - isdefined(object, index::Int) - -Tests whether an assignable location is defined. The arguments can be a module and a symbol -or a composite object and field name (as a symbol) or index. -""" -isdefined - -""" - wait([x]) - -Block the current task until some event occurs, depending on the type of the argument: - -* [`RemoteChannel`](@ref) : Wait for a value to become available on the specified remote - channel. -* [`Future`](@ref) : Wait for a value to become available for the specified future. -* [`Channel`](@ref): Wait for a value to be appended to the channel. -* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. -* `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, returning its result value. If the task fails - with an exception, the exception is propagated (re-thrown in the task that called `wait`). -* `RawFD`: Wait for changes on a file descriptor (see [`poll_fd`](@ref) for keyword - arguments and return code) - -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. -""" -wait - -""" - 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. -""" -copy - -""" - isempty(collection) -> Bool - -Determine whether a collection is empty (has no elements). - -# Examples -```jldoctest -julia> isempty([]) -true - -julia> isempty([1 2 3]) -false -``` -""" -isempty - -""" - 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: convert(Float64, 1 + 2im) -Stacktrace: - [1] convert(::Type{Float64}, ::Complex{Int64}) at ./complex.jl:37 -``` -""" -InexactError - -""" - typemax(T) - -The highest value representable by the given (real) numeric `DataType`. -""" -typemax - -""" - 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 will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). -Stacktrace: - [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 - [2] sqrt at ./math.jl:462 [inlined] - [3] sqrt(::Int64) at ./math.jl:472 -``` -""" -DomainError - -""" - Task(func) - -Create a `Task` (i.e. coroutine) to execute the given function (which must be -callable with no arguments). The task exits when this function returns. - -# Examples -```jldoctest -julia> a() = det(rand(1000, 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 - -""" - ==(x, y) - -Generic equality operator, giving a single [`Bool`](@ref) result. Falls back to `===`. -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. - -Follows IEEE semantics for floating-point numbers. - -Collections should generally implement `==` by calling `==` recursively on all contents. - -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. -""" -Base.:(==) - -""" - seekstart(s) - -Seek a stream to its beginning. -""" -seekstart - -""" - nfields(x) -> Int - -Get the number of fields in the given object. -""" -nfields - -""" - show(stream, 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` (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(stream, ::MIME"mime", x::T) = ...`, -where `mime` is a MIME-type string and the function body calls `write` (or similar) to write -that representation of `x` to `stream`. (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(stream, ::MIME"image/png", x::MyImage) = ...` to allow -your images to be displayed on any PNG-capable `Display` (such as IJulia). As usual, be sure -to `import Base.show` in order to add new methods to the built-in Julia function -`show`. - -The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` -output that calls `show` with 2 arguments. Therefore, this case should be handled by -defining a 2-argument `show(stream::IO, x::MyType)` method. - -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 first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. -See [`IOContext`](@ref) for details. -""" -show(stream, mime, x) - -""" - isless(x, y) - -Test whether `x` is less than `y`, according to a canonical total order. Values that are -normally unordered, such as `NaN`, are ordered in an arbitrary but consistent fashion. This -is the default comparison used by [`sort`](@ref). Non-numeric types with a canonical total order -should implement this function. Numeric types only need to implement it if they have special -values such as `NaN`. -""" -isless - -""" - error(message::AbstractString) - -Raise an `ErrorException` with the given message. -""" -error - -""" - readcsv(source, [T::Type]; options...) - -Equivalent to [`readdlm`](@ref) with `delim` set to comma, and type optionally defined by `T`. -""" -readcsv - -""" - UndefVarError(var::Symbol) - -A symbol in the current scope is not defined. -""" -UndefVarError - -""" - gc() - -Perform garbage collection. This should not generally be used. -""" -gc - -""" - unsafe_trunc(T, x) - -`unsafe_trunc(T, x)` returns the nearest integral value of type `T` whose absolute value is -less than or equal to `x`. If the value is not representable by `T`, an arbitrary value will -be returned. -""" -unsafe_trunc - -""" - parent(A) - -Returns the "parent array" of an array view type (e.g., `SubArray`), or the array itself if -it is not a view. - -# Examples -```jldoctest -julia> a = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> s_a = Symmetric(a) -2×2 Symmetric{Int64,Array{Int64,2}}: - 1 2 - 2 4 - -julia> parent(s_a) -2×2 Array{Int64,2}: - 1 2 - 3 4 -``` -""" -parent - -""" - gc_enable(on::Bool) - -Control whether garbage collection is enabled using a boolean argument (`true` for enabled, -`false` for disabled). Returns previous GC state. Disabling garbage collection should be -used only with extreme caution, as it can cause memory use to grow without bound. -""" -gc_enable - -""" - OverflowError(msg) - -The result of an expression is too large for the specified type and will cause a wraparound. -""" -OverflowError - -""" - object_id(x) - -Get a hash value for `x` based on object identity. `object_id(x)==object_id(y)` if `x === y`. -""" -object_id - -""" - cat(dims, A...) - -Concatenate the input arrays along the specified dimensions in the iterable `dims`. For -dimensions not in `dims`, all input arrays should have the same size, which will also be the -size of the output array along that dimension. For dimensions in `dims`, the size of the -output array is the sum of the sizes of the input arrays along that dimension. If `dims` is -a single number, the different arrays are tightly stacked along that dimension. If `dims` is -an iterable containing several dimensions, this allows one to construct block diagonal -matrices and their higher-dimensional analogues by simultaneously increasing several -dimensions for every new input array and putting zero blocks elsewhere. For example, -`cat([1,2], matrices...)` builds a block diagonal matrix, i.e. a block matrix with -`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the -diagonal. -""" -cat - -""" - show(x) - -Write an informative text representation of a value to the current output stream. New types -should overload `show(io, x)` where the first argument is a stream. The representation used -by `show` generally includes Julia-specific formatting and type information. -""" -show(x) - -""" - finalizer(x, f) - -Register a function `f(x)` to be called when there are no program-accessible references to -`x`. The type of `x` must be a `mutable struct`, otherwise the behavior of this function is -unpredictable. -""" -finalizer - -""" - TypeError(func::Symbol, context::AbstractString, expected::Type, got) - -A type assertion failure, or calling an intrinsic function with an incorrect argument type. -""" -TypeError - -""" - setfield!(value, name::Symbol, x) - -Assign `x` to a named field in `value` of composite type. The syntax `a.b = c` calls -`setfield!(a, :b, c)`. -""" -setfield! - -""" -``` -*(x, y...) -``` - -Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. -""" -Base.:(*)(x, y...) - -""" - time() - -Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. -""" -time() - -""" - ismatch(r::Regex, s::AbstractString) -> Bool - -Test whether a string contains a match of the given regular expression. -""" -ismatch - -""" - matchall(r::Regex, s::AbstractString[, overlap::Bool=false]) -> Vector{AbstractString} - -Return a vector of the matching substrings from [`eachmatch`](@ref). -""" -matchall - -""" - 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::Function, 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: -```julia -get!(dict, key) do - # default value calculated here - time() -end -``` -""" -get!(f::Function,collection,key) -:@assert - -""" - 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). -It has been designed with simplicity and performance as a goal and does not validate -the data read. Malformed data can result in process termination. The caller has to ensure -the integrity and correctness of data read from `stream`. -""" -deserialize - -""" - length(collection) -> Integer - -Return the number of elements in the collection. - -Use [`endof`](@ref) to get the last valid index of an indexable collection. - -# Examples -```jldoctest -julia> length(1:5) -5 - -julia> length([1, 2, 3, 4]) -4 - -julia> length([1 2; 3 4]) -4 -``` -""" -length(collection) - -""" - InterruptException() - -The process was stopped by a terminal interrupt (CTRL+C). -""" -InterruptException - -""" - issubnormal(f) -> Bool - -Test whether a floating point number is subnormal. -""" -issubnormal - -""" - NullException() - -An attempted access to a [`Nullable`](@ref) with no defined value. - -# Examples -```jldoctest -julia> a = Nullable{Int}() -Nullable{Int64}() - -julia> get(a) -ERROR: NullException() -Stacktrace: - [1] get(::Nullable{Int64}) at ./nullable.jl:92 -``` -""" -NullException - -""" - intersect(s1,s2...) - ∩(s1,s2) - -Construct the intersection of two or more sets. -Maintains order and multiplicity of the first argument for arrays and ranges. - -# Examples -```jldoctest -julia> intersect([1, 2, 3], [3, 4, 5]) -1-element Array{Int64,1}: - 3 - -julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) -3-element Array{Int64,1}: - 4 - 4 - 6 -``` -""" -intersect - -""" - 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. -""" -promote_rule - -""" - match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) - -Search for the first match of the regular expression `r` in `s` and return a `RegexMatch` -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. -""" -match - -""" - start(iter) -> state - -Get initial iteration state for an iterable object. - -# Examples -```jldoctest -julia> start(1:5) -1 - -julia> start([1;2;3]) -1 - -julia> start([4;2;3]) -1 -``` -""" -start - -""" - 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`. -""" -isa - -""" - done(iter, state) -> Bool - -Test whether we are done iterating. - -# Examples -```jldoctest -julia> done(1:5, 3) -false - -julia> done(1:5, 5) -false - -julia> done(1:5, 6) -true -``` -""" -done - -""" - 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: convert(Int64, 3.5) -Stacktrace: - [1] convert(::Type{Int64}, ::Float64) at ./float.jl:680 -``` - -If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@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(Rational{Int32}, x) -1//3 - -julia> convert(Rational{Int64}, x) -6004799503160661//18014398509481984 -``` - -If `T` is a collection type and `x` a collection, the result of `convert(T, x)` may alias -`x`. -```jldoctest -julia> x = Int[1,2,3]; - -julia> y = convert(Vector{Int}, x); - -julia> y === x -true -``` -Similarly, if `T` is a composite type and `x` a related instance, the result of -`convert(T, x)` may alias part or all of `x`. -```jldoctest -julia> x = speye(5); - -julia> typeof(x) -SparseMatrixCSC{Float64,Int64} - -julia> y = convert(SparseMatrixCSC{Float64,Int64}, x); - -julia> z = convert(SparseMatrixCSC{Float32,Int64}, y); - -julia> y === x -true - -julia> z === x -false - -julia> z.colptr === x.colptr -true -``` -""" -convert - -""" - applicable(f, args...) -> Bool - -Determine whether the given generic function has a method applicable to the given arguments. - -# Examples -```jldoctest -julia> function f(x, y) - x + y - end; - -julia> applicable(f, 1) -false - -julia> applicable(f, 1, 2) -true -``` -""" -applicable - -""" - 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). -""" -fma - -""" - copy!(dest, do, src, so, N) - -Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at -offset `do`. Returns `dest`. -""" -copy!(dest,d,src,so,N) - -""" - +(x, y...) - -Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. -""" -+ - -""" - 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)`. -""" -setindex!(collection,value,key...) - -""" - zeros([A::AbstractArray,] [T=eltype(A)::Type,] [dims=size(A)::Tuple]) - -Create an array of all zeros with the same layout as `A`, element type `T` and size `dims`. -The `A` argument can be skipped, which behaves like `Array{Float64,0}()` was passed. -For convenience `dims` may also be passed in variadic form. - -# Examples -```jldoctest -julia> zeros(1) -1-element Array{Float64,1}: - 0.0 - -julia> zeros(Int8, 2, 3) -2×3 Array{Int8,2}: - 0 0 0 - 0 0 0 - -julia> A = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> zeros(A) -2×2 Array{Int64,2}: - 0 0 - 0 0 - -julia> zeros(A, Float64) -2×2 Array{Float64,2}: - 0.0 0.0 - 0.0 0.0 - -julia> zeros(A, Bool, (3,)) -3-element Array{Bool,1}: - false - false - false -``` -See also [`ones`](@ref), [`similar`](@ref). -""" -zeros - -""" - Symbol(x...) -> Symbol - -Create a `Symbol` by concatenating the string representations of the arguments together. -""" -Symbol - -""" - isvalid(value) -> Bool - -Returns `true` if the given value is valid for its type, which currently can be either -`Char` or `String`. -""" -isvalid(value) - -""" - isvalid(T, value) -> Bool - -Returns `true` if the given value is valid for that type. Types currently can -be either `Char` or `String`. Values for `Char` can be of type `Char` or [`UInt32`](@ref). -Values for `String` can be of that type, or `Vector{UInt8}`. -""" -isvalid(T,value) - -""" - 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: - [1] pop!(::Dict{String,Int64}, ::String) at ./dict.jl:539 - -julia> pop!(d, "e", 4) -4 -``` -""" -pop!(collection,key,default) - -""" - pop!(collection) -> item - -Remove an item in `collection` and return it. If `collection` is an -ordered container, the last item is returned. - -# Examples -```jldoctest -julia> A=[1, 2, 3] -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> pop!(A) -3 - -julia> A -2-element Array{Int64,1}: - 1 - 2 - -julia> S = Set([1, 2]) -Set([2, 1]) - -julia> pop!(S) -2 - -julia> S -Set([1]) - -julia> pop!(Dict(1=>2)) -1 => 2 -``` -""" -pop!(collection) - -""" - seekend(s) - -Seek a stream to its end. -""" -seekend - -""" - 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: - [1] div(::Int64, ::Int64) at ./int.jl:183 -``` -""" -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. -""" -Bool - -for bit in (16, 32, 64) - @eval begin - """ - Float$($bit) <: AbstractFloat - - $($bit)-bit floating point number type. - """ - $(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 - -""" - Vector{T}(n) - -Construct an uninitialized [`Vector{T}`](@ref) of length `n`. - -# Examples -```julia-repl -julia> Vector{Float64}(3) -3-element Array{Float64,1}: - 6.90966e-310 - 6.90966e-310 - 6.90966e-310 -``` -""" -Vector{T}(n) - -""" - Matrix{T}(m, n) - -Construct an uninitialized [`Matrix{T}`](@ref) of size `m`×`n`. - -# Examples -```julia-repl -julia> Matrix{Float64}(2, 3) -2×3 Array{Float64,2}: - 6.93517e-310 6.93517e-310 6.93517e-310 - 6.93517e-310 6.93517e-310 1.29396e-320 -``` -""" -Matrix{T}(m, n) - -""" - Array{T}(dims) - Array{T,N}(dims) - -Construct an uninitialized `N`-dimensional [`Array`](@ref) -containing elements of type `T`. `N` can either be supplied explicitly, -as in `Array{T,N}(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`. - -# Examples -```julia-repl -julia> A = Array{Float64,2}(2, 3) # N given explicitly -2×3 Array{Float64,2}: - 6.90198e-310 6.90198e-310 6.90198e-310 - 6.90198e-310 6.90198e-310 0.0 - -julia> B = Array{Float64}(2) # N determined by the input -2-element Array{Float64,1}: - 1.87103e-320 - 0.0 -``` -""" -Array{T,N}(dims) diff --git a/base/docs/utils.jl b/base/docs/utils.jl index 9f7a54f110f5b..7cf5b4f032462 100644 --- a/base/docs/utils.jl +++ b/base/docs/utils.jl @@ -289,7 +289,7 @@ function levsort(search, candidates) scores = map(cand -> (levenshtein(search, cand), -fuzzyscore(search, cand)), candidates) candidates = candidates[sortperm(scores)] i = 0 - for i = 1:length(candidates) + for outer i = 1:length(candidates) levenshtein(search, candidates[i]) > 3 && break end return candidates[1:i] @@ -328,7 +328,7 @@ printmatches(args...; cols = displaysize(STDOUT)[2]) = printmatches(STDOUT, args function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = displaysize(io)[2]) i = 0 total = 0 - for i = 1:length(ss) + 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 diff --git a/base/env.jl b/base/env.jl index c6010c193cc6d..fb2d306e11f62 100644 --- a/base/env.jl +++ b/base/env.jl @@ -77,6 +77,7 @@ similar(::EnvHash) = Dict{String,String}() getindex(::EnvHash, k::AbstractString) = access_env(k->throw(KeyError(k)), k) get(::EnvHash, k::AbstractString, def) = access_env(k->def, k) +get(f::Callable, ::EnvHash, k::AbstractString) = access_env(k->f(), k) in(k::AbstractString, ::KeyIterator{EnvHash}) = _hasenv(k) pop!(::EnvHash, k::AbstractString) = (v = ENV[k]; _unsetenv(k); v) pop!(::EnvHash, k::AbstractString, def) = haskey(ENV,k) ? pop!(ENV,k) : def @@ -142,7 +143,7 @@ end """ withenv(f::Function, kv::Pair...) -Execute `f()` in an environment that is temporarily modified (not replaced as in `setenv`) +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 diff --git a/base/error.jl b/base/error.jl index 60f37eb6c1467..170a5252cefc5 100644 --- a/base/error.jl +++ b/base/error.jl @@ -25,6 +25,11 @@ throw ## native julia error handling ## +""" + error(message::AbstractString) + +Raise an `ErrorException` with the given message. +""" error(s::AbstractString) = throw(ErrorException(s)) """ @@ -85,7 +90,7 @@ systemerror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(str Throw an [`AssertionError`](@ref) if `cond` is `false`. Also available as the macro [`@assert`](@ref). """ -assert(x) = x ? nothing : throw(Main.Base.AssertionError()) +assert(x) = x ? nothing : throw(AssertionError()) """ @assert cond [text] @@ -114,7 +119,7 @@ macro assert(ex, msgs...) # string() might not be defined during bootstrap msg = :(Main.Base.string($(Expr(:quote,msg)))) end - return :($(esc(ex)) ? $(nothing) : throw(Main.Base.AssertionError($msg))) + return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) end struct ExponentialBackOff diff --git a/base/essentials.jl b/base/essentials.jl index 95f1dc75c86bb..42ecf5ae40bbc 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -61,6 +61,77 @@ macro _propagate_inbounds_meta() Expr(:meta, :inline, :propagate_inbounds) 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: convert(Int64, 3.5) +Stacktrace: + [1] convert(::Type{Int64}, ::Float64) at ./float.jl:701 +``` + +If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@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(Rational{Int32}, x) +1//3 + +julia> convert(Rational{Int64}, x) +6004799503160661//18014398509481984 +``` + +If `T` is a collection type and `x` a collection, the result of `convert(T, x)` may alias +`x`. +```jldoctest +julia> x = Int[1,2,3]; + +julia> y = convert(Vector{Int}, x); + +julia> y === x +true +``` +Similarly, if `T` is a composite type and `x` a related instance, the result of +`convert(T, x)` may alias part or all of `x`. +```jldoctest +julia> x = speye(5); + +julia> typeof(x) +SparseMatrixCSC{Float64,Int64} + +julia> y = convert(SparseMatrixCSC{Float64,Int64}, x); + +julia> z = convert(SparseMatrixCSC{Float32,Int64}, y); + +julia> y === x +true + +julia> z === x +false + +julia> z.colptr === x.colptr +true +``` +""" +function convert end + convert(::Type{Any}, @nospecialize(x)) = x convert(::Type{T}, x::T) where {T} = x @@ -201,7 +272,12 @@ convert(::Type{T}, x::Tuple{Any, Vararg{Any}}) where {T<:Tuple} = #convert(::Type{Tuple{}}, ::Tuple{}) = () #convert(::Type{Tuple{Vararg{S}}} where S, ::Tuple{}) = () -oftype(x, c) = convert(typeof(x), c) +""" + oftype(x, y) + +Convert `y` to the type of `x` (`convert(typeof(x), y)`). +""" +oftype(x, y) = convert(typeof(x), y) unsigned(x::Int) = reinterpret(UInt, x) signed(x::UInt) = reinterpret(Int, x) @@ -211,16 +287,83 @@ ptr_arg_cconvert(::Type{Ptr{T}}, x) where {T} = cconvert(T, x) ptr_arg_unsafe_convert(::Type{Ptr{T}}, x) where {T} = unsafe_convert(T, x) ptr_arg_unsafe_convert(::Type{Ptr{Void}}, x) = 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) = convert(T, x) # do the conversion eagerly in most cases 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, A) + +Change the type-interpretation of a block of memory. +For arrays, this constructs an array with the same binary data as the given +array, but with the specified element type. +For example, +`reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a +[`Float32`](@ref). + +!!! warning + + It is not allowed to `reinterpret` an array to an element type with a larger alignment then + the alignment of the array. For a normal `Array`, this is the alignment of its element type. + For a reinterpreted array, this is the alignment of the `Array` it was reinterpreted from. + For example, `reinterpret(UInt32, UInt8[0, 0, 0, 0])` is not allowed but + `reinterpret(UInt32, reinterpret(UInt8, Float32[1.0]))` is allowed. + +# Examples +```jldoctest +julia> reinterpret(Float32, UInt32(7)) +1.0f-44 + +julia> reinterpret(Float32, UInt32[1 2 3 4 5]) +1×5 Array{Float32,2}: + 1.4013f-45 2.8026f-45 4.2039f-45 5.60519f-45 7.00649f-45 +``` +""" reinterpret(::Type{T}, x) where {T} = bitcast(T, x) reinterpret(::Type{Unsigned}, x::Float16) = reinterpret(UInt16,x) reinterpret(::Type{Signed}, x::Float16) = reinterpret(Int16,x) +""" + sizeof(T) + +Size, in bytes, of the canonical binary representation of the given DataType `T`, if any. + +# Examples +```jldoctest +julia> sizeof(Float32) +4 + +julia> sizeof(Complex128) +16 +``` + +If `T` does not have a specific size, an error is thrown. + +```jldoctest +julia> sizeof(Base.LinAlg.LU) +ERROR: argument is an abstract type; size is indeterminate +Stacktrace: + [1] sizeof(::Type{T} where T) at ./essentials.jl:367 +``` +""" sizeof(x) = Core.sizeof(x) function append_any(xs...) @@ -236,7 +379,7 @@ function append_any(xs...) ccall(:jl_array_grow_end, Void, (Any, UInt), out, 16) l += 16 end - Core.arrayset(out, y, i) + Core.arrayset(true, out, y, i) i += 1 end end @@ -245,8 +388,13 @@ function append_any(xs...) end # simple Array{Any} operations needed for bootstrap -setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset(A, x, i) +@eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = Core.arrayset($(Expr(:boundscheck)), A, x, i) + +""" + precompile(f, args::Tuple{Vararg{Any}}) +Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it. +""" function precompile(@nospecialize(f), args::Tuple) ccall(:jl_compile_hint, Int32, (Any,), Tuple{Core.Typeof(f), args...}) != 0 end @@ -264,11 +412,48 @@ section of the Metaprogramming chapter of the manual for more details and exampl """ 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 that the function in which `@boundscheck` is written must be inlined into +its caller with [`@inline`](@ref) in order for `@inbounds` to have effect. + +```jldoctest +julia> @inline function g(A, i) + @boundscheck checkbounds(A, i) + return "accessing (\$A)[\$i]" + end + f1() = return g(1:2, -1) + f2() = @inbounds return g(1:2, -1) +f2 (generic function with 1 method) + +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:428 + [2] checkbounds at ./abstractarray.jl:392 [inlined] + [3] g at ./REPL[20]:2 [inlined] + [4] f1() at ./REPL[20]:5 + +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 [`size`](@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) - # hack: use this syntax since it avoids introducing line numbers - :($(Expr(:boundscheck,true)); - $(esc(blk)); - $(Expr(:boundscheck,:pop))) + return Expr(:if, Expr(:boundscheck), esc(blk)) end """ @@ -276,7 +461,8 @@ end Eliminates array bounds checking within expressions. -In the example below the bound check of array A is skipped to improve performance. +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) @@ -292,19 +478,22 @@ end 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. """ macro inbounds(blk) - :($(Expr(:inbounds,true)); - $(esc(blk)); - $(Expr(:inbounds,:pop))) + return Expr(:block, + Expr(:inbounds, true), + esc(blk), + Expr(:inbounds, :pop)) end macro label(name::Symbol) - Expr(:symboliclabel, name) + return esc(Expr(:symboliclabel, name)) end macro goto(name::Symbol) - Expr(:symbolicgoto, name) + return esc(Expr(:symbolicgoto, name)) end # SimpleVector @@ -417,7 +606,7 @@ function vector_any(@nospecialize xs...) n = length(xs) a = Vector{Any}(n) @inbounds for i = 1:n - Core.arrayset(a,xs[i],i) + Core.arrayset(false, a, xs[i], i) end a end @@ -442,8 +631,6 @@ function as_kwargs(xs) return to end -isempty(itr) = done(itr, start(itr)) - """ invokelatest(f, args...; kwargs...) @@ -459,3 +646,85 @@ function invokelatest(f, args...; kwargs...) inner() = f(args...; kwargs...) Core._apply_latest(inner) end + +# iteration protocol + +""" + next(iter, state) -> item, state + +For a given iterable object and iteration state, return the current item and the next iteration state. + +# Examples +```jldoctest +julia> next(1:5, 3) +(3, 4) + +julia> next(1:5, 5) +(5, 6) +``` +""" +function next end + +""" + start(iter) -> state + +Get initial iteration state for an iterable object. + +# Examples +```jldoctest +julia> start(1:5) +1 + +julia> start([1;2;3]) +1 + +julia> start([4;2;3]) +1 +``` +""" +function start end + +""" + done(iter, state) -> Bool + +Test whether we are done iterating. + +# Examples +```jldoctest +julia> done(1:5, 3) +false + +julia> done(1:5, 5) +false + +julia> done(1:5, 6) +true +``` +""" +function done end + +""" + isempty(collection) -> Bool + +Determine whether a collection is empty (has no elements). + +# Examples +```jldoctest +julia> isempty([]) +true + +julia> isempty([1 2 3]) +false +``` +""" +isempty(itr) = done(itr, start(itr)) + +""" + 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". +""" +values(itr) = itr diff --git a/base/event.jl b/base/event.jl index 6dfbe2a8fc176..74bb6ba3069a3 100644 --- a/base/event.jl +++ b/base/event.jl @@ -18,6 +18,29 @@ mutable struct Condition Condition() = new([]) end +""" + wait([x]) + +Block the current task until some event occurs, depending on the type of the argument: + +* [`RemoteChannel`](@ref) : Wait for a value to become available on the specified remote + channel. +* [`Future`](@ref) : Wait for a value to become available for the specified future. +* [`Channel`](@ref): Wait for a value to be appended to the channel. +* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. +* `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, returning its result value. If the task fails + with an exception, the exception is propagated (re-thrown in the task that called `wait`). +* `RawFD`: Wait for changes on a file descriptor (see [`poll_fd`](@ref) for keyword + arguments and return code) + +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::Condition) ct = current_task() diff --git a/base/exports.jl b/base/exports.jl index eecd75f36d46e..608c5f46d0d8f 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -151,22 +151,15 @@ export Cwstring, # Exceptions - ArgumentError, DimensionMismatch, CapturedException, CompositeException, EOFError, - ErrorException, InvalidStateException, KeyError, - LoadError, - InitError, - MethodError, NullException, ParseError, SystemError, - TypeError, - AssertionError, UnicodeError, # Global constants and variables @@ -193,10 +186,7 @@ export NaN64, im, π, pi, - e, eu, - γ, eulergamma, - catalan, - φ, golden, + ℯ, I, # Operators @@ -495,10 +485,13 @@ export minmax, ndims, nonzeros, - countnz, ones, parent, parentindexes, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, permute, permute!, permutedims, @@ -524,8 +517,6 @@ export searchsorted, searchsortedfirst, searchsortedlast, - select!, - select, shuffle, shuffle!, size, @@ -533,8 +524,6 @@ export sort!, sort, sortcols, - selectperm, - selectperm!, sortperm, sortperm!, sortrows, @@ -562,8 +551,8 @@ export cond, condskeel, cross, - ctranspose!, - ctranspose, + adjoint!, + adjoint, det, diag, diagind, @@ -579,7 +568,6 @@ export eigvals, eigvals!, eigvecs, - expm, eye, factorize, givens, @@ -598,7 +586,6 @@ export linreg, logabsdet, logdet, - logm, lu, lufact!, lufact, @@ -622,7 +609,6 @@ export schur, schurfact!, schurfact, - sqrtm, svd, svdfact!, svdfact, @@ -650,10 +636,6 @@ export # bitarrays falses, flipbits!, - rol, - rol!, - ror, - ror!, trues, # dequeues @@ -706,6 +688,7 @@ export mapreducedim, merge!, merge, + pairs, #pop!, #push!, reduce, @@ -762,6 +745,7 @@ export graphemes, hex, hex2bytes, + hex2bytes!, ind2chr, info, is_assigned_char, diff --git a/base/fastmath.jl b/base/fastmath.jl index 0256d0e8e0761..69b5cb7f758e5 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -270,7 +270,7 @@ sqrt_fast(x::FloatTypes) = sqrt_llvm(x) const libm = Base.libm_name -for f in (:acos, :acosh, :asin, :asinh, :atan, :atanh, :cbrt, :cos, +for f in (:acos, :acosh, :asinh, :atan, :atanh, :cbrt, :cos, :cosh, :exp2, :expm1, :lgamma, :log10, :log1p, :log2, :log, :sin, :sinh, :tan, :tanh) f_fast = fast_op[f] @@ -292,36 +292,22 @@ atan2_fast(x::Float32, y::Float32) = atan2_fast(x::Float64, y::Float64) = ccall(("atan2",libm), Float64, (Float64,Float64), x, y) +asin_fast(x::FloatTypes) = asin(x) + # explicit implementations -# FIXME: Change to `ccall((:sincos, libm))` when `Ref` calling convention can be -# stack allocated. @inline function sincos_fast(v::Float64) - return Base.llvmcall(""" - %f = bitcast i8 *%1 to void (double, double *, double *)* - %ps = alloca double - %pc = alloca double - call void %f(double %0, double *%ps, double *%pc) - %s = load double, double* %ps - %c = load double, double* %pc - %res0 = insertvalue [2 x double] undef, double %s, 0 - %res = insertvalue [2 x double] %res0, double %c, 1 - ret [2 x double] %res - """, Tuple{Float64,Float64}, Tuple{Float64,Ptr{Void}}, v, cglobal((:sincos, libm))) + s = Ref{Cdouble}() + c = Ref{Cdouble}() + ccall((:sincos, libm), Void, (Cdouble, Ptr{Cdouble}, Ptr{Cdouble}), v, s, c) + return (s[], c[]) end @inline function sincos_fast(v::Float32) - return Base.llvmcall(""" - %f = bitcast i8 *%1 to void (float, float *, float *)* - %ps = alloca float - %pc = alloca float - call void %f(float %0, float *%ps, float *%pc) - %s = load float, float* %ps - %c = load float, float* %pc - %res0 = insertvalue [2 x float] undef, float %s, 0 - %res = insertvalue [2 x float] %res0, float %c, 1 - ret [2 x float] %res - """, Tuple{Float32,Float32}, Tuple{Float32,Ptr{Void}}, v, cglobal((:sincosf, libm))) + s = Ref{Cfloat}() + c = Ref{Cfloat}() + ccall((:sincosf, libm), Void, (Cfloat, Ptr{Cfloat}, Ptr{Cfloat}), v, s, c) + return (s[], c[]) end @inline function sincos_fast(v::Float16) diff --git a/base/file.jl b/base/file.jl index 9876aeb5ac89a..dff7b0905c791 100644 --- a/base/file.jl +++ b/base/file.jl @@ -254,6 +254,8 @@ function touch(path::AbstractString) end end +const temp_prefix = "jl_" + if Sys.iswindows() function tempdir() @@ -265,12 +267,16 @@ function tempdir() resize!(temppath,lentemppath) return transcode(String, temppath) end + tempname(uunique::UInt32=UInt32(0)) = tempname(tempdir(), uunique) -const temp_prefix = cwstring("jl_") + function tempname(temppath::AbstractString,uunique::UInt32) tempp = cwstring(temppath) + temppfx = cwstring(temp_prefix) tname = Vector{UInt16}(32767) - uunique = ccall(:GetTempFileNameW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32,Ptr{UInt16}), tempp,temp_prefix,uunique,tname) + uunique = ccall(:GetTempFileNameW,stdcall,UInt32, + (Ptr{UInt16}, Ptr{UInt16}, UInt32, Ptr{UInt16}), + tempp, temppfx, uunique, tname) lentname = findfirst(tname,0)-1 if uunique == 0 || lentname <= 0 error("GetTempFileName failed: $(Libc.FormatMessage())") @@ -284,22 +290,6 @@ function mktemp(parent=tempdir()) return (filename, Base.open(filename, "r+")) end -function mktempdir(parent=tempdir()) - seed::UInt32 = rand(UInt32) - while true - if (seed & typemax(UInt16)) == 0 - seed += 1 - end - filename = tempname(parent, seed) - ret = ccall(:_wmkdir, Int32, (Ptr{UInt16},), cwstring(filename)) - if ret == 0 - return filename - end - systemerror(:mktempdir, Libc.errno()!=Libc.EEXIST) - seed += 1 - end -end - else # !windows # Obtain a temporary filename. function tempname() @@ -322,13 +312,6 @@ function mktemp(parent=tempdir()) return (b, fdio(p, true)) end -# Create and return the name of a temporary directory -function mktempdir(parent=tempdir()) - b = joinpath(parent, "tmpXXXXXX") - p = ccall(:mkdtemp, Cstring, (Cstring,), b) - systemerror(:mktempdir, p == C_NULL) - return unsafe_string(p) -end end # os-test @@ -356,12 +339,33 @@ is an open file object for this path. mktemp(parent) """ - mktempdir(parent=tempdir()) + mktempdir(parent=tempdir(); prefix="$temp_prefix") Create a temporary directory in the `parent` directory and return its path. -If `parent` does not exist, throw an error. +If `parent` does not exist, throw an error. An optional `prefix` to the directory name can +be provided. """ -mktempdir(parent) +function mktempdir(parent=tempdir(); prefix="$temp_prefix") + template = prefix*"XXXXXX" + tpath = joinpath(parent, template) + + req = Libc.malloc(_sizeof_uv_fs) + try + ret = ccall(:uv_fs_mkdtemp, Int32, + (Ptr{Void}, Ptr{Void}, Cstring, Ptr{Void}), + eventloop(), req, tpath, C_NULL) + if ret < 0 + ccall(:uv_fs_req_cleanup, Void, (Ptr{Void},), req) + uv_error("mktempdir", ret) + assert(false) + end + path = unsafe_string(ccall(:jl_uv_fs_t_path, Ptr{Cchar}, (Ptr{Void},), req)) + ccall(:uv_fs_req_cleanup, Void, (Ptr{Void},), req) + return path + finally + Libc.free(req) + end +end """ @@ -381,13 +385,14 @@ function mktemp(fn::Function, parent=tempdir()) end """ - mktempdir(f::Function, parent=tempdir()) + mktempdir(f::Function, parent=tempdir(); prefix="$temp_prefix") -Apply the function `f` to the result of [`mktempdir(parent)`](@ref) and remove the -temporary directory upon completion. +Apply the function `f` to the result of [`mktempdir(parent; prefix)`](@ref) and remove the +temporary directory upon completion. An optional `prefix` to the directory name can +be provided. """ -function mktempdir(fn::Function, parent=tempdir()) - tmpdir = mktempdir(parent) +function mktempdir(fn::Function, parent=tempdir(); prefix="$temp_prefix") + tmpdir = mktempdir(parent; prefix=prefix) try fn(tmpdir) finally diff --git a/base/float.jl b/base/float.jl index 40242fab6b112..8be322ba4ea6f 100644 --- a/base/float.jl +++ b/base/float.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +const IEEEFloat = Union{Float16, Float32, Float64} + ## floating point traits ## """ @@ -134,6 +136,10 @@ end function convert(::Type{Float16}, val::Float32) f = reinterpret(UInt32, val) + if isnan(val) + t = 0x8000 ⊻ (0x8000 & ((f >> 0x10) % UInt16)) + return reinterpret(Float16, t ⊻ ((f >> 0xd) % UInt16)) + end i = (f >> 23) & 0x1ff + 1 sh = shifttable[i] f &= 0x007fffff @@ -182,7 +188,7 @@ function convert(::Type{Float32}, val::Float16) ret = 0xff800000 end else # NaN - ret = 0x7fc00000 | (sign<<31) + ret = 0x7fc00000 | (sign<<31) | (sig<<(23-10)) end else sign = sign << 31 @@ -274,6 +280,15 @@ Float64 float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) float(::Type{T}) where {T<:AbstractFloat} = T +""" + unsafe_trunc(T, x) + +`unsafe_trunc(T, x)` returns the nearest integral value of type `T` whose absolute value is +less than or equal to `x`. If the value is not representable by `T`, an arbitrary value will +be returned. +""" +function unsafe_trunc end + for Ti in (Int8, Int16, Int32, Int64) @eval begin unsafe_trunc(::Type{$Ti}, x::Float16) = unsafe_trunc($Ti, Float32(x)) @@ -561,7 +576,14 @@ hash(x::Float64, h::UInt) = isnan(x) ? (hx_NaN ⊻ h) : hx(fptoui(UInt64, abs(x) hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h) hash(x::Float32, h::UInt) = hash(Float64(x), h) -## precision, as defined by the effective number of bits in the mantissa ## +""" + precision(num::AbstractFloat) + +Get the precision of a floating point number, as defined by the effective number of bits in +the mantissa. +""" +function precision end + precision(::Type{Float16}) = 11 precision(::Type{Float32}) = 24 precision(::Type{Float64}) = 53 @@ -576,7 +598,7 @@ signed integer, so that `abs(typemin(x)) == typemin(x) < 0`, in which case the r `uabs(x)` will be an unsigned integer of the same size. """ uabs(x::Integer) = abs(x) -uabs(x::Signed) = unsigned(abs(x)) +uabs(x::BitSigned) = unsigned(abs(x)) """ @@ -585,7 +607,7 @@ uabs(x::Signed) = unsigned(abs(x)) The result of `n` iterative applications of `nextfloat` to `x` if `n >= 0`, or `-n` applications of `prevfloat` if `n < 0`. """ -function nextfloat(f::Union{Float16,Float32,Float64}, d::Integer) +function nextfloat(f::IEEEFloat, d::Integer) F = typeof(f) fumax = reinterpret(Unsigned, F(Inf)) U = typeof(fumax) @@ -686,10 +708,17 @@ for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UIn end end -@eval begin - issubnormal(x::Float32) = (abs(x) < $(bitcast(Float32, 0x00800000))) & (x!=0) - issubnormal(x::Float64) = (abs(x) < $(bitcast(Float64, 0x0010000000000000))) & (x!=0) +""" + issubnormal(f) -> Bool + +Test whether a floating point number is subnormal. +""" +function issubnormal(x::T) where {T<:IEEEFloat} + y = reinterpret(Unsigned, x) + (y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0) +end +@eval begin typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00)) typemax(::Type{Float16}) = $(Inf16) typemin(::Type{Float32}) = $(-Inf32) @@ -706,17 +735,6 @@ end realmax(::Type{Float32}) = $(bitcast(Float32, 0x7f7fffff)) realmax(::Type{Float64}) = $(bitcast(Float64, 0x7fefffffffffffff)) - """ - realmin(T) - - The smallest in absolute value non-subnormal value representable by the given - floating-point DataType `T`. - """ - realmin(x::T) where {T<:AbstractFloat} = realmin(T) - realmax(x::T) where {T<:AbstractFloat} = realmax(T) - realmin() = realmin(Float64) - realmax() = realmax(Float64) - eps(x::AbstractFloat) = isfinite(x) ? abs(x) >= realmin(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)) @@ -724,6 +742,33 @@ end eps() = eps(Float64) end +""" + realmin(T) + +The smallest in absolute value non-subnormal value representable by the given +floating-point DataType `T`. +""" +realmin(x::T) where {T<:AbstractFloat} = realmin(T) + +""" + realmax(T) + +The highest finite value representable by the given floating-point DataType `T`. + +# Examples +```jldoctest +julia> realmax(Float16) +Float16(6.55e4) + +julia> realmax(Float32) +3.4028235f38 +``` +""" +realmax(x::T) where {T<:AbstractFloat} = realmax(T) + +realmin() = realmin(Float64) +realmax() = realmax(Float64) + """ eps(::Type{T}) where T<:AbstractFloat eps() @@ -821,32 +866,11 @@ exponent_half(::Type{Float16}) = 0x3800 significand_mask(::Type{Float16}) = 0x03ff # integer size of float -fpinttype(::Type{Float64}) = UInt64 -fpinttype(::Type{Float32}) = UInt32 -fpinttype(::Type{Float16}) = UInt16 - -## TwicePrecision utilities -# The numeric constants are half the number of bits in the mantissa -for (F, T, n) in ((Float16, UInt16, 5), (Float32, UInt32, 12), (Float64, UInt64, 26)) - @eval begin - function truncbits(x::$F, nb) - @_inline_meta - truncmask(x, typemax($T) << nb) - end - function truncmask(x::$F, mask) - @_inline_meta - reinterpret($F, mask & reinterpret($T, x)) - end - function splitprec(x::$F) - @_inline_meta - hi = truncmask(x, typemax($T) << $n) - hi, x-hi - end - end -end +uinttype(::Type{Float64}) = UInt64 +uinttype(::Type{Float32}) = UInt32 +uinttype(::Type{Float16}) = UInt16 -truncbits(x, nb) = x -truncmask(x, mask) = x +Base.iszero(x::Float16) = reinterpret(UInt16, x) & ~sign_mask(Float16) == 0x0000 ## Array operations on floating point numbers ## @@ -861,7 +885,8 @@ 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) = StepRangeLen(float(r.ref), float(r.step), length(r), r.offset) +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::LinSpace) LinSpace(float(r.start), float(r.stop), length(r)) end diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 39b30c73c2e6b..6cde2b86a9bc0 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -237,6 +237,16 @@ function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where 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 + fma_libm(x::Float32, y::Float32, z::Float32) = ccall(("fmaf", libm_name), Float32, (Float32,Float32,Float32), x, y, z) fma_libm(x::Float64, y::Float64, z::Float64) = diff --git a/base/gcutils.jl b/base/gcutils.jl new file mode 100644 index 0000000000000..e42cc9e6de303 --- /dev/null +++ b/base/gcutils.jl @@ -0,0 +1,53 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) +==(w::WeakRef, v) = isequal(w.value, v) +==(w, v::WeakRef) = isequal(w, v.value) + +""" + finalizer(x, f) + +Register a function `f(x)` to be called when there are no program-accessible references to +`x`. The type of `x` must be a `mutable struct`, otherwise the behavior of this function is +unpredictable. +""" +function finalizer(@nospecialize(o), @nospecialize(f)) + if isimmutable(o) + error("objects of type ", typeof(o), " cannot be finalized") + end + ccall(:jl_gc_add_finalizer_th, Void, (Ptr{Void}, Any, Any), + Core.getptls(), o, f) +end + +function finalizer(o::T, f::Ptr{Void}) where T + @_inline_meta + if isimmutable(T) + error("objects of type ", T, " cannot be finalized") + end + ccall(:jl_gc_add_ptr_finalizer, Void, (Ptr{Void}, Any, Ptr{Void}), + Core.getptls(), o, f) +end + +""" + finalize(x) + +Immediately run finalizers registered for object `x`. +""" +finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), + Core.getptls(), o) + +""" + gc() + +Perform garbage collection. This should not generally be used. +""" +gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Int32,), full) + +""" + gc_enable(on::Bool) + +Control whether garbage collection is enabled using a boolean argument (`true` for enabled, +`false` for disabled). Returns previous GC state. Disabling garbage collection should be +used only with extreme caution, as it can cause memory use to grow without bound. +""" +gc_enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 diff --git a/base/gmp.jl b/base/gmp.jl index 6ae010c511521..04e627881c8b9 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -21,33 +21,33 @@ else end const CdoubleMax = Union{Float16, Float32, Float64} -gmp_version() = VersionNumber(unsafe_string(unsafe_load(cglobal((:__gmp_version, :libgmp), Ptr{Cchar})))) -gmp_bits_per_limb() = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, :libgmp), Cint))) +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 GMP_VERSION = gmp_version() -const GMP_BITS_PER_LIMB = gmp_bits_per_limb() +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 GMP_BITS_PER_LIMB == 32 +if BITS_PER_LIMB == 32 const Limb = UInt32 const SLimbMax = Union{Int8, Int16, Int32} const ULimbMax = Union{UInt8, UInt16, UInt32} -elseif GMP_BITS_PER_LIMB == 64 +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 == $GMP_BITS_PER_LIMB)") + error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $BITS_PER_LIMB)") end """ - BigInt <: Integer + BigInt <: Signed Arbitrary precision integer type. """ -mutable struct BigInt <: Integer +mutable struct BigInt <: Signed alloc::Cint size::Cint d::Ptr{Limb} @@ -82,11 +82,11 @@ BigInt(x) function __init__() try - if gmp_version().major != GMP_VERSION.major || gmp_bits_per_limb() != GMP_BITS_PER_LIMB - msg = gmp_bits_per_limb() != GMP_BITS_PER_LIMB ? error : warn - msg(string("The dynamically loaded GMP library (version $(gmp_version()) with __gmp_bits_per_limb == $(gmp_bits_per_limb()))\n", - "does not correspond to the compile time version (version $GMP_VERSION with __gmp_bits_per_limb == $GMP_BITS_PER_LIMB).\n", - "Please rebuild Julia.")) + if version().major != VERSION.major || bits_per_limb() != BITS_PER_LIMB + msg = bits_per_limb() != BITS_PER_LIMB ? error : warn + msg("The dynamically loaded GMP library (v\"$(version())\" with __gmp_bits_per_limb == $(bits_per_limb()))\n", + "does not correspond to the compile time version (v\"$VERSION\" with __gmp_bits_per_limb == $BITS_PER_LIMB).\n", + "Please rebuild Julia.") end ccall((:__gmp_set_memory_functions, :libgmp), Void, @@ -98,8 +98,7 @@ function __init__() 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") + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") end end @@ -113,57 +112,57 @@ module MPZ # and `add!(x, a) = add!(x, x, a)`. using Base.GMP: BigInt, Limb -const mpz_t = Ptr{BigInt} +const mpz_t = Ref{BigInt} const bitcnt_t = Culong gmpz(op::Symbol) = (Symbol(:__gmpz_, op), :libgmp) -init!(x::BigInt) = (ccall((:__gmpz_init, :libgmp), Void, (mpz_t,), &x); x) -init2!(x::BigInt, a) = (ccall((:__gmpz_init2, :libgmp), Void, (mpz_t, bitcnt_t), &x, a); x) +init!(x::BigInt) = (ccall((:__gmpz_init, :libgmp), Void, (mpz_t,), x); x) +init2!(x::BigInt, a) = (ccall((:__gmpz_init2, :libgmp), Void, (mpz_t, bitcnt_t), x, a); x) -realloc2!(x, a) = (ccall((:__gmpz_realloc2, :libgmp), Void, (mpz_t, bitcnt_t), &x, a); x) +realloc2!(x, a) = (ccall((:__gmpz_realloc2, :libgmp), Void, (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)) +sizeinbase(a::BigInt, b) = Int(ccall((:__gmpz_sizeinbase, :libgmp), Csize_t, (mpz_t, Cint), a, b)) for op in (:add, :sub, :mul, :fdiv_q, :tdiv_q, :fdiv_r, :tdiv_r, :gcd, :lcm, :and, :ior, :xor) op! = Symbol(op, :!) @eval begin - $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, mpz_t), &x, &a, &b); x) + $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t, mpz_t), x, a, b); x) $op(a::BigInt, b::BigInt) = $op!(BigInt(), 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) + 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)), Void, (mpz_t, mpz_t, Culong), &x, &a, b); x) + $op!(x::BigInt, a::BigInt, b) = (ccall($(gmpz(op)), Void, (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), Void, (mpz_t, Culong, mpz_t), &x, a, &b); x) +ui_sub!(x::BigInt, a, b::BigInt) = (ccall((:__gmpz_ui_sub, :libgmp), Void, (mpz_t, Culong, mpz_t), x, a, b); x) ui_sub(a, b::BigInt) = ui_sub!(BigInt(), a, b) for op in (:scan1, :scan0) - @eval $op(a::BigInt, b) = Int(ccall($(gmpz(op)), Culong, (mpz_t, Culong), &a, b)) + @eval $op(a::BigInt, b) = Int(ccall($(gmpz(op)), Culong, (mpz_t, Culong), a, b)) end -mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, :libgmp), Void, (mpz_t, mpz_t, Clong), &x, &a, b); x) +mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, :libgmp), Void, (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)), Void, (mpz_t, mpz_t), &x, &a); x) + $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Void, (mpz_t, mpz_t), x, a); x) $op(a::BigInt) = $op!(BigInt(), a) end op == :set && continue # MPZ.set!(x) would make no sense @@ -173,51 +172,49 @@ 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)), Void, (mpz_t, $T), &x, a); x) + $op!(x::BigInt, a) = (ccall($(gmpz(op)), Void, (mpz_t, $T), x, a); x) $op(a) = $op!(BigInt(), a) end end -popcount(a::BigInt) = ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), &a) % Int +popcount(a::BigInt) = Int(ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), a)) -mpn_popcount(d::Ptr{Limb}, s::Integer) = ccall((:__gmpn_popcount, :libgmp), Culong, (Ptr{Limb}, Csize_t), d, s) % Int +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), Void, (mpz_t, mpz_t, mpz_t, mpz_t), &x, &y, &a, &b) + ccall((:__gmpz_tdiv_qr, :libgmp), Void, (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), Void, (mpz_t, mpz_t, mpz_t, mpz_t), &x, &a, &b, &c); x) + (ccall((:__gmpz_powm, :libgmp), Void, (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), Void, (mpz_t, mpz_t, mpz_t, mpz_t, mpz_t), - &x, &y, &z, &a, &b) + ccall((:__gmpz_gcdext, :libgmp), Void, (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) = ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), &a, &b) % Int -cmp_si(a::BigInt, b) = ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), &a, b) % Int -cmp_ui(a::BigInt, b) = ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), &a, b) % Int -cmp_d(a::BigInt, b) = ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), &a, b) % Int +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) = ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), &x, a, b) % Int -get_d(a::BigInt) = ccall((:__gmpz_get_d, :libgmp), Cdouble, (mpz_t,), &a) +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) -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), Void, (mpz_t, Clong), &x, a) -import!(x::BigInt, a, b, c, d, e, f) = - ccall((:__gmpz_import, :libgmp), Void, (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}), - &x, a, b, c, d, e, f) +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), Void, (mpz_t, Clong), x, a) +import!(x::BigInt, a, b, c, d, e, f) = ccall((:__gmpz_import, :libgmp), Void, + (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Void}), x, a, b, c, d, e, f) end # module MPZ @@ -232,6 +229,7 @@ widen(::Type{BigInt}) = BigInt signed(x::BigInt) = x convert(::Type{BigInt}, x::BigInt) = x +convert(::Type{Signed}, x::BigInt) = x hastypemax(::Type{BigInt}) = false @@ -316,7 +314,7 @@ rem(x::BigInt, ::Type{Bool}) = !iszero(x) & unsafe_load(x.d) % Bool # never unsa 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{Unsigned,Signed} +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)) @@ -326,7 +324,7 @@ end rem(x::Integer, ::Type{BigInt}) = convert(BigInt, x) -function convert(::Type{T}, x::BigInt) where T<:Unsigned +function convert(::Type{T}, x::BigInt) where T<:Base.BitUnsigned if sizeof(T) < sizeof(Limb) convert(T, convert(Limb,x)) else @@ -335,7 +333,7 @@ function convert(::Type{T}, x::BigInt) where T<:Unsigned end end -function convert(::Type{T}, x::BigInt) where T<:Signed +function convert(::Type{T}, x::BigInt) where T<:Base.BitSigned n = abs(x.size) if sizeof(T) < sizeof(Limb) SLimb = typeof(Signed(one(Limb))) @@ -349,7 +347,7 @@ function convert(::Type{T}, x::BigInt) where T<:Signed end -(::Type{Float64})(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) +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) @@ -392,6 +390,14 @@ convert(::Type{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) for information about some pitfalls with floating-point numbers. +""" +function big end + big(::Type{<:Integer}) = BigInt big(::Type{<:Rational}) = Rational{BigInt} @@ -583,6 +589,8 @@ 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 string(x::BigInt) = dec(x) show(io::IO, x::BigInt) = print(io, string(x)) diff --git a/base/grisu/grisu.jl b/base/grisu/grisu.jl index 0474372233516..64942d2775ba0 100644 --- a/base/grisu/grisu.jl +++ b/base/grisu/grisu.jl @@ -116,7 +116,7 @@ end function Base.show(io::IO, x::Union{Float64,Float32}) if get(io, :compact, false) - _show(io, x, PRECISION, 6, true, true) + _show(io, x, PRECISION, 6, x isa Float64, true) else _show(io, x, SHORTEST, 0, true, false) end diff --git a/base/hashing.jl b/base/hashing.jl index 331d6c214ea3e..ee4be4d384941 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -2,6 +2,17 @@ ## hashing a single value ## +""" + hash(x[, h::UInt]) + +Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The +optional second argument `h` is a 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 `==` (hence +`isequal`) to guarantee the property mentioned above. +""" hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) diff --git a/base/hashing2.jl b/base/hashing2.jl index 37a401ac974f0..b4c548b2ccab1 100644 --- a/base/hashing2.jl +++ b/base/hashing2.jl @@ -136,7 +136,7 @@ function decompose(x::BigFloat)::Tuple{BigInt, Int, Int} s = BigInt() s.size = cld(x.prec, 8*sizeof(GMP.Limb)) # limbs b = s.size * sizeof(GMP.Limb) # bytes - ccall((:__gmpz_realloc2, :libgmp), Void, (Ptr{BigInt}, Culong), &s, 8b) # bits + ccall((:__gmpz_realloc2, :libgmp), Void, (Ref{BigInt}, Culong), s, 8b) # bits ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, Csize_t), s.d, x.d, b) # bytes s, x.exp - 8b, x.sign end diff --git a/base/indices.jl b/base/indices.jl index e55a64dd38597..a5f705d2ac201 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -211,6 +211,7 @@ 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`. """ to_indices(A, I::Tuple) = (@_inline_meta; to_indices(A, indices(A), I)) +to_indices(A, I::Tuple{Any}) = (@_inline_meta; to_indices(A, (linearindices(A),), I)) to_indices(A, inds, ::Tuple{}) = () to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) = (@_inline_meta; (to_index(A, I[1]), to_indices(A, _maybetail(inds), tail(I))...)) @@ -223,7 +224,7 @@ _maybetail(t::Tuple) = tail(t) Represent an AbstractUnitRange of indices as a vector of the indices themselves. -Upon calling `to_indices()`, Colons are converted to Slice objects to represent +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 diff --git a/base/inference.jl b/base/inference.jl index 2c899590a290a..198a5aaaca788 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -53,7 +53,6 @@ end # cond && use(a) # slot property bit flags -const Slot_Assigned = 2 const Slot_AssignedOnce = 16 const Slot_UsedUndef = 32 @@ -404,7 +403,16 @@ const _Type_name = Type.body.name isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _Type_name # true if Type is inlineable as constant (is a singleton) -isconstType(@nospecialize t) = isType(t) && (isleaftype(t.parameters[1]) || t.parameters[1] === Union{}) +function isconstType(@nospecialize t) + isType(t) || return false + p1 = t.parameters[1] + # typeof(Bottom) is special since even though it is as leaftype, + # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it + p1 === typeof(Union{}) && return false + p1 === Union{} && return true + isleaftype(p1) && return true + return false +end iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) @@ -1136,6 +1144,8 @@ function const_datatype_getfield_tfunc(sv, fld) return nothing end +getfield_tfunc(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) = + getfield_tfunc(s00, name) function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if isa(s00, TypeVar) s00 = s00.ub @@ -1146,13 +1156,9 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) rewrap(getfield_tfunc(s.b, name),s00)) elseif isa(s, Conditional) return Bottom # Bool has no fields - elseif isa(s, Const) || isType(s) + elseif isa(s, Const) || isconstType(s) if !isa(s, Const) - p1 = s.parameters[1] - if !isleaftype(p1) - return Any - end - sv = p1 + sv = s.parameters[1] else sv = s.val end @@ -1188,7 +1194,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) end s = typeof(sv) end - if !isa(s,DataType) || s.abstract + if isType(s) || !isa(s, DataType) || s.abstract return Any end if s <: Tuple && name ⊑ Symbol @@ -1256,8 +1262,10 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) # in the current type system return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) end -add_tfunc(getfield, 2, 2, (@nospecialize(s), @nospecialize(name)) -> getfield_tfunc(s, name), 1) +add_tfunc(getfield, 2, 3, getfield_tfunc, 1) add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) +fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds)) = + fieldtype_tfunc(s0, name) function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 return Type @@ -1317,7 +1325,7 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) end return Type{<:ft} end -add_tfunc(fieldtype, 2, 2, fieldtype_tfunc, 0) +add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) function valid_tparam(@nospecialize(x)) if isa(x,Tuple) @@ -1511,27 +1519,25 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, elseif f === svec return SimpleVector elseif f === arrayset - if length(argtypes) < 3 && !isva + if length(argtypes) < 4 + isva && return Any return Bottom end - a1 = argtypes[1] - if isvarargtype(a1) - return unwrap_unionall(a1).parameters[1] - end - return a1 + return argtypes[2] elseif f === arrayref - if length(argtypes) < 2 && !isva + if length(argtypes) < 3 + isva && return Any return Bottom end - a = widenconst(argtypes[1]) + a = widenconst(argtypes[2]) if a <: Array - if isa(a,DataType) && (isa(a.parameters[1],Type) || isa(a.parameters[1],TypeVar)) + if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) # TODO: the TypeVar case should not be needed here a = a.parameters[1] - return isa(a,TypeVar) ? a.ub : a - elseif isa(a,UnionAll) && !has_free_typevars(a) + return isa(a, TypeVar) ? a.ub : a + elseif isa(a, UnionAll) && !has_free_typevars(a) unw = unwrap_unionall(a) - if isa(unw,DataType) + if isa(unw, DataType) return rewrap_unionall(unw.parameters[1], a) end end @@ -2446,6 +2452,8 @@ function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) return abstract_eval_constant(e.args[1]) elseif e.head === :invoke error("type inference data-flow error: tried to double infer a function") + elseif e.head === :boundscheck + return Bool elseif e.head === :isdefined sym = e.args[1] t = Bool @@ -3154,6 +3162,10 @@ function typeinf_work(frame::InferenceState) # directly forward changes to an SSAValue to the applicable line record_ssa_assign(changes_var.id + 1, changes.vtype.typ, frame) end + elseif isa(stmt, NewvarNode) + sn = slot_id(stmt.slot) + changes = changes::VarTable + changes[sn] = VarState(Bottom, true) elseif isa(stmt, GotoNode) pc´ = (stmt::GotoNode).label elseif isa(stmt, Expr) @@ -3366,7 +3378,7 @@ function optimize(me::InferenceState) type_annotate!(me) # run optimization passes on fulltree - force_noinline = false + force_noinline = true if me.optimize # This pass is required for the AST to be valid in codegen # if any `SSAValue` is created by type inference. Ref issue #6068 @@ -3374,11 +3386,7 @@ function optimize(me::InferenceState) # if we start to create `SSAValue` in type inference when not # optimizing and use unoptimized IR in codegen. gotoifnot_elim_pass!(me) - inlining_pass!(me) - # probably not an ideal location for most of these steps, - # but boundscheck elimination is not idempotent and needs to run as part of inlining - code = me.src.code::Array{Any,1} - meta_elim_pass!(code, me.src.propagate_inbounds, coverage_enabled()) + inlining_pass!(me, me.src.propagate_inbounds) # Clean up after inlining gotoifnot_elim_pass!(me) basic_dce_pass!(me) @@ -3389,9 +3397,12 @@ function optimize(me::InferenceState) getfield_elim_pass!(me) # Clean up for `alloc_elim_pass!` and `getfield_elim_pass!` void_use_elim_pass!(me) - filter!(x -> x !== nothing, code) # Pop metadata before label reindexing - force_noinline = popmeta!(code, :noinline)[1] + let code = me.src.code::Array{Any,1} + meta_elim_pass!(code, coverage_enabled()) + filter!(x -> x !== nothing, code) + force_noinline = popmeta!(code, :noinline)[1] + end reindex_labels!(me) end @@ -3564,7 +3575,7 @@ function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, unde elseif isa(subex, Slot) id = slot_id(subex) s = vtypes[id] - vt = widenconst(s.typ) + vt = s.typ if s.undef # find used-undef variables undefs[id] = true @@ -3619,9 +3630,9 @@ end function type_annotate!(sv::InferenceState) # remove all unused ssa values gt = sv.src.ssavaluetypes - for i = 1:length(gt) - if gt[i] === NF - gt[i] = Union{} + for j = 1:length(gt) + if gt[j] === NF + gt[j] = Union{} end end @@ -3672,37 +3683,48 @@ function type_annotate!(sv::InferenceState) end # finish marking used-undef variables - for i = 1:nslots - if undefs[i] - src.slotflags[i] |= Slot_UsedUndef + for j = 1:nslots + if undefs[j] + src.slotflags[j] |= Slot_UsedUndef end end nothing end # widen all Const elements in type annotations -function _widen_all_consts!(e::Expr, untypedload::Vector{Bool}) +function _widen_all_consts!(e::Expr, untypedload::Vector{Bool}, slottypes::Vector{Any}) e.typ = widenconst(e.typ) for i = 1:length(e.args) x = e.args[i] if isa(x, Expr) - _widen_all_consts!(x, untypedload) - elseif isa(x, Slot) && (i != 1 || e.head !== :(=)) - untypedload[slot_id(x)] = true + _widen_all_consts!(x, untypedload, slottypes) + elseif isa(x, TypedSlot) + vt = widenconst(x.typ) + if !(vt === x.typ) + if slottypes[x.id] <: vt + x = SlotNumber(x.id) + untypedload[x.id] = true + else + x = TypedSlot(x.id, vt) + end + e.args[i] = x + end + elseif isa(x, SlotNumber) && (i != 1 || e.head !== :(=)) + untypedload[x.id] = true end end nothing end + function widen_all_consts!(src::CodeInfo) for i = 1:length(src.ssavaluetypes) src.ssavaluetypes[i] = widenconst(src.ssavaluetypes[i]) end nslots = length(src.slottypes) untypedload = fill(false, nslots) - for i = 1:length(src.code) - x = src.code[i] - isa(x, Expr) && _widen_all_consts!(x, untypedload) - end + e = Expr(:body) + e.args = src.code + _widen_all_consts!(e, untypedload, src.slottypes) for i = 1:nslots src.slottypes[i] = widen_slot_type(src.slottypes[i], untypedload[i]) end @@ -3735,7 +3757,10 @@ end # replace slots 1:na with argexprs, static params with spvals, and increment # other slots by offset. -function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecialize(spsig), spvals::Vector{Any}, offset::Int) +function substitute!( + @nospecialize(e), na::Int, argexprs::Vector{Any}, + @nospecialize(spsig), spvals::Vector{Any}, + offset::Int, boundscheck::Symbol) if isa(e, Slot) id = slot_id(e) if 1 <= id <= na @@ -3752,15 +3777,17 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia end end if isa(e, NewvarNode) - return NewvarNode(substitute!(e.slot, na, argexprs, spsig, spvals, offset)) + return NewvarNode(substitute!(e.slot, na, argexprs, spsig, spvals, offset, boundscheck)) end if isa(e, Expr) e = e::Expr head = e.head if head === :static_parameter - return spvals[e.args[1]] + sp = spvals[e.args[1]] + is_self_quoting(sp) && return sp + return QuoteNode(sp) elseif head === :foreigncall - @assert !isa(spsig,UnionAll) || !isempty(spvals) + @assert !isa(spsig, UnionAll) || !isempty(spvals) for i = 1:length(e.args) if i == 2 e.args[2] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, spvals) @@ -3775,12 +3802,20 @@ function substitute!(@nospecialize(e), na::Int, argexprs::Vector{Any}, @nospecia elseif i == 5 @assert isa(e.args[5], Int) else - e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset) + e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset, boundscheck) end end + elseif head === :boundscheck + if boundscheck === :propagate + return e + elseif boundscheck === :off + return false + else + return true + end elseif !is_meta_expr_head(head) for i = 1:length(e.args) - e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset) + e.args[i] = substitute!(e.args[i], na, argexprs, spsig, spvals, offset, boundscheck) end end end @@ -3896,9 +3931,10 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil if is_known_call(e, arrayref, src, mod) || is_known_call(e, arraylen, src, mod) return false elseif is_known_call(e, getfield, src, mod) - length(ea) == 3 || return false + nargs = length(ea) + (nargs == 3 || nargs == 4) || return false et = exprtype(e, src, mod) - if !isa(et,Const) && !(isType(et) && isleaftype(et)) + if !isa(et, Const) && !(isType(et) && isleaftype(et)) # first argument must be immutable to ensure e is affect_free a = ea[2] typ = widenconst(exprtype(a, src, mod)) @@ -3924,12 +3960,19 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil return false end elseif head === :new - if !allow_volatile - a = ea[1] - typ = widenconst(exprtype(a, src, mod)) - if !isType(typ) || !isa((typ::Type).parameters[1],DataType) || ((typ::Type).parameters[1]::DataType).mutable - return false - end + a = ea[1] + typ = exprtype(a, src, mod) + # `Expr(:new)` of unknown type could raise arbitrary TypeError. + typ, isexact = instanceof_tfunc(typ) + isexact || return false + (isleaftype(typ) && !iskindtype(typ)) || return false + typ = typ::DataType + if !allow_volatile && typ.mutable + return false + end + fieldcount(typ) >= length(ea) - 1 || return false + for fld_idx in 1:(length(ea) - 1) + exprtype(ea[fld_idx + 1], src, mod) ⊑ fieldtype(typ, fld_idx) || return false end # fall-through elseif head === :return @@ -4161,8 +4204,9 @@ end # `ft` is the type of the function. `f` is the exact function if known, or else `nothing`. # `pending_stmts` is an array of statements from functions inlined so far, so # we can estimate the total size of the enclosing function after inlining. -function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector{Any}, sv::InferenceState, - pending_stmts) +function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector{Any}, + pending_stmt::Vector{Any}, boundscheck::Symbol, + sv::InferenceState) argexprs = e.args if (f === typeassert || ft ⊑ typeof(typeassert)) && length(atypes)==3 @@ -4460,22 +4504,10 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector add_backedge!(linfo, sv) end - spvals = Any[] - for i = 1:length(methsp) - push!(spvals, methsp[i]) - end - for i = 1:length(spvals) - si = spvals[i] - if isa(si, Symbol) || isa(si, SSAValue) || isa(si, Slot) - spvals[i] = QuoteNode(si) - end - end - nm = length(unwrap_unionall(metharg).parameters) body = Expr(:block) body.args = ast - propagate_inbounds = src.propagate_inbounds # see if each argument occurs only once in the body expression stmts = [] @@ -4536,7 +4568,7 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector end # ok, substitute argument expressions for argument names in the body - body = substitute!(body, na, argexprs, method.sig, spvals, length(sv.src.slotnames) - na) + body = substitute!(body, na, argexprs, method.sig, Any[methsp...], length(sv.src.slotnames) - na, boundscheck) append!(sv.src.slotnames, src.slotnames[(na + 1):end]) append!(sv.src.slottypes, src.slottypes[(na + 1):end]) append!(sv.src.slotflags, src.slotflags[(na + 1):end]) @@ -4657,22 +4689,6 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector push!(stmts, Expr(:meta, :pop_loc)) end end - if !isempty(stmts) && !propagate_inbounds - # avoid redundant inbounds annotations - s_1, s_end = stmts[1], stmts[end] - i = 2 - while length(stmts) > i && ((isa(s_1,Expr)&&s_1.head===:line) || isa(s_1,LineNumberNode)) - s_1 = stmts[i] - i += 1 - end - if isa(s_1, Expr) && s_1.head === :inbounds && s_1.args[1] === false && - isa(s_end, Expr) && s_end.head === :inbounds && s_end.args[1] === :pop - else - # inlined statements are out-of-bounds by default - unshift!(stmts, Expr(:inbounds, false)) - push!(stmts, Expr(:inbounds, :pop)) - end - end if isa(expr, Expr) old_t = e.typ @@ -4815,18 +4831,54 @@ function mk_tuplecall(args, sv::InferenceState) return e end -function inlining_pass!(sv::InferenceState) +function inlining_pass!(sv::InferenceState, propagate_inbounds::Bool) + # Also handles bounds check elision: + # + # 1. If check_bounds is always on, set `Expr(:boundscheck)` true + # 2. If check_bounds is always off, set `Expr(:boundscheck)` false + # 3. If check_bounds is default, figure out whether each boundscheck + # is true, false, or propagate based on the enclosing inbounds directives + _opt_check_bounds = JLOptions().check_bounds + opt_check_bounds = (_opt_check_bounds == 0 ? :default : + _opt_check_bounds == 1 ? :on : + :off) + # Number of stacked inbounds + inbounds_depth = 0 + eargs = sv.src.code i = 1 stmtbuf = [] while i <= length(eargs) ei = eargs[i] if isa(ei, Expr) - eargs[i] = inlining_pass(ei, sv, stmtbuf, 1) - if !isempty(stmtbuf) - splice!(eargs, i:i-1, stmtbuf) - i += length(stmtbuf) - empty!(stmtbuf) + if ei.head === :inbounds + eargs[i] = nothing + arg1 = ei.args[1] + if arg1 === true # push + inbounds_depth += 1 + elseif arg1 === false # clear + inbounds_depth = 0 + elseif inbounds_depth > 0 # pop + inbounds_depth -= 1 + end + else + if opt_check_bounds === :off + boundscheck = :off + elseif opt_check_bounds === :on + boundscheck = :on + elseif inbounds_depth > 0 + boundscheck = :off + elseif propagate_inbounds + boundscheck = :propagate + else + boundscheck = :on + end + eargs[i] = inlining_pass(ei, sv, stmtbuf, 1, boundscheck) + if !isempty(stmtbuf) + splice!(eargs, i:(i - 1), stmtbuf) + i += length(stmtbuf) + empty!(stmtbuf) + end end end i += 1 @@ -4837,7 +4889,7 @@ const corenumtype = Union{Int32, Int64, Float32, Float64} # return inlined replacement for `e`, inserting new needed statements # at index `ins` in `stmts`. -function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) +function inlining_pass(e::Expr, sv::InferenceState, stmts::Vector{Any}, ins, boundscheck::Symbol) if e.head === :meta # ignore meta nodes return e @@ -4847,10 +4899,14 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) return e end # inliners for special expressions + if e.head === :boundscheck + return e + end if e.head === :isdefined isa(e.typ, Const) && return e.typ.val return e end + eargs = e.args if length(eargs) < 1 return e @@ -4893,7 +4949,7 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) argloc = eargs end sl0 = length(stmts) - res = inlining_pass(ei, sv, stmts, ins) + res = inlining_pass(ei, sv, stmts, ins, boundscheck) ns = length(stmts) - sl0 # number of new statements just added if isccallee restype = exprtype(res, sv.src, sv.mod) @@ -4985,11 +5041,11 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) exprtype(a1, sv.src, sv.mod) ⊑ basenumtype) if square e.args = Any[GlobalRef(Main.Base,:*), a1, a1] - res = inlining_pass(e, sv, stmts, ins) + res = inlining_pass(e, sv, stmts, ins, boundscheck) else e.args = Any[GlobalRef(Main.Base,:*), Expr(:call, GlobalRef(Main.Base,:*), a1, a1), a1] e.args[2].typ = e.typ - res = inlining_pass(e, sv, stmts, ins) + res = inlining_pass(e, sv, stmts, ins, boundscheck) end return res end @@ -5005,7 +5061,7 @@ function inlining_pass(e::Expr, sv::InferenceState, stmts, ins) (a === Bottom || isvarargtype(a)) && return e ata[i] = a end - res = inlineable(f, ft, e, ata, sv, stmts) + res = inlineable(f, ft, e, ata, stmts, boundscheck, sv) if isa(res,Tuple) if isa(res[2],Array) && !isempty(res[2]) splice!(stmts, ins:ins-1, res[2]) @@ -5110,7 +5166,7 @@ function add_slot!(src::CodeInfo, @nospecialize(typ), is_sa::Bool, name::Symbol= id = length(src.slotnames) + 1 push!(src.slotnames, name) push!(src.slottypes, typ) - push!(src.slotflags, Slot_Assigned + is_sa * Slot_AssignedOnce) + push!(src.slotflags, is_sa * Slot_AssignedOnce) return SlotNumber(id) end @@ -5304,7 +5360,7 @@ function find_sa_vars(src::CodeInfo, nargs::Int) end end end - filter!((v, _) -> !haskey(av2, v), av) + filter!(p -> !haskey(av2, p.first), av) return av end @@ -5404,7 +5460,7 @@ function void_use_elim_pass!(sv::InferenceState) nothing end -function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_coverage::Bool) +function meta_elim_pass!(code::Array{Any,1}, do_coverage::Bool) # 1. Remove place holders # # 2. If coverage is off, remove line number nodes that don't mark any @@ -5412,54 +5468,6 @@ function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_covera # # 3. Remove top level SSAValue # - # 4. Handle bounds check elision - # - # 4.1. If check_bounds is always on, delete all `Expr(:boundscheck)` - # 4.2. If check_bounds is always off, delete all boundscheck blocks. - # 4.3. If check_bounds is default, figure out whether each checkbounds - # blocks needs to be eliminated or could be eliminated when inlined - # into another function. Delete the blocks that should be eliminated - # and delete the `Expr(:boundscheck)` for blocks that will never be - # deleted. (i.e. the ones that are not eliminated with - # `length(inbounds_stack) >= 2`) - # - # When deleting IR with boundscheck, keep the label node in order to not - # confuse later passes or codegen. (we could also track if any SSAValue - # is deleted while still having uses that are not but that's a little - # expensive). - # - # 5. Clean up `Expr(:inbounds)` - # - # Delete all `Expr(:inbounds)` that is unnecessary, which is all of them - # for non-default check_bounds. For default check_bounds this includes - # - # * `Expr(:inbounds, true)` in `Expr(:inbounds, true)` - # * `Expr(:inbounds, false)` when - # `!is_inbounds && length(inbounds_stack) >= 2` - # - # Functions without `propagate_inbounds` have an implicit `false` on the - # `inbounds_stack` - # - # There are other cases in which we can eliminate `Expr(:inbounds)` or - # `Expr(:boundscheck)` (e.g. when they don't enclose any non-meta - # expressions). Those are a little harder to detect and are hopefully - # not too common. - check_bounds = JLOptions().check_bounds - - inbounds_stack = propagate_inbounds ? Bool[] : Bool[false] - # Whether the push is deleted (therefore if the pop has to be too) - # Shared for `Expr(:boundscheck)` and `Expr(:inbounds)` - bounds_elim_stack = Bool[] - # The expression index of the push, set to `0` when encountering a - # non-meta expression that might be affect by the push. - # The clearing needs to be propagated up during pop - # This is not pushed to if the push is already eliminated - # Also shared for `Expr(:boundscheck)` and `Expr(:inbounds)` - bounds_push_pos_stack = Int[0] # always non-empty - # Number of boundscheck pushes in a eliminated boundscheck block - void_boundscheck_depth = 0 - is_inbounds = check_bounds == 2 - enabled = true # Position of the last line number node without any non-meta expressions # in between. @@ -5487,140 +5495,16 @@ function meta_elim_pass!(code::Array{Any,1}, propagate_inbounds::Bool, do_covera prev_dbg_stack[end] = i continue elseif !isa(ex, Expr) - if enabled - prev_dbg_stack[end] = 0 - push_loc_pos_stack[end] = 0 - bounds_push_pos_stack[end] = 0 - else - code[i] = nothing - end + prev_dbg_stack[end] = 0 + push_loc_pos_stack[end] = 0 continue end ex = ex::Expr args = ex.args head = ex.head - if head === :boundscheck - if !enabled - # we are in an eliminated boundscheck, simply record the number - # of push/pop - if !(args[1] === :pop) - void_boundscheck_depth += 1 - elseif void_boundscheck_depth == 0 - # There must have been a push - pop!(bounds_elim_stack) - enabled = true - else - void_boundscheck_depth -= 1 - end - code[i] = nothing - elseif args[1] === :pop - # This will also delete pops that don't match - if (isempty(bounds_elim_stack) ? true : - pop!(bounds_elim_stack)) - code[i] = nothing - continue - end - push_idx = bounds_push_pos_stack[end] - if length(bounds_push_pos_stack) > 1 - pop!(bounds_push_pos_stack) - end - if push_idx > 0 - code[push_idx] = nothing - code[i] = nothing - else - bounds_push_pos_stack[end] = 0 - end - elseif is_inbounds - code[i] = nothing - push!(bounds_elim_stack, true) - enabled = false - elseif check_bounds == 1 || length(inbounds_stack) >= 2 - # Not inbounds and at least two levels deep, this will never - # be eliminated when inlined to another function. - code[i] = nothing - push!(bounds_elim_stack, true) - else - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - continue - end - if !enabled && !(do_coverage && head === :meta) - code[i] = nothing - continue - end - if head === :inbounds - if check_bounds != 0 - code[i] = nothing - continue - end - arg1 = args[1] - if arg1 === true - if !isempty(inbounds_stack) && inbounds_stack[end] - code[i] = nothing - push!(bounds_elim_stack, true) - else - is_inbounds = true - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - push!(inbounds_stack, true) - elseif arg1 === false - if is_inbounds - # There must have been a `true` on the stack so - # `inbounds_stack` must not be empty - if !inbounds_stack[end] - is_inbounds = false - end - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - elseif length(inbounds_stack) >= 2 - code[i] = nothing - push!(bounds_elim_stack, true) - else - push!(bounds_elim_stack, false) - push!(bounds_push_pos_stack, i) - end - push!(inbounds_stack, false) - else - # pop - inbounds_len = length(inbounds_stack) - if inbounds_len != 0 - pop!(inbounds_stack) - inbounds_len -= 1 - end - # This will also delete pops that don't match - if (isempty(bounds_elim_stack) ? true : - pop!(bounds_elim_stack)) - # No need to update `is_inbounds` since the push was a no-op - code[i] = nothing - continue - end - if inbounds_len >= 2 - is_inbounds = (inbounds_stack[inbounds_len] || - inbounds_stack[inbounds_len - 1]) - elseif inbounds_len == 1 - is_inbounds = inbounds_stack[inbounds_len] - else - is_inbounds = false - end - push_idx = bounds_push_pos_stack[end] - if length(bounds_push_pos_stack) > 1 - pop!(bounds_push_pos_stack) - end - if push_idx > 0 - code[push_idx] = nothing - code[i] = nothing - else - bounds_push_pos_stack[end] = 0 - end - end - continue - end if head !== :meta prev_dbg_stack[end] = 0 push_loc_pos_stack[end] = 0 - bounds_push_pos_stack[end] = 0 continue end nargs = length(args) @@ -5671,11 +5555,14 @@ function getfield_elim_pass!(sv::InferenceState) end function _getfield_elim_pass!(e::Expr, sv::InferenceState) - for i = 1:length(e.args) + nargs = length(e.args) + for i = 1:nargs e.args[i] = _getfield_elim_pass!(e.args[i], sv) end - if is_known_call(e, getfield, sv.src, sv.mod) && length(e.args)==3 && - (isa(e.args[3],Int) || isa(e.args[3],QuoteNode)) + if is_known_call(e, getfield, sv.src, sv.mod) && + (nargs == 3 || nargs == 4) && + (isa(e.args[3], Int) || isa(e.args[3], QuoteNode)) && + (nargs == 3 || isa(e.args[4], Bool)) e1 = e.args[2] j = e.args[3] single_use = true diff --git a/base/initdefs.jl b/base/initdefs.jl index 2cab2349383cd..ff96f21855e3a 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -19,10 +19,12 @@ An array of the command line arguments passed to Julia, as strings. const ARGS = String[] """ - exit([code]) + exit(code=0) + +Quit the program with an exit code. The default exit code is zero, indicating that the +program completed successfully (see also [`quit`](@ref)). In an interactive session, +`exit()` can be called with the keyboard shorcut `^D`. -Quit (or control-D at the prompt). The default exit code is zero, indicating that the -processes completed successfully. """ exit(n) = ccall(:jl_exit, Void, (Int32,), n) exit() = exit(0) @@ -30,8 +32,9 @@ exit() = exit(0) """ quit() -Quit the program indicating that the processes completed successfully. This function calls -`exit(0)` (see [`exit`](@ref)). +Quit the program indicating successful completion. This function is equivalent to +`exit(0)` (see [`exit`](@ref)). In an interactive session, `quit()` can be called +with the keyboard shorcut `^D`. """ quit() = exit() diff --git a/base/int.jl b/base/int.jl index 29f206dab146c..e563dcc066df8 100644 --- a/base/int.jl +++ b/base/int.jl @@ -84,7 +84,6 @@ 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::Signed) = convert(typeof(x), flipsign(promote_noncircular(x, y)...)) 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)) @@ -125,7 +124,7 @@ abs(x::Signed) = flipsign(x,x) ~(n::Integer) = -n-1 -unsigned(x::Signed) = reinterpret(typeof(convert(Unsigned, zero(x))), x) +unsigned(x::BitSigned) = reinterpret(typeof(convert(Unsigned, zero(x))), x) unsigned(x::Bool) = convert(Unsigned, x) """ @@ -157,11 +156,11 @@ signed without checking for overflow. """ signed(x) = convert(Signed, x) -div(x::Signed, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x) -div(x::Unsigned, y::Signed) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y)) +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::Signed, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) -rem(x::Unsigned, y::Signed) = rem(x, unsigned(abs(y))) +rem(x::BitSigned, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) +rem(x::Unsigned, y::BitSigned) = rem(x, unsigned(abs(y))) fld(x::Signed, y::Unsigned) = div(x, y) - (signbit(x) & (rem(x, y) != 0)) fld(x::Unsigned, y::Signed) = div(x, y) - (signbit(y) & (rem(x, y) != 0)) @@ -294,6 +293,26 @@ julia> 4 | 1 (|)(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) + +Byte-swap an integer. Flip the bits of its binary representation. + +# Examples +```jldoctest +julia> a = bswap(4) +288230376151711744 + +julia> bswap(a) +4 + +julia> bin(1) +"1" + +julia> bin(bswap(1)) +"100000000000000000000000000000000000000000000000000000000" +``` +""" bswap(x::Union{Int8, UInt8}) = x bswap(x::Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}) = bswap_int(x) @@ -376,12 +395,12 @@ trailing_ones(x::Integer) = trailing_zeros(~x) (<=)(x::T, y::T) where {T<:BitSigned} = sle_int(x, y) (<=)(x::T, y::T) where {T<:BitUnsigned} = ule_int(x, y) -==(x::Signed, y::Unsigned) = (x >= 0) & (unsigned(x) == y) -==(x::Unsigned, y::Signed ) = (y >= 0) & (x == unsigned(y)) -<( x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) < y) -<( x::Unsigned, y::Signed ) = (y >= 0) & (x < unsigned(y)) -<=(x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) <= y) -<=(x::Unsigned, y::Signed ) = (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)) +<=(x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) <= y) +<=(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x <= unsigned(y)) ## integer shifts ## @@ -513,6 +532,45 @@ convert(::Type{Unsigned}, x::Union{Float32, Float64, Bool}) = convert(UInt, x) convert(::Type{Integer}, x::Integer) = x convert(::Type{Integer}, x::Real) = convert(Signed, x) +""" + trunc([T,] x, [digits, [base]]) + +`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value +is less than or equal to `x`. + +`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is +not representable. + +`digits` and `base` work as for [`round`](@ref). +""" +function trunc end + +""" + floor([T,] x, [digits, [base]]) + +`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. + +`digits` and `base` work as for [`round`](@ref). +""" +function floor end + +""" + ceil([T,] x, [digits, [base]]) + +`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. + +`digits` and `base` work as for [`round`](@ref). +""" +function ceil end + round(x::Integer) = x trunc(x::Integer) = x floor(x::Integer) = x @@ -568,6 +626,29 @@ _default_type(::Union{Type{Integer},Type{Signed}}) = Int ## traits ## +""" + typemin(T) + +The lowest value representable by the given (real) numeric DataType `T`. + +# Examples +```jldoctest +julia> typemin(Float16) +-Inf16 + +julia> typemin(Float32) +-Inf32 +``` +""" +function typemin end + +""" + typemax(T) + +The highest value representable by the given (real) numeric `DataType`. +""" +function typemax end + typemin(::Type{Int8 }) = Int8(-128) typemax(::Type{Int8 }) = Int8(127) typemin(::Type{UInt8 }) = UInt8(0) diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 4ad8d2795ad6f..748f075b4b81b 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -330,8 +330,13 @@ function versioninfo(io::IO=STDOUT; verbose::Bool=false, packages::Bool=false) if packages || verbose println(io, "Packages:") println(io, " Package Directory: ", Pkg.dir()) - println(io, " Package Status:") - Pkg.status(io) + print(io, " Package Status:") + if isdir(Pkg.dir()) + println(io, "") + Pkg.status(io) + else + println(io, " no packages installed") + end end end @@ -347,14 +352,35 @@ problematic for performance, so the results need to be used judiciously. See [`@code_warntype`](@ref man-code-warntype) for more information. """ function code_warntype(io::IO, f, @nospecialize(t)) + function slots_used(ci, slotnames) + used = falses(length(slotnames)) + scan_exprs!(used, ci.code) + return used + end + + function scan_exprs!(used, exprs) + for ex in exprs + if isa(ex, Slot) + used[ex.id] = true + elseif isa(ex, Expr) + scan_exprs!(used, ex.args) + end + end + end + emph_io = IOContext(io, :TYPEEMPHASIZE => true) for (src, rettype) in code_typed(f, t) println(emph_io, "Variables:") slotnames = sourceinfo_slotnames(src) + used_slotids = slots_used(src, slotnames) for i = 1:length(slotnames) print(emph_io, " ", slotnames[i]) - if isa(src.slottypes, Array) - show_expr_type(emph_io, src.slottypes[i], true) + if used_slotids[i] + if isa(src.slottypes, Array) + show_expr_type(emph_io, src.slottypes[i], true) + end + else + print(emph_io, " ") end print(emph_io, '\n') end @@ -363,8 +389,7 @@ function code_warntype(io::IO, f, @nospecialize(t)) body.args = src.code body.typ = rettype # Fix slot names and types in function body - show_unquoted(IOContext(IOContext(emph_io, :SOURCEINFO => src), - :SOURCE_SLOTNAMES => slotnames), + show_unquoted(IOContext(emph_io, :SOURCEINFO => src, :SOURCE_SLOTNAMES => slotnames), body, 2) print(emph_io, '\n') end @@ -623,7 +648,7 @@ else rethrow() end elseif downloadcmd == :curl - run(`curl -L -f -o $filename $url`) + run(`curl -g -L -f -o $filename $url`) elseif downloadcmd == :fetch run(`fetch -f $filename $url`) else diff --git a/base/intfuncs.jl b/base/intfuncs.jl index d6c782e33a89e..e60408dce1f33 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -159,12 +159,19 @@ end invmod(n::Integer, m::Integer) = invmod(promote(n,m)...) # ^ for any x supporting * -to_power_type(x::Number) = oftype(x*x, x) -to_power_type(x) = x -@noinline throw_domerr_powbysq(p) = throw(DomainError(p, +to_power_type(x) = convert(promote_op(*, typeof(x), typeof(x)), x) +@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, string("Cannot raise an integer x to a negative power ", p, '.', - "\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ", - "of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p"))) + "\nConvert input to float."))) +@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p, + string("Cannot raise an integer x to a negative power ", p, '.', + "\nMake x a float by adding a zero decimal (e.g., 2.0^$p instead ", + "of 2^$p), or write 1/x^$(-p), float(x)^$p, or (x//1)^$p"))) +@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p, + string("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"))) function power_by_squaring(x_, p::Integer) x = to_power_type(x_) if p == 1 @@ -174,9 +181,9 @@ function power_by_squaring(x_, p::Integer) elseif p == 2 return x*x elseif p < 0 - x == 1 && return copy(x) - x == -1 && return iseven(p) ? one(x) : copy(x) - throw_domerr_powbysq(p) + 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 @@ -196,7 +203,7 @@ function power_by_squaring(x_, p::Integer) 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(p) + p < 0 && !x && throw_domerr_powbysq(x, p) return (p==0) | x end @@ -417,7 +424,7 @@ function ndigits0z(x::UInt128) return n + ndigits0z(UInt64(x)) end -ndigits0z(x::Signed) = ndigits0z(unsigned(abs(x))) +ndigits0z(x::BitSigned) = ndigits0z(unsigned(abs(x))) ndigits0z(x::Integer) = ndigits0zpb(x, 10) @@ -700,6 +707,22 @@ julia> dec(20, 3) """ dec +""" + bits(n) + +A string giving the literal bit representation of a number. + +# Examples +```jldoctest +julia> bits(4) +"0000000000000000000000000000000000000000000000000000000000000100" + +julia> bits(2.2) +"0100000000000001100110011001100110011001100110011001100110011010" +``` +""" +function bits end + bits(x::Union{Bool,Int8,UInt8}) = bin(reinterpret(UInt8,x),8) bits(x::Union{Int16,UInt16,Float16}) = bin(reinterpret(UInt16,x),16) bits(x::Union{Char,Int32,UInt32,Float32}) = bin(reinterpret(UInt32,x),32) @@ -817,9 +840,8 @@ end function factorial(n::Integer) n < 0 && throw(DomainError(n, "`n` must be nonnegative.")) - local f::typeof(n*n), i::typeof(n*n) - f = 1 - for i = 2:n + f::typeof(n*n) = 1 + for i::typeof(n*n) = 2:n f *= i end return f diff --git a/base/intset.jl b/base/intset.jl index bbd2825256c3b..a83a27175aacc 100644 --- a/base/intset.jl +++ b/base/intset.jl @@ -155,13 +155,15 @@ symdiff(s::IntSet, ns) = symdiff!(copy(s), ns) For each element in `itr`, destructively toggle its inclusion in set `s`. """ -symdiff!(s::IntSet, ns) = (for n in ns; symdiff!(s, n); end; s) +symdiff!(s::IntSet, ns) = (for n in ns; int_symdiff!(s, n); end; s) """ symdiff!(s, n) The set `s` is destructively modified to toggle the inclusion of integer `n`. """ -function symdiff!(s::IntSet, n::Integer) +symdiff!(s::IntSet, n::Integer) = int_symdiff!(s, n) + +function int_symdiff!(s::IntSet, n::Integer) 0 < n < typemax(Int) || _throw_intset_bounds_err() val = !(n in s) _setint!(s, n, val) diff --git a/base/io.jl b/base/io.jl index 5959e94434b64..34829dae03a0e 100644 --- a/base/io.jl +++ b/base/io.jl @@ -2,6 +2,27 @@ # Generic IO stubs -- all subtypes should implement these (if meaningful) +""" + EOFError() + +No more data was available to read from a file or stream. +""" +mutable struct EOFError <: Exception end + +""" + SystemError(prefix::AbstractString, [errno::Int32]) + +A system call failed with an error code (in the `errno` global variable). +""" +mutable struct SystemError <: Exception + prefix::AbstractString + 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()) +end + lock(::IO) = nothing unlock(::IO) = nothing reseteof(x::IO) = nothing @@ -56,6 +77,17 @@ function iswritable end function copy end function eof end +""" + read(stream::IO, T) + +Read a single value of type `T` from `stream`, in canonical binary representation. + + read(stream::IO, String) + +Read the entirety of `stream`, as a String. +""" +read(stream, t) + """ write(stream::IO, x) write(filename::AbstractString, x) @@ -169,6 +201,15 @@ Open a file and read its contents. `args` is passed to `read`: this is equivalen Read the entire contents of a file as a string. """ read(filename::AbstractString, args...) = open(io->read(io, args...), filename) + +""" + read!(stream::IO, array::Union{Array, BitArray}) + read!(filename::AbstractString, array::Union{Array, BitArray}) + +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), filename) """ diff --git a/base/iobuffer.jl b/base/iobuffer.jl index a379adfff0e25..15bcd54b1b788 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -36,8 +36,8 @@ StringVector(n::Integer) = Vector{UInt8}(_string_n(n)) Create an `IOBuffer`, which may optionally operate on a pre-existing array. If the readable/writable arguments are given, they restrict whether or not the buffer may be read -from or written to respectively. By default the buffer is readable but not writable. The -last argument optionally specifies a size beyond which the buffer may not be grown. +from or written to respectively. The last argument optionally specifies a size beyond which +the buffer may not be grown. """ IOBuffer(data::AbstractVector{UInt8}, readable::Bool=true, writable::Bool=false, maxsize::Int=typemax(Int)) = GenericIOBuffer(data, readable, writable, true, false, maxsize) diff --git a/base/iostream.jl b/base/iostream.jl index 3ee3c315d36c3..7ae9f112f50f6 100644 --- a/base/iostream.jl +++ b/base/iostream.jl @@ -26,7 +26,15 @@ IOStream(name::AbstractString) = IOStream(name, true) unsafe_convert(T::Type{Ptr{Void}}, s::IOStream) = convert(T, pointer(s.ios)) show(io::IO, s::IOStream) = print(io, "IOStream(", s.name, ")") + +""" + fd(stream) + +Returns 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{Void},), s.ios)) + stat(s::IOStream) = stat(fd(s)) close(s::IOStream) = ccall(:ios_close, Void, (Ptr{Void},), s.ios) isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Void},), s.ios)!=0 @@ -39,11 +47,22 @@ end iswritable(s::IOStream) = ccall(:ios_get_writable, Cint, (Ptr{Void},), s.ios)!=0 isreadable(s::IOStream) = ccall(:ios_get_readable, Cint, (Ptr{Void},), 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. +""" function truncate(s::IOStream, n::Integer) systemerror("truncate", ccall(:ios_trunc, Cint, (Ptr{Void}, Csize_t), s.ios, n) != 0) return s end +""" + seek(s, pos) + +Seek a stream to the given position. +""" function seek(s::IOStream, n::Integer) ret = ccall(:ios_seek, Int64, (Ptr{Void}, Int64), s.ios, n) systemerror("seek", ret == -1) @@ -51,13 +70,28 @@ function seek(s::IOStream, n::Integer) return s end +""" + seekstart(s) + +Seek a stream to its beginning. +""" seekstart(s::IO) = seek(s,0) +""" + seekend(s) + +Seek a stream to its end. +""" function seekend(s::IOStream) systemerror("seekend", ccall(:ios_seek_end, Int64, (Ptr{Void},), s.ios) != 0) return s end +""" + skip(s, offset) + +Seek a stream relative to the current position. +""" function skip(s::IOStream, delta::Integer) ret = ccall(:ios_skip, Int64, (Ptr{Void}, Int64), s.ios, delta) systemerror("skip", ret == -1) @@ -65,6 +99,11 @@ function skip(s::IOStream, delta::Integer) return s end +""" + position(s) + +Get the current position of a stream. +""" function position(s::IOStream) pos = ccall(:ios_pos, Int64, (Ptr{Void},), s.ios) systemerror("position", pos == -1) diff --git a/base/irrationals.jl b/base/irrationals.jl index 27b94a5968903..4e106bc54987b 100644 --- a/base/irrationals.jl +++ b/base/irrationals.jl @@ -121,8 +121,7 @@ macro irrational(sym, val, def) function Base.convert(::Type{BigFloat}, ::Irrational{$qsym}) c = BigFloat() ccall(($(string("mpfr_const_", def)), :libmpfr), - Cint, (Ptr{BigFloat}, Int32), - &c, MPFR.ROUNDING_MODE[]) + Cint, (Ref{BigFloat}, Int32), c, MPFR.ROUNDING_MODE[]) return c end end : quote @@ -142,91 +141,6 @@ end big(x::Irrational) = convert(BigFloat,x) big(::Type{<:Irrational}) = BigFloat -## specific irrational mathematical constants - -@irrational π 3.14159265358979323846 pi -@irrational e 2.71828182845904523536 exp(big(1)) -@irrational γ 0.57721566490153286061 euler -@irrational catalan 0.91596559417721901505 catalan -@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 - -# aliases -""" - pi - π - -The constant pi. - -```jldoctest -julia> pi -π = 3.1415926535897... -``` -""" -π, const pi = π - -""" - e - eu - -The constant e. - -```jldoctest -julia> e -e = 2.7182818284590... -``` -""" -e, const eu = e - -""" - γ - eulergamma - -Euler's constant. - -```jldoctest -julia> eulergamma -γ = 0.5772156649015... -``` -""" -γ, const eulergamma = γ - -""" - φ - golden - -The golden ratio. - -```jldoctest -julia> golden -φ = 1.6180339887498... -``` -""" -φ, const golden = φ - -""" - catalan - -Catalan's constant. - -```jldoctest -julia> catalan -catalan = 0.9159655941772... -``` -""" -catalan - -# special behaviors - -# use exp for e^x or e.^x, as in -# ^(::Irrational{:e}, x::Number) = exp(x) -# but need to loop over types to prevent ambiguity with generic rules for ^(::Number, x) etc. -for T in (Irrational, Rational, Integer, Number) - ^(::Irrational{:e}, x::T) = exp(x) -end - -log(::Irrational{:e}) = 1 # use 1 to correctly promote expressions like log(x)/log(e) -log(::Irrational{:e}, x::Number) = log(x) - # align along = for nice Array printing function alignment(io::IO, x::Irrational) m = match(r"^(.*?)(=.*)$", sprint(0, showcompact, x, env=io)) diff --git a/base/iterators.jl b/base/iterators.jl index d3671af58bb25..9a401adfb2e00 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -2,7 +2,7 @@ module Iterators -import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims +import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims, pairs using Base: tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds @@ -78,14 +78,16 @@ struct IndexValue{I,A<:AbstractArray} end """ - enumerate(IndexLinear(), A) - enumerate(IndexCartesian(), A) - enumerate(IndexStyle(A), A) + 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]`. This -is similar to `enumerate(A)`, except `i` will always be a valid index -for `A`. +`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()` ensures that `i` will be an integer; specifying `IndexCartesian()` ensures that `i` will be a @@ -96,7 +98,7 @@ been defined as the native indexing style for array `A`. ```jldoctest julia> A = ["a" "d"; "b" "e"; "c" "f"]; -julia> for (index, value) in enumerate(IndexStyle(A), A) +julia> for (index, value) in pairs(IndexStyle(A), A) println("\$index \$value") end 1 a @@ -108,7 +110,7 @@ julia> for (index, value) in enumerate(IndexStyle(A), A) julia> S = view(A, 1:2, :); -julia> for (index, value) in enumerate(IndexStyle(S), S) +julia> for (index, value) in pairs(IndexStyle(S), S) println("\$index \$value") end CartesianIndex{2}((1, 1)) a @@ -117,15 +119,14 @@ CartesianIndex{2}((1, 2)) d CartesianIndex{2}((2, 2)) e ``` -Note that `enumerate(A)` returns `i` as a *counter* (always starting -at 1), whereas `enumerate(IndexLinear(), A)` returns `i` as an *index* -(starting at the first linear index of `A`, which may or may not be -1). - See also: [`IndexStyle`](@ref), [`indices`](@ref). """ -enumerate(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) -enumerate(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) +pairs(::IndexLinear, A::AbstractArray) = IndexValue(A, linearindices(A)) +pairs(::IndexCartesian, A::AbstractArray) = IndexValue(A, CartesianRange(indices(A))) + +# faster than zip(keys(a), values(a)) for arrays +pairs(A::AbstractArray) = pairs(IndexCartesian(), A) +pairs(A::AbstractVector) = pairs(IndexLinear(), A) length(v::IndexValue) = length(v.itr) indices(v::IndexValue) = indices(v.itr) @@ -134,11 +135,11 @@ size(v::IndexValue) = size(v.itr) @propagate_inbounds function next(v::IndexValue, state) indx, n = next(v.itr, state) item = v.data[indx] - (indx, item), n + (indx => item), n end @inline done(v::IndexValue, state) = done(v.itr, state) -eltype(::Type{IndexValue{I,A}}) where {I,A} = Tuple{eltype(I), eltype(A)} +eltype(::Type{IndexValue{I,A}}) where {I,A} = Pair{eltype(I), eltype(A)} iteratorsize(::Type{IndexValue{I}}) where {I} = iteratorsize(I) iteratoreltype(::Type{IndexValue{I}}) where {I} = iteratoreltype(I) diff --git a/base/libc.jl b/base/libc.jl index fe3f46d446f9c..34301239516ca 100644 --- a/base/libc.jl +++ b/base/libc.jl @@ -39,7 +39,7 @@ if Sys.iswindows() function dup(src::WindowsRawSocket) new_handle = Ref{Ptr{Void}}(-1) my_process = ccall(:GetCurrentProcess, stdcall, Ptr{Void}, ()) - const DUPLICATE_SAME_ACCESS = 0x2 + DUPLICATE_SAME_ACCESS = 0x2 status = ccall(:DuplicateHandle, stdcall, Int32, (Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Ptr{Void}}, UInt32, Int32, UInt32), my_process, src.handle, my_process, new_handle, 0, false, DUPLICATE_SAME_ACCESS) @@ -155,7 +155,7 @@ mutable struct TmStruct t = floor(t) tm = TmStruct() # TODO: add support for UTC via gmtime_r() - ccall(:localtime_r, Ptr{TmStruct}, (Ptr{Int}, Ptr{TmStruct}), &t, &tm) + ccall(:localtime_r, Ptr{TmStruct}, (Ref{Int}, Ref{TmStruct}), t, tm) return tm end end @@ -170,13 +170,11 @@ library. strftime(t) = strftime("%c", t) strftime(fmt::AbstractString, t::Real) = strftime(fmt, TmStruct(t)) function strftime(fmt::AbstractString, tm::TmStruct) - timestr = Vector{UInt8}(128) - n = ccall(:strftime, Int, (Ptr{UInt8}, Int, Cstring, Ptr{TmStruct}), - timestr, length(timestr), fmt, &tm) - if n == 0 - return "" - end - return String(timestr[1:n]) + timestr = Base.StringVector(128) + n = ccall(:strftime, Int, (Ptr{UInt8}, Int, Cstring, Ref{TmStruct}), + timestr, length(timestr), fmt, tm) + n == 0 && return "" + return String(resize!(timestr,n)) end """ @@ -192,8 +190,7 @@ determine the timezone. strptime(timestr::AbstractString) = strptime("%c", timestr) function strptime(fmt::AbstractString, timestr::AbstractString) tm = TmStruct() - r = ccall(:strptime, Cstring, (Cstring, Cstring, Ptr{TmStruct}), - timestr, fmt, &tm) + 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. @@ -206,7 +203,7 @@ function strptime(fmt::AbstractString, timestr::AbstractString) # if we didn't explicitly parse the weekday or year day, use mktime # to fill them in automatically. if !ismatch(r"([^%]|^)%(a|A|j|w|Ow)", fmt) - ccall(:mktime, Int, (Ptr{TmStruct},), &tm) + ccall(:mktime, Int, (Ref{TmStruct},), tm) end end return tm @@ -219,7 +216,13 @@ end Converts a `TmStruct` struct to a number of seconds since the epoch. """ -time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ptr{TmStruct},), &tm)) +time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm)) + +""" + time() + +Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. +""" time() = ccall(:jl_clock_now, Float64, ()) ## process-related functions ## @@ -290,10 +293,10 @@ if Sys.iswindows() GetLastError() = ccall(:GetLastError, stdcall, UInt32, ()) function FormatMessage(e=GetLastError()) - const FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) - const FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) - const FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) - const FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) + 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, (Cint, Ptr{Void}, Cint, Cint, Ptr{Ptr{UInt16}}, Cint, Ptr{Void}), diff --git a/base/libdl.jl b/base/libdl.jl index 9c2b8255f7788..cd08d66cf2f78 100644 --- a/base/libdl.jl +++ b/base/libdl.jl @@ -234,8 +234,8 @@ function dllist() dynamic_libraries = Vector{AbstractString}(0) @static if Sys.islinux() - const callback = cfunction(dl_phdr_info_callback, Cint, - Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) + callback = cfunction(dl_phdr_info_callback, Cint, + Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) ccall(:dl_iterate_phdr, Cint, (Ptr{Void}, Ref{Vector{AbstractString}}), callback, dynamic_libraries) end @@ -254,8 +254,8 @@ function dllist() end @static if Sys.isbsd() && !Sys.isapple() - const callback = cfunction(dl_phdr_info_callback, Cint, - Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) + callback = cfunction(dl_phdr_info_callback, Cint, + Tuple{Ref{dl_phdr_info}, Csize_t, Ref{Vector{AbstractString}}}) ccall(:dl_iterate_phdr, Cint, (Ptr{Void}, Ref{Vector{AbstractString}}), callback, dynamic_libraries) shift!(dynamic_libraries) end diff --git a/base/libgit2/blame.jl b/base/libgit2/blame.jl index a0dee7bb25f02..ee239a0ce0ae0 100644 --- a/base/libgit2/blame.jl +++ b/base/libgit2/blame.jl @@ -1,5 +1,13 @@ # 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()) blame_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_blame_file, :libgit2), Cint, @@ -8,6 +16,14 @@ function GitBlame(repo::GitRepo, path::AbstractString; options::BlameOptions=Bla 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) return ccall((:git_blame_get_hunk_count, :libgit2), Int32, (Ptr{Void},), blame.ptr) end diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 74fb15bd17946..1b288b8edd4f1 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -17,7 +17,7 @@ function mirror_callback(remote::Ptr{Ptr{Void}}, repo_ptr::Ptr{Void}, config = GitConfig(GitRepo(repo_ptr,false)) name_str = unsafe_string(name) err= try set!(config, "remote.$name_str.mirror", true) - catch -1 + catch; -1 finally close(config) end err != 0 && return Cint(err) @@ -48,8 +48,8 @@ function user_abort() return Cint(Error.EAUTH) end -function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, - username_ptr, schema, host) +function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username_ptr) + creds = Base.get(p.credential)::SSHCredentials isusedcreds = checkused!(creds) # Note: The same SSHCredentials can be used to authenticate separate requests using the @@ -75,7 +75,7 @@ function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, username = username_ptr != Cstring(C_NULL) ? unsafe_string(username_ptr) : "" if isempty(username) uname = creds.user # check if credentials were already used - prompt_url = git_url(scheme=schema, host=host) + prompt_url = git_url(scheme=p.scheme, host=p.host) if !isusedcreds username = uname else @@ -85,7 +85,7 @@ function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, end end - prompt_url = git_url(scheme=schema, host=host, username=username) + prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) # For SSH we need a private key location privatekey = if haskey(ENV,"SSH_KEY_PATH") @@ -167,28 +167,28 @@ function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}}, libgit2credptr, creds.user, creds.pubkey, creds.prvkey, creds.pass) end -function authenticate_userpass(creds::UserPasswordCredentials, libgit2credptr::Ptr{Ptr{Void}}, - schema, host, urlusername) +function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload) + creds = Base.get(p.credential)::UserPasswordCredentials isusedcreds = checkused!(creds) if creds.prompt_if_incorrect username = creds.user userpass = creds.pass - prompt_url = git_url(scheme=schema, host=host) + prompt_url = git_url(scheme=p.scheme, host=p.host) if Sys.iswindows() if isempty(username) || isempty(userpass) || isusedcreds response = Base.winprompt("Please enter your credentials for '$prompt_url'", "Credentials required", - isempty(username) ? urlusername : username; prompt_username = true) + isempty(username) ? p.username : username; prompt_username = true) isnull(response) && return user_abort() username, userpass = unsafe_get(response) end elseif isusedcreds response = Base.prompt("Username for '$prompt_url'", - default=isempty(username) ? urlusername : username) + default=isempty(username) ? p.username : username) isnull(response) && return user_abort() username = unsafe_get(response) - prompt_url = git_url(scheme=schema, host=host, username=username) + prompt_url = git_url(scheme=p.scheme, host=p.host, username=username) response = Base.prompt("Password for '$prompt_url'", password=true) isnull(response) && return user_abort() userpass = unsafe_get(response) @@ -211,7 +211,7 @@ end """Credentials callback function Function provides different credential acquisition functionality w.r.t. a connection protocol. -If a payload is provided then `payload_ptr` should contain a `LibGit2.AbstractCredentials` object. +If a payload is provided then `payload_ptr` should contain a `LibGit2.CredentialPayload` object. For `LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT` type, if the payload contains fields: `user` & `pass`, they are used to create authentication credentials. @@ -238,48 +238,74 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, username_ptr::Cstring, allowed_types::Cuint, payload_ptr::Ptr{Void}) err = Cint(0) - url = unsafe_string(url_ptr) + explicit = false - # parse url for schema and host - urlparts = match(URL_REGEX, url) - schema = urlparts[:scheme] === nothing ? "" : urlparts[:scheme] - urlusername = urlparts[:user] === nothing ? "" : urlparts[:user] - host = urlparts[:host] - - # get credentials object from payload pointer + # get `CredentialPayload` object from payload pointer @assert payload_ptr != C_NULL - creds = unsafe_pointer_to_objref(payload_ptr) - explicit = !isnull(creds[]) && !isa(Base.get(creds[]), CachedCredentials) + p = unsafe_pointer_to_objref(payload_ptr)[]::CredentialPayload + + # Parse URL only during the first call to this function. Future calls will use the + # information cached inside the payload. + if isempty(p.host) + url = match(URL_REGEX, unsafe_string(url_ptr)) + + p.scheme = url[:scheme] === nothing ? "" : url[:scheme] + p.username = url[:user] === nothing ? "" : url[:user] + p.host = url[:host] + p.path = url[:path] + + # 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. + if !isnull(p.credential) + explicit = true + cred = unsafe_get(p.credential) + if isa(cred, SSHCredentials) + allowed_types &= Cuint(Consts.CREDTYPE_SSH_KEY) + elseif isa(cred, UserPasswordCredentials) + allowed_types &= Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT) + else + allowed_types &= Cuint(0) # Unhandled credential type + end + end + end + # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) - sshcreds = get_creds!(creds, "ssh://$host", reset!(SSHCredentials(true), -1)) - if isa(sshcreds, SSHCredentials) - err = authenticate_ssh(sshcreds, libgit2credptr, username_ptr, schema, host) - err == 0 && return err + if isnull(p.credential) || !isa(unsafe_get(p.credential), SSHCredentials) + creds = reset!(SSHCredentials(p.username, "", true), -1) + if !isnull(p.cache) + credid = "ssh://$(p.host)" + creds = get_creds!(unsafe_get(p.cache), credid, creds) + end + p.credential = Nullable(creds) end + err = authenticate_ssh(libgit2credptr, p, username_ptr) + err == 0 && return err end if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) - defaultcreds = reset!(UserPasswordCredentials(true), -1) - credid = "$(isempty(schema) ? "ssh" : schema)://$host" - upcreds = get_creds!(creds, credid, defaultcreds) - # If there were stored SSH credentials, but we ended up here that must - # mean that something went wrong. Replace the SSH credentials by user/pass - # credentials - if !isa(upcreds, UserPasswordCredentials) - upcreds = defaultcreds - isa(Base.get(creds[]), CachedCredentials) && (Base.get(creds[]).creds[credid] = upcreds) + if isnull(p.credential) || !isa(unsafe_get(p.credential), UserPasswordCredentials) + creds = reset!(UserPasswordCredentials(p.username, "", true), -1) + if !isnull(p.cache) + credid = "$(isempty(p.scheme) ? "ssh" : p.scheme)://$(p.host)" + creds = get_creds!(unsafe_get(p.cache), credid, creds) + end + p.credential = Nullable(creds) end - return authenticate_userpass(upcreds, libgit2credptr, schema, host, urlusername) + 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 remote host. + # with the requested authentication method. if err == 0 if explicit - warn("The explicitly provided credentials were incompatible with " * - "the server's supported authentication methods") + ccall((:giterr_set_str, :libgit2), Void, (Cint, Cstring), Cint(Error.Callback), + "The explicitly provided credential is incompatible with the requested " * + "authentication methods.") end err = Cint(Error.EAUTH) end diff --git a/base/libgit2/commit.jl b/base/libgit2/commit.jl index 0cf8ca641076d..cd05299168456 100644 --- a/base/libgit2/commit.jl +++ b/base/libgit2/commit.jl @@ -50,7 +50,6 @@ function Base.show(io::IO, c::GitCommit) print(io, "Git Commit:\nCommit Author: $authstr\nCommitter: $cmtrstr\nSHA: $(GitHash(c))\nMessage:\n$(message(c))") end -""" Wrapper around `git_commit_create` """ function commit(repo::GitRepo, refname::AbstractString, msg::AbstractString, @@ -73,7 +72,25 @@ function commit(repo::GitRepo, return commit_id_ptr[] end -"""Commit changes to repository""" +""" + commit(repo::GitRepo, msg::AbstractString; kwargs...) -> GitHash + +Wrapper around [`git_commit_create`](https://libgit2.github.com/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 commited 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), diff --git a/base/libgit2/config.jl b/base/libgit2/config.jl index 2adf7a82e99b9..9f5a7549b9b48 100644 --- a/base/libgit2/config.jl +++ b/base/libgit2/config.jl @@ -68,7 +68,7 @@ end addfile(cfg::GitConfig, path::AbstractString, level::Consts.GIT_CONFIG=Consts.CONFIG_LEVEL_APP, force::Bool=false) Add an existing git configuration file located at `path` to the current -[`GitConfig`](@ref) `cfg`. If the file does not exist, it will be created. +`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). If `force` is `false` and a configuration for the given priority level already exists, `addfile` will error. If `force` is diff --git a/base/libgit2/consts.jl b/base/libgit2/consts.jl index 0f4ab0989271a..07f6f75c6df44 100644 --- a/base/libgit2/consts.jl +++ b/base/libgit2/consts.jl @@ -2,304 +2,365 @@ module Consts - 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 - - # 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 - @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 - - @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) - - @enum(GIT_MERGE_PREFERENCE, MERGE_PREFERENCE_NONE = 0, - MERGE_PREFERENCE_NO_FASTFORWARD = 1, - MERGE_PREFERENCE_FASTFORWARD_ONLY = 2) - - @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 - @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) - - # 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) - - @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 - - """ +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) + +# 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) + +@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)) +""" +@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 LibGit2.version() >= v"0.24.0" - """ -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) - """ + @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, @@ -309,19 +370,19 @@ These priority levels correspond to the natural escalation logic (from higher to CONFIG_LEVEL_APP = 6, CONFIG_HIGHEST_LEVEL =-1) else - """ -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) - """ + @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, @@ -331,35 +392,35 @@ These priority levels correspond to the natural escalation logic (from higher to 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) - - - """ +""" +@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) +""" +@enum(GIT_PROXY, PROXY_NONE, + PROXY_AUTO, + PROXY_SPECIFIED) end diff --git a/base/libgit2/index.jl b/base/libgit2/index.jl index e75ecf7c14185..2ca285ec788b1 100644 --- a/base/libgit2/index.jl +++ b/base/libgit2/index.jl @@ -74,6 +74,28 @@ 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) @check ccall((:git_index_add_all, :libgit2), Cint, @@ -81,12 +103,28 @@ function add!(idx::GitIndex, files::AbstractString...; 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...) @check ccall((:git_index_update_all, :libgit2), Cint, (Ptr{Void}, Ptr{StrArrayStruct}, Ptr{Void}, Ptr{Void}), 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...) @check ccall((:git_index_remove_all, :libgit2), Cint, (Ptr{Void}, Ptr{StrArrayStruct}, Ptr{Void}, Ptr{Void}), diff --git a/base/libgit2/libgit2.jl b/base/libgit2/libgit2.jl index c9f69d0cb6980..4eec2372f5f68 100644 --- a/base/libgit2/libgit2.jl +++ b/base/libgit2/libgit2.jl @@ -522,7 +522,7 @@ function reset!(repo::GitRepo, committish::AbstractString, pathspecs::AbstractSt end """ - reset!(repo::GitRepo, id::GitHash, mode::Cint = Consts.RESET_MIXED) + 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`: @@ -861,6 +861,14 @@ function restore(s::State, repo::GitRepo) 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 diff --git a/base/libgit2/merge.jl b/base/libgit2/merge.jl index 4a85128e6500d..91556476a581a 100644 --- a/base/libgit2/merge.jl +++ b/base/libgit2/merge.jl @@ -1,5 +1,18 @@ # 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, comittish::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) ann_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_annotated_commit_lookup, :libgit2), Cint, @@ -34,6 +47,34 @@ function GitHash(ann::GitAnnotated) unsafe_load(ccall((:git_annotated_commit_id, :libgit2), Ptr{GitHash}, (Ptr{Void},), ann.ptr)) 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. + +`merge_analysis` returns two outputs. `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}) analysis = Ref{Cint}(0) preference = Ref{Cint}(0) @@ -48,7 +89,9 @@ end """ ffmerge!(repo::GitRepo, ann::GitAnnotated) -Fastforward merge changes into current head +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)) @@ -68,6 +111,29 @@ function ffmerge!(repo::GitRepo, ann::GitAnnotated) 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()) @@ -83,6 +149,40 @@ 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()) @@ -140,6 +240,12 @@ function merge!(repo::GitRepo, anns::Vector{GitAnnotated}, fastforward::Bool; 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) oid1_ptr = Ref(GitHash(one)) oid2_ptr = Ref(GitHash(two)) diff --git a/base/libgit2/oid.jl b/base/libgit2/oid.jl index 3e369a1358349..ad84253e75ce0 100644 --- a/base/libgit2/oid.jl +++ b/base/libgit2/oid.jl @@ -1,5 +1,12 @@ # 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")) @@ -38,6 +45,11 @@ function GitHash(id::AbstractString) return oid_ptr[] end +""" + GitShortHash(buf::Buffer) + +Construct a `GitShortHash` from the data stored in the given [`Buffer`](@ref). +""" function GitShortHash(buf::Buffer) oid_ptr = Ref{GitHash}() @check ccall((:git_oid_fromstrn, :libgit2), Cint, @@ -59,6 +71,21 @@ function GitShortHash(id::AbstractString) 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 @@ -128,6 +155,11 @@ end Base.hex(id::GitHash) = join([hex(i,2) for i in id.val]) Base.hex(id::GitShortHash) = hex(id.hash)[1:id.len] +""" + 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) Base.string(id::AbstractGitHash) = hex(id) @@ -156,6 +188,11 @@ 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 diff --git a/base/libgit2/remote.jl b/base/libgit2/remote.jl index df80810fb0231..f69299c96ae1d 100644 --- a/base/libgit2/remote.jl +++ b/base/libgit2/remote.jl @@ -276,6 +276,7 @@ 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}; @@ -295,6 +296,7 @@ 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 @@ -328,8 +330,8 @@ Base.show(io::IO, rmt::GitRemote) = print(io, "GitRemote:\nRemote name: ", name( 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 or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +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 @@ -350,8 +352,8 @@ 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 or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +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 @@ -372,8 +374,8 @@ 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 or the git repository -located at `path`. Typically git repos use "origin" as the remote name. +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 diff --git a/base/libgit2/repository.jl b/base/libgit2/repository.jl index 84e7c70681665..4f169add16115 100644 --- a/base/libgit2/repository.jl +++ b/base/libgit2/repository.jl @@ -251,7 +251,16 @@ contains detailed information about it based on the keyword argument: * `options::DescribeOptions=DescribeOptions()` -Equivalent to `git describe `. +A git decription of a `commitish` object looks for the tag (by default, annotated, +although a search of all tags can be performed) which can be reached from `commitish` +which is most recent. If the tag is pointing to `commitish`, then only the tag is +included in the description. Otherwise, a suffix is included which contains the +number of commits between `commitish` 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 `. See [`DescribeOptions`](@ref) for more +information. """ function GitDescribeResult(commitish::GitObject; options::DescribeOptions=DescribeOptions()) @@ -265,14 +274,19 @@ end """ LibGit2.GitDescribeResult(repo::GitRepo; kwarg...) -Produce a `GitDescribeResult` of the repository `repo`'s working directory, -which can include all the commits and tags (or, for instance, HEAD only). +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()` -Equivalent to `git describe`. +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()) result_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @@ -307,6 +321,13 @@ function Base.show(io::IO, result::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()) @check ccall((:git_checkout_tree, :libgit2), Cint, @@ -314,6 +335,13 @@ function checkout_tree(repo::GitRepo, obj::GitObject; repo.ptr, obj.ptr, Ref(options)) end +""" + checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitIndex}(); options::CheckoutOptions = CheckoutOptions()) + +Update the working tree of `repo` to match the index `idx`. If `idx` is null, 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::Nullable{GitIndex} = Nullable{GitIndex}(); options::CheckoutOptions = CheckoutOptions()) @check ccall((:git_checkout_index, :libgit2), Cint, @@ -323,6 +351,16 @@ function checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitInd 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()) @check ccall((:git_checkout_head, :libgit2), Cint, (Ptr{Void}, Ptr{CheckoutOptions}), @@ -365,6 +403,19 @@ function reset!(repo::GitRepo, obj::GitObject, mode::Cint; 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) clone_opts_ref = Ref(clone_opts) @@ -375,6 +426,29 @@ function clone(repo_url::AbstractString, repo_path::AbstractString, 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) fhr = Ref{Vector{FetchHead}}(FetchHead[]) ffcb = fetchhead_foreach_cb() diff --git a/base/libgit2/status.jl b/base/libgit2/status.jl index ccfb4c8e19985..568676f8eda82 100644 --- a/base/libgit2/status.jl +++ b/base/libgit2/status.jl @@ -7,7 +7,7 @@ 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. +submodules or not. See [`StatusOptions`](@ref) for more information. """ function GitStatus(repo::GitRepo; status_opts=StatusOptions()) stat_ptr_ptr = Ref{Ptr{Void}}(C_NULL) diff --git a/base/libgit2/tree.jl b/base/libgit2/tree.jl index 60da6a821f0be..3d87a42593efc 100644 --- a/base/libgit2/tree.jl +++ b/base/libgit2/tree.jl @@ -1,11 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license """ -Traverse the entries in a tree and its subtrees in post or pre order. + treewalk(f::Function, tree::GitTree, payload=Any[], post::Bool=false) -Function parameter should have following signature: +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: (Cstring, Ptr{Void}, Ptr{Void}) -> 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::Function, tree::GitTree, payload=Any[], post::Bool = false) cbf = cfunction(f, Cint, Tuple{Cstring, Ptr{Void}, Ptr{Void}}) @@ -19,21 +29,42 @@ 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) str = ccall((:git_tree_entry_name, :libgit2), Cstring, (Ptr{Void},), 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) return ccall((:git_tree_entry_filemode, :libgit2), Cint, (Ptr{Void},), 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) otype = ccall((:git_tree_entry_type, :libgit2), Cint, (Ptr{Void},), 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) oid_ptr = ccall((:git_tree_entry_id, :libgit2), Ptr{UInt8}, (Ptr{Void},), te.ptr) return GitHash(oid_ptr) @@ -53,6 +84,20 @@ function Base.getindex(tree::GitTree, i::Integer) 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) +``` +""" +function GitObject(e::GitTreeEntry) end function (::Type{T})(te::GitTreeEntry) where T<:GitObject repo = repository(te) obj_ptr_ptr = Ref{Ptr{Void}}(C_NULL) diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index 65a6cbc65ff60..e38dacbb3ce6a 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -50,6 +50,12 @@ end An action signature (e.g. for committers, taggers, etc). Matches the [`git_signature`](https://libgit2.github.com/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 @@ -116,19 +122,38 @@ function free(buf_ref::Base.Ref{Buffer}) ccall((:git_buf_free, :libgit2), Void, (Ptr{Buffer},), buf_ref) end -"Abstract credentials payload" -abstract type AbstractCredentials end - -"Checks if credentials were used" -checkused!(p::AbstractCredentials) = true -checkused!(p::Void) = false -"Resets credentials for another use" -reset!(p::AbstractCredentials, cnt::Int=3) = nothing - """ LibGit2.CheckoutOptions Matches the [`git_checkout_options`](https://libgit2.github.com/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 = 1 @@ -161,6 +186,8 @@ Matches the [`git_checkout_options`](https://libgit2.github.com/libgit2/#HEAD/ty perfdata_payload::Ptr{Void} end +abstract type Payload end + """ LibGit2.RemoteCallbacks @@ -183,12 +210,16 @@ Matches the [`git_remote_callbacks`](https://libgit2.github.com/libgit2/#HEAD/ty payload::Ptr{Void} end -function RemoteCallbacks(credentials::Ptr{Void}, payload::Ref{Nullable{AbstractCredentials}}) - RemoteCallbacks(credentials=credentials, payload=pointer_from_objref(payload)) +function RemoteCallbacks(credentials_cb::Ptr{Void}, payload::Ref{<:Payload}) + RemoteCallbacks(credentials=credentials_cb, payload=pointer_from_objref(payload)) +end + +function RemoteCallbacks(credentials_cb::Ptr{Void}, payload::Payload) + RemoteCallbacks(credentials_cb, Ref(payload)) end -function RemoteCallbacks(credentials::Ptr{Void}, payload::Nullable{<:AbstractCredentials}) - RemoteCallbacks(credentials, Ref{Nullable{AbstractCredentials}}(payload)) +function RemoteCallbacks(credentials_cb::Ptr{Void}, credentials) + RemoteCallbacks(credentials_cb, CredentialPayload(credentials)) end """ @@ -239,6 +270,20 @@ end LibGit2.FetchOptions Matches the [`git_fetch_options`](https://libgit2.github.com/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 = 1 @@ -258,6 +303,23 @@ end LibGit2.CloneOptions Matches the [`git_clone_options`](https://libgit2.github.com/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 = 1 @@ -276,6 +338,32 @@ end LibGit2.DiffOptionsStruct Matches the [`git_diff_options`](https://libgit2.github.com/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 @@ -303,6 +391,20 @@ end LibGit2.DescribeOptions Matches the [`git_describe_options`](https://libgit2.github.com/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 = 1 @@ -406,6 +508,44 @@ end LibGit2.MergeOptions Matches the [`git_merge_options`](https://libgit2.github.com/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 = 1 @@ -427,6 +567,20 @@ end LibGit2.BlameOptions Matches the [`git_blame_options`](https://libgit2.github.com/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 = 1 @@ -471,6 +625,15 @@ end LibGit2.CherrypickOptions Matches the [`git_cherrypick_options`](https://libgit2.github.com/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 = 1 @@ -520,6 +683,20 @@ 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 = 1 @@ -539,6 +716,23 @@ end Describes a single instruction/operation to be performed during the rebase. Matches the [`git_rebase_operation`](https://libgit2.github.com/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 @@ -555,6 +749,14 @@ end Options to control how `git_status_foreach_ext()` will issue callbacks. Matches the [`git_status_opt_t`](https://libgit2.github.com/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`. """ @kwdef struct StatusOptions version::Cuint = 1 @@ -572,6 +774,14 @@ end 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 @@ -585,6 +795,15 @@ end 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 @@ -771,8 +990,12 @@ The fields represent: boundary::Char end +""" + with(f::Function, obj) -""" Resource management helper function +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 @@ -784,6 +1007,15 @@ 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 @@ -831,6 +1063,14 @@ end import Base.securezero! +"Abstract credentials payload" +abstract type AbstractCredentials end + +"Checks if credentials were used" +checkused!(p::AbstractCredentials) = true +"Resets credentials for another use" +reset!(p::AbstractCredentials, cnt::Int=3) = nothing + "Credentials that support only `user` and `password` parameters" mutable struct UserPasswordCredentials <: AbstractCredentials user::String @@ -905,18 +1145,40 @@ reset!(p::CachedCredentials) = (foreach(reset!, values(p.cred)); p) "Obtain the cached credentials for the given host+protocol (credid), or return and store the default if not found" get_creds!(collection::CachedCredentials, credid, default) = get!(collection.cred, credid, default) -get_creds!(creds::AbstractCredentials, credid, default) = creds -get_creds!(creds::Void, credid, default) = default -function get_creds!(creds::Ref{Nullable{AbstractCredentials}}, credid, default) - if isnull(creds[]) - creds[] = Nullable{AbstractCredentials}(default) - return default - else - get_creds!(Base.get(creds[]), credid, default) - end -end function securezero!(p::CachedCredentials) foreach(securezero!, values(p.cred)) return p end + +""" + LibGit2.CredentialPayload + +Retains state between multiple calls to the credential callback. A single +`CredentialPayload` instance will be used when authentication fails for a URL but different +instances will be used when the URL has changed. +""" +mutable struct CredentialPayload <: Payload + credential::Nullable{AbstractCredentials} + cache::Nullable{CachedCredentials} + scheme::String + username::String + host::String + path::String + + function CredentialPayload(credential::Nullable{<:AbstractCredentials}, cache::Nullable{CachedCredentials}) + new(credential, cache, "", "", "", "") + end +end + +function CredentialPayload(credential::Nullable{<:AbstractCredentials}) + CredentialPayload(credential, Nullable{CachedCredentials}()) +end + +function CredentialPayload(cache::Nullable{CachedCredentials}) + CredentialPayload(Nullable{AbstractCredentials}(), cache) +end + +function CredentialPayload() + CredentialPayload(Nullable{AbstractCredentials}(), Nullable{CachedCredentials}()) +end diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index 07906deb9ede6..1bbfc978e39c1 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -10,31 +10,63 @@ const URL_REGEX = r""" )? (?[A-Za-z0-9\-\.]+) (?() - (?:\:(?\d+))? # only parse port when not using scp-like syntax + # Only parse port when not using scp-like syntax + (?:\:(?\d+))? + /? | :? ) (? - (?()/|(?<=:)) # scp-like syntax must be preceeded by a colon + # Require path to be preceeded by '/'. Alternatively, ':' when using scp-like syntax. + (?<=(?()/|:)) .* )? $ """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) ccall((:git_libgit2_version, :libgit2), Void, - (Ptr{Cint}, Ptr{Cint}, Ptr{Cint}), major, minor, patch) + (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[] diff --git a/base/libgit2/walker.jl b/base/libgit2/walker.jl index 41b44fcbc14b4..ecac18dbeb7de 100644 --- a/base/libgit2/walker.jl +++ b/base/libgit2/walker.jl @@ -1,5 +1,23 @@ # 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 [`map`](@ref LibGit2.map) +and [`count`](@ref LibGit2.count) (for instance, `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 + count((oid,repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) +end +``` +Here, `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) w_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_revwalk_new, :libgit2), Cint, @@ -27,11 +45,25 @@ 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) @check ccall((:git_revwalk_push_head, :libgit2), Cint, (Ptr{Void},), 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 [`map`](@ref LibGit2.map). +""" function Base.push!(w::GitRevWalker, cid::GitHash) @check ccall((:git_revwalk_push, :libgit2), Cint, (Ptr{Void}, Ptr{GitHash}), w.ptr, Ref(cid)) return w @@ -50,6 +82,27 @@ end repository(w::GitRevWalker) = w.owner +""" + LibGit2.map(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, +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. + * `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, `map` visits each commit using the `GitRevWalker` and finds its `GitHash`. +""" function Base.map(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), range::AbstractString="", @@ -79,6 +132,30 @@ function Base.map(f::Function, walker::GitRevWalker; 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 + count((oid, repo)->(oid == commit_oid1), walker, oid=commit_oid1, by=LibGit2.Consts.SORT_TIME) +end +``` +`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 Base.count(f::Function, walker::GitRevWalker; oid::GitHash=GitHash(), by::Cint = Consts.SORT_NONE, diff --git a/base/linalg/arpack.jl b/base/linalg/arpack.jl index c268920fc3a91..c96f8362fb912 100644 --- a/base/linalg/arpack.jl +++ b/base/linalg/arpack.jl @@ -12,8 +12,7 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio tol::Real, maxiter::Integer, mode::Integer, v0::Vector) lworkl = cmplx ? ncv * (3*ncv + 5) : (sym ? ncv * (ncv + 8) : ncv * (3*ncv + 6) ) TR = cmplx ? T.types[1] : T - TOL = Vector{TR}(1) - TOL[1] = tol + TOL = Ref{TR}(tol) v = Matrix{T}(n, ncv) workd = Vector{T}(3*n) @@ -22,14 +21,14 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio if isempty(v0) resid = Vector{T}(n) - info = zeros(BlasInt, 1) + info = Ref{BlasInt}(0) else resid = deepcopy(v0) - info = ones(BlasInt, 1) + info = Ref{BlasInt}(1) end iparam = zeros(BlasInt, 11) ipntr = zeros(BlasInt, (sym && !cmplx) ? 11 : 14) - ido = zeros(BlasInt, 1) + ido = Ref{BlasInt}(0) iparam[1] = BlasInt(1) # ishifts iparam[3] = BlasInt(maxiter) # maxiter @@ -48,50 +47,50 @@ function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Functio naupd(ido, bmat, n, which, nev, TOL, resid, ncv, v, n, iparam, ipntr, workd, workl, lworkl, info) end - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end x = view(workd, ipntr[1]+zernm1) y = view(workd, ipntr[2]+zernm1) if mode == 1 # corresponds to dsdrv1, dndrv1 or zndrv1 - if ido[1] == 1 + if ido[] == 1 matvecA!(y, x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 3 && bmat == "I" # corresponds to dsdrv2, dndrv2 or zndrv2 - if ido[1] == -1 || ido[1] == 1 + if ido[] == -1 || ido[] == 1 y[:] = solveSI(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 2 # corresponds to dsdrv3, dndrv3 or zndrv3 - if ido[1] == -1 || ido[1] == 1 + if ido[] == -1 || ido[] == 1 matvecA!(y, x) if sym x[:] = y # overwrite as per Remark 5 in dsaupd.f end y[:] = solveSI(y) - elseif ido[1] == 2 + elseif ido[] == 2 y[:] = matvecB(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) end elseif mode == 3 && bmat == "G" # corresponds to dsdrv4, dndrv4 or zndrv4 - if ido[1] == -1 + if ido[] == -1 y[:] = solveSI(matvecB(x)) - elseif ido[1] == 1 + elseif ido[] == 1 y[:] = solveSI(view(workd,ipntr[3]+zernm1)) - elseif ido[1] == 2 + elseif ido[] == 2 y[:] = matvecB(x) - elseif ido[1] == 99 + elseif ido[] == 99 break else throw(ARPACKException("unexpected behavior")) @@ -106,63 +105,63 @@ end function eupd_wrapper(T, n::Integer, sym::Bool, cmplx::Bool, bmat::String, nev::Integer, which::String, ritzvec::Bool, - TOL::Array, resid, ncv::Integer, v, ldv, sigma, iparam, ipntr, + TOL::Ref, resid, ncv::Integer, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork) howmny = "A" select = Vector{BlasInt}(ncv) - info = zeros(BlasInt, 1) + info = Ref{BlasInt}(0) - dmap = x->abs.(x) + dmap = x -> abs.(x) if iparam[7] == 3 # shift-and-invert - dmap = x->abs.(1 ./ (x .- sigma)) + dmap = x -> abs.(1 ./ (x .- sigma)) elseif which == "LR" || which == "LA" || which == "BE" dmap = real elseif which == "SR" || which == "SA" - dmap = x->-real(x) + dmap = x -> -real(x) elseif which == "LI" dmap = imag elseif which == "SI" - dmap = x->-imag(x) + dmap = x -> -imag(x) end if cmplx d = Vector{T}(nev+1) - sigmar = ones(T, 1)*sigma + sigmar = Ref{T}(sigma) workev = Vector{T}(2ncv) neupd(ritzvec, howmny, select, d, v, ldv, sigmar, workev, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, rwork, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end p = sortperm(dmap(d[1:nev]), rev=true) return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid) elseif sym d = Vector{T}(nev) - sigmar = ones(T, 1)*sigma + sigmar = Ref{T}(sigma) seupd(ritzvec, howmny, select, d, v, ldv, sigmar, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end p = sortperm(dmap(d), rev=true) return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid) else - dr = Vector{T}(nev+1) - di = Vector{T}(nev+1) + dr = Vector{T}(nev+1) + di = Vector{T}(nev+1) fill!(dr,NaN) fill!(di,NaN) - sigmar = ones(T, 1)*real(sigma) - sigmai = ones(T, 1)*imag(sigma) + sigmar = Ref{T}(real(sigma)) + sigmai = Ref{T}(imag(sigma)) workev = Vector{T}(3*ncv) neupd(ritzvec, howmny, select, dr, di, v, ldv, sigmar, sigmai, workev, bmat, n, which, nev, TOL, resid, ncv, v, ldv, iparam, ipntr, workd, workl, lworkl, info) - if info[1] != 0 - throw(ARPACKException(info[1])) + if info[] != 0 + throw(ARPACKException(info[])) end evec = complex.(Matrix{T}(n, nev+1), Matrix{T}(n, nev+1)) @@ -203,52 +202,54 @@ for (T, saupd_name, seupd_name, naupd_name, neupd_name) in ((:Float64, :dsaupd_, :dseupd_, :dnaupd_, :dneupd_), (:Float32, :ssaupd_, :sseupd_, :snaupd_, :sneupd_)) @eval begin - function naupd(ido, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + function naupd(ido, bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(naupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), - ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), + ido, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(bmat), sizeof(evtype)) end function neupd(rvec, howmny, select, dr, di, z, ldz, sigmar, sigmai, - workev::Array{$T}, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + workev::Vector{$T}, bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(neupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, - Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), - &rvec, howmny, select, dr, di, z, &ldz, sigmar, sigmai, - workev, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, - sizeof(howmny), sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ref{$T}, Ref{$T}, Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), + rvec, howmny, select, dr, di, z, ldz, + sigmar, sigmai, workev, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) end - function saupd(ido, bmat, n, which, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + function saupd(ido, bmat, n, which, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(saupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong), - ido, bmat, &n, which, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(which)) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong), + ido, bmat, n, which, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(bmat), sizeof(which)) end function seupd(rvec, howmny, select, d, z, ldz, sigma, - bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info) + bmat, n, evtype, nev, TOL::Ref{$T}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, info) ccall(($(string(seupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, - Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong), - &rvec, howmny, select, d, z, &ldz, sigma, - bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ref{BlasInt}, Clong, Clong, Clong), + rvec, howmny, select, d, z, ldz, + sigma, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype)) end end end @@ -257,30 +258,31 @@ for (T, TR, naupd_name, neupd_name) in ((:Complex128, :Float64, :znaupd_, :zneupd_), (:Complex64, :Float32, :cnaupd_, :cneupd_)) @eval begin - function naupd(ido, bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, - rwork::Array{$TR}, info) + function naupd(ido, bmat, n, evtype, nev, TOL::Ref{$TR}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, + rwork::Vector{$TR}, info) ccall(($(string(naupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{BlasInt}), - ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, rwork, info) + (Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$TR}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$TR}, Ref{BlasInt}), + ido, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, rwork, info) end - function neupd(rvec, howmny, select, d, z, ldz, sigma, workev::Array{$T}, - bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv, - iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, - rwork::Array{$TR}, info) + function neupd(rvec, howmny, select, d, z, ldz, sigma, workev::Vector{$T}, + bmat, n, evtype, nev, TOL::Ref{$TR}, resid::Vector{$T}, ncv, v::Matrix{$T}, ldv, + iparam, ipntr, workd::Vector{$T}, workl::Vector{$T}, lworkl, + rwork::Vector{$TR}, info) ccall(($(string(neupd_name)), :libarpack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, - Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$TR}, Ptr{BlasInt}), - &rvec, howmny, select, d, z, &ldz, sigma, workev, - bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv, - iparam, ipntr, workd, workl, &lworkl, rwork, info) + (Ref{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, + Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ref{BlasInt}, Ptr{UInt8}, Ref{BlasInt}, + Ptr{$TR}, Ptr{$T}, Ref{BlasInt}, Ptr{$T}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ref{BlasInt}, Ptr{$TR}, Ref{BlasInt}), + rvec, howmny, select, d, z, ldz, + sigma, workev, bmat, n, evtype, nev, + TOL, resid, ncv, v, ldv, + iparam, ipntr, workd, workl, lworkl, rwork, info) end end end diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index 4c18334f4f172..2806e908fc79e 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -148,15 +148,17 @@ end convert(::Type{Matrix}, A::Bidiagonal{T}) where {T} = convert(Matrix{T}, A) convert(::Type{Array}, A::Bidiagonal) = convert(Matrix, A) full(A::Bidiagonal) = convert(Array, A) -promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)} +promote_rule(::Type{Matrix{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)} #Converting from Bidiagonal to Tridiagonal Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M) function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T - z = zeros(T, size(A)[1]-1) - A.uplo == 'U' ? Tridiagonal(z, convert(Vector{T},A.dv), convert(Vector{T},A.ev)) : Tridiagonal(convert(Vector{T},A.ev), convert(Vector{T},A.dv), z) + 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} = Tridiagonal{promote_type(T,S)} +promote_rule(::Type{<:Tridiagonal{T}}, ::Type{<:Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)} # No-op for trivial conversion Bidiagonal{T} -> Bidiagonal{T} convert(::Type{Bidiagonal{T}}, A::Bidiagonal{T}) where {T} = A @@ -221,7 +223,7 @@ broadcast(::typeof(floor), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiag broadcast(::typeof(ceil), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(ceil.(T, M.dv), ceil.(T, M.ev), M.uplo) transpose(M::Bidiagonal) = Bidiagonal(M.dv, M.ev, M.uplo == 'U' ? :L : :U) -ctranspose(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), M.uplo == 'U' ? :L : :U) +adjoint(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), M.uplo == 'U' ? :L : :U) istriu(M::Bidiagonal) = M.uplo == 'U' || iszero(M.ev) istril(M::Bidiagonal) = M.uplo == 'L' || iszero(M.ev) @@ -456,7 +458,7 @@ end #Linear solvers A_ldiv_B!(A::Union{Bidiagonal, AbstractTriangular}, b::AbstractVector) = naivesub!(A, b) At_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(transpose(A), b) -Ac_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(ctranspose(A), b) +Ac_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(adjoint(A), b) function A_ldiv_B!(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix) nA,mA = size(A) tmp = similar(B,size(B,1)) diff --git a/base/linalg/bitarray.jl b/base/linalg/bitarray.jl index b591b94d0e358..b7dacb300a3f8 100644 --- a/base/linalg/bitarray.jl +++ b/base/linalg/bitarray.jl @@ -86,8 +86,7 @@ function diag(B::BitMatrix) v end -function diagm(v::Union{BitVector,BitMatrix}) - isa(v, BitMatrix) && size(v,1)==1 || size(v,2)==1 || throw(DimensionMismatch()) +function diagm(v::BitVector) n = length(v) a = falses(n, n) for i=1:n @@ -134,7 +133,7 @@ end ## Structure query functions -issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && countnz(A - A.')==0 +issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && count(!iszero, A - A.')==0 ishermitian(A::BitMatrix) = issymmetric(A) function nonzero_chunks(chunks::Vector{UInt64}, pos0::Int, pos1::Int) @@ -295,4 +294,4 @@ function transpose(B::BitMatrix) return Bt end -ctranspose(B::Union{BitMatrix,BitVector}) = transpose(B) +adjoint(B::Union{BitMatrix,BitVector}) = transpose(B) diff --git a/base/linalg/blas.jl b/base/linalg/blas.jl index 7da0bba72cb4b..0ed249b91a9d1 100644 --- a/base/linalg/blas.jl +++ b/base/linalg/blas.jl @@ -2,13 +2,14 @@ module BLAS -import ..axpy! +import ..axpy!, ..axpby! import Base: copy! export # Level 1 asum, axpy!, + axpby!, blascopy!, dot, dotc, @@ -142,11 +143,11 @@ function check() (_, info) = LinAlg.LAPACK.potrf!('U', [1.0 0.0; 0.0 -1.0]) if info != 2 # mangled info code if info == 2^33 - error("""BLAS and LAPACK are compiled with 32-bit integer support, but Julia expects 64-bit integers. Please build Julia with USE_BLAS64=0.""") + error("BLAS and LAPACK are compiled with 32-bit integer support, but Julia expects 64-bit integers. Please build Julia with USE_BLAS64=0.") elseif info == 0 - error("""BLAS and LAPACK are compiled with 64-bit integer support but Julia expects 32-bit integers. Please build Julia with USE_BLAS64=1.""") + error("BLAS and LAPACK are compiled with 64-bit integer support but Julia expects 32-bit integers. Please build Julia with USE_BLAS64=1.") else - error("""The LAPACK library produced an undefined error code. Please verify the installation of BLAS and LAPACK.""") + error("The LAPACK library produced an undefined error code. Please verify the installation of BLAS and LAPACK.") end end @@ -171,8 +172,8 @@ for (fname, elty) in ((:dcopy_,:Float64), # SUBROUTINE DCOPY(N,DX,INCX,DY,INCY) function blascopy!(n::Integer, DX::Union{Ptr{$elty},StridedArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},StridedArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, DX, &incx, DY, &incy) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, DX, incx, DY, incy) DY end end @@ -202,8 +203,8 @@ for (fname, elty) in ((:dscal_,:Float64), # SUBROUTINE DSCAL(N,DA,DX,INCX) function scal!(n::Integer, DA::$elty, DX::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &n, &DA, DX, &incx) + (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + n, DA, DX, incx) DX end end @@ -266,8 +267,8 @@ for (fname, elty) in ((:ddot_,:Float64), # DOUBLE PRECISION DX(*),DY(*) function dot(n::Integer, DX::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer, DY::Union{Ptr{$elty},DenseArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), $elty, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, DX, &incx, DY, &incy) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, DX, incx, DY, incy) end end end @@ -358,8 +359,8 @@ for (fname, elty, ret_type) in ((:dnrm2_,:Float64,:Float64), # SUBROUTINE DNRM2(N,X,INCX) function nrm2(n::Integer, X::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), $ret_type, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, X, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, X, incx) end end end @@ -391,8 +392,8 @@ for (fname, elty, ret_type) in ((:dasum_,:Float64,:Float64), # SUBROUTINE ASUM(N, X, INCX) function asum(n::Integer, X::Union{Ptr{$elty},DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas), $ret_type, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, X, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, X, incx) end end end @@ -434,8 +435,8 @@ for (fname, elty) in ((:daxpy_,:Float64), # DOUBLE PRECISION DX(*),DY(*) function axpy!(n::Integer, alpha::($elty), dx::Union{Ptr{$elty}, DenseArray{$elty}}, incx::Integer, dy::Union{Ptr{$elty}, DenseArray{$elty}}, incy::Integer) ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, &alpha, dx, &incx, dy, &incy) + (Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, alpha, dx, incx, dy, incy) dy end end @@ -463,6 +464,55 @@ function axpy!(alpha::Number, x::Array{T}, rx::Union{UnitRange{Ti},Range{Ti}}, 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> Base.BLAS.axpby!(2., x, 3., y) +3-element Array{Float64,1}: +14.0 +19.0 +24.0 +``` +""" +function axpby! end + +for (fname, elty) in ((:daxpby_,:Float64), (:saxpby_,:Float32), + (:zaxpby_,:Complex128), (:caxpby_,:Complex64)) + @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}, + DenseArray{$elty}}, incx::Integer, beta::($elty), + dy::Union{Ptr{$elty}, DenseArray{$elty}}, incy::Integer) + ccall((@blasfunc($fname), libblas), Void, (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::Union{DenseArray{T},StridedVector{T}}, beta::Number, y::Union{DenseArray{T},StridedVector{T}}) where T<:BlasFloat + if length(x) != length(y) + throw(DimensionMismatch("x has length $(length(x)), but y has length $(length(y))")) + end + axpby!(length(x), convert(T,alpha), pointer(x), stride(x, 1), convert(T,beta), pointer(y), stride(y, 1)) + y +end + ## iamax for (fname, elty) in ((:idamax_,:Float64), (:isamax_,:Float32), @@ -471,8 +521,8 @@ for (fname, elty) in ((:idamax_,:Float64), @eval begin function iamax(n::Integer, dx::Union{Ptr{$elty}, DenseArray{$elty}}, incx::Integer) ccall((@blasfunc($fname), libblas),BlasInt, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &n, dx, &incx) + (Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + n, dx, incx) end end end @@ -503,12 +553,12 @@ for (fname, elty) in ((:dgemv_,:Float64), throw(DimensionMismatch("A.' has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &trans, &size(A,1), &size(A,2), &alpha, - A, &max(1,stride(A,2)), X, &stride(X,1), - &beta, Y, &stride(Y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + trans, size(A,1), size(A,2), alpha, + A, max(1,stride(A,2)), X, stride(X,1), + beta, Y, stride(Y,1)) Y end function gemv(trans::Char, alpha::($elty), A::StridedMatrix{$elty}, X::StridedVector{$elty}) @@ -578,13 +628,13 @@ for (fname, elty) in ((:dgbmv_,:Float64), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function gbmv!(trans::Char, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}), - &trans, &m, &size(A,2), &kl, - &ku, &alpha, A, &max(1,stride(A,2)), - x, &stride(x,1), &beta, y, &stride(y,1)) + (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}), + trans, m, size(A,2), kl, + ku, alpha, A, max(1,stride(A,2)), + x, stride(x,1), beta, y, stride(y,1)) y end function gbmv(trans::Char, m::Integer, kl::Integer, ku::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -634,12 +684,12 @@ for (fname, elty, lib) in ((:dsymv_,:Float64,libblas), throw(DimensionMismatch("A has size $(size(A)), and y has length $(length(y))")) end ccall((@blasfunc($fname), $lib), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &alpha, A, - &max(1,stride(A,2)), x, &stride(x,1), &beta, - y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, n, alpha, A, + max(1,stride(A,2)), x, stride(x,1), beta, + y, stride(y,1)) y end function symv(uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -687,12 +737,12 @@ for (fname, elty) in ((:zhemv_,:Complex128), incx = stride(x, 1) incy = stride(y, 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, A, - &lda, x, &incx, &β, - y, &incy) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, A, + lda, x, incx, β, + y, incy) y end function hemv(uplo::Char, α::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -717,12 +767,12 @@ for (fname, elty) in ((:dsbmv_,:Float64), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function sbmv!(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &size(A,2), &k, &alpha, - A, &max(1,stride(A,2)), x, &stride(x,1), - &beta, y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + uplo, size(A,2), k, alpha, + A, max(1,stride(A,2)), x, stride(x,1), + beta, y, stride(y,1)) y end function sbmv(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -779,12 +829,12 @@ for (fname, elty) in ((:zhbmv_,:Complex128), # DOUBLE PRECISION A(LDA,*),X(*),Y(*) function hbmv!(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}, beta::($elty), y::StridedVector{$elty}) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &size(A,2), &k, &alpha, - A, &max(1,stride(A,2)), x, &stride(x,1), - &beta, y, &stride(y,1)) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + uplo, size(A,2), k, alpha, + A, max(1,stride(A,2)), x, stride(x,1), + beta, y, stride(y,1)) y end function hbmv(uplo::Char, k::Integer, alpha::($elty), A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -837,10 +887,10 @@ for (fname, elty) in ((:dtrmv_,:Float64), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - A, &max(1,stride(A,2)), x, &max(1,stride(x, 1))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, trans, diag, n, + A, max(1,stride(A,2)), x, max(1,stride(x, 1))) x end function trmv(uplo::Char, trans::Char, diag::Char, A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -889,10 +939,10 @@ for (fname, elty) in ((:dtrsv_,:Float64), throw(DimensionMismatch("size of A is $n != length(x) = $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, - A, &max(1,stride(A,2)), x, &stride(x, 1)) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, trans, diag, n, + A, max(1,stride(A,2)), x, stride(x, 1)) x end function trsv(uplo::Char, trans::Char, diag::Char, A::StridedMatrix{$elty}, x::StridedVector{$elty}) @@ -921,12 +971,12 @@ for (fname, elty) in ((:dger_,:Float64), throw(DimensionMismatch("A has size ($m,$n), x has length $(length(x)), y has length $(length(y))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}), - &m, &n, &α, x, - &stride(x, 1), y, &stride(y, 1), A, - &max(1,stride(A,2))) + (Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}), + m, n, α, x, + stride(x, 1), y, stride(y, 1), A, + max(1,stride(A,2))) A end end @@ -953,10 +1003,10 @@ for (fname, elty, lib) in ((:dsyr_,:Float64,libblas), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), $lib), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, x, - &stride(x, 1), A, &max(1,stride(A, 2))) + (Ref{UInt8}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, x, + stride(x, 1), A, max(1,stride(A, 2))) A end end @@ -982,10 +1032,10 @@ for (fname, elty, relty) in ((:zher_,:Complex128, :Float64), throw(DimensionMismatch("A has size ($n,$n), x has length $(length(x))")) end ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, &α, x, - &stride(x, 1), A, &max(1,stride(A,2))) + (Ref{UInt8}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + uplo, n, α, x, + stride(x, 1), A, max(1,stride(A,2))) A end end @@ -1027,14 +1077,14 @@ for (gemm, elty) in throw(DimensionMismatch("A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) end ccall((@blasfunc($gemm), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}), - &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))) + (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}), + 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))) C end function gemm(transA::Char, transB::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1084,12 +1134,12 @@ for (mfname, elty) in ((:dsymm_,:Float64), throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end ccall((@blasfunc($mfname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, - &max(1,stride(B,2)), &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, m, n, + alpha, A, max(1,stride(A,2)), B, + max(1,stride(B,2)), beta, C, max(1,stride(C,2))) C end function symm(side::Char, uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1149,12 +1199,12 @@ for (mfname, elty) in ((:zhemm_,:Complex128), throw(DimensionMismatch("B has second dimension $(size(B,2)) but needs to match second dimension of C, $n")) end ccall((@blasfunc($mfname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, - &max(1,stride(B,2)), &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, m, n, + alpha, A, max(1,stride(A,2)), B, + max(1,stride(B,2)), beta, C, max(1,stride(C,2))) C end function hemm(side::Char, uplo::Char, alpha::($elty), A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) @@ -1207,12 +1257,12 @@ for (fname, elty) in ((:dsyrk_,:Float64), if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), &beta, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), beta, + C, max(1,stride(C,2))) C end end @@ -1263,12 +1313,12 @@ for (fname, elty, relty) in ((:zherk_, :Complex128, :Float64), end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &α, A, &max(1,stride(A,2)), &β, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$relty}, Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + α, A, max(1,stride(A,2)), β, + C, max(1,stride(C,2))) C end function herk(uplo::Char, trans::Char, α::$relty, A::StridedVecOrMat{$elty}) @@ -1302,12 +1352,12 @@ for (fname, elty) in ((:dsyr2k_,:Float64), if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), &beta, - C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, + Ptr{$elty}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), beta, + C, max(1,stride(C,2))) C end end @@ -1339,12 +1389,12 @@ for (fname, elty1, elty2) in ((:zher2k_,:Complex128,:Float64), (:cher2k_,:Comple if nn != n throw(DimensionMismatch("C has size ($n,$n), corresponding dimension of A is $nn")) end k = size(A, trans == 'N' ? 2 : 1) ccall((@blasfunc($fname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty1}, Ptr{$elty1}, Ptr{BlasInt}, Ptr{$elty1}, Ptr{BlasInt}, - Ptr{$elty2}, Ptr{$elty1}, Ptr{BlasInt}), - &uplo, &trans, &n, &k, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), - &beta, C, &max(1,stride(C,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty1}, Ptr{$elty1}, Ref{BlasInt}, Ptr{$elty1}, Ref{BlasInt}, + Ref{$elty2}, Ptr{$elty1}, Ref{BlasInt}), + uplo, trans, n, k, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2)), + beta, C, max(1,stride(C,2))) C end function her2k(uplo::Char, trans::Char, alpha::($elty1), A::StridedVecOrMat{$elty1}, B::StridedVecOrMat{$elty1}) @@ -1424,10 +1474,10 @@ for (mmname, smname, elty) in throw(DimensionMismatch("size of A, $(size(A)), doesn't match $side size of B with dims, $(size(B))")) end ccall((@blasfunc($mmname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &transa, &diag, &m, &n, - &alpha, A, &max(1,stride(A,2)), B, &max(1,stride(B,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, transa, diag, m, n, + alpha, A, max(1,stride(A,2)), B, max(1,stride(B,2))) B end function trmm(side::Char, uplo::Char, transa::Char, diag::Char, @@ -1449,12 +1499,12 @@ for (mmname, smname, elty) in throw(DimensionMismatch("size of A is ($k,$k), size of B is ($m,$n), side is $side, and transa='$transa'")) end ccall((@blasfunc($smname), libblas), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &side, &uplo, &transa, &diag, - &m, &n, &alpha, A, - &max(1,stride(A,2)), B, &max(1,stride(B,2))) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, + Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}), + side, uplo, transa, diag, + m, n, alpha, A, + max(1,stride(A,2)), B, max(1,stride(B,2))) B end function trsm(side::Char, uplo::Char, transa::Char, diag::Char, alpha::$elty, A::StridedMatrix{$elty}, B::StridedMatrix{$elty}) diff --git a/base/linalg/bunchkaufman.jl b/base/linalg/bunchkaufman.jl index 12763756fca5b..3d3865b64fbb6 100644 --- a/base/linalg/bunchkaufman.jl +++ b/base/linalg/bunchkaufman.jl @@ -116,7 +116,7 @@ julia> A = [1 2 3; 2 1 2; 3 2 1] julia> F = bkfact(Symmetric(A, :L)) Base.LinAlg.BunchKaufman{Float64,Array{Float64,2}} D factor: -3×3 Tridiagonal{Float64}: +3×3 Tridiagonal{Float64,Array{Float64,1}}: 1.0 3.0 ⋅ 3.0 1.0 0.0 ⋅ 0.0 -1.0 diff --git a/base/linalg/cholesky.jl b/base/linalg/cholesky.jl index fe3bb861f5a58..f2aebadcbe735 100644 --- a/base/linalg/cholesky.jl +++ b/base/linalg/cholesky.jl @@ -52,6 +52,9 @@ function CholeskyPivoted(A::AbstractMatrix{T}, uplo::Char, piv::Vector{BlasInt}, CholeskyPivoted{T,typeof(A)}(A, uplo, piv, rank, tol, info) end +# make a copy that allow inplace Cholesky factorization +@inline choltype(A) = promote_type(typeof(chol(one(eltype(A)))), Float32) +@inline cholcopy(A) = copy_oftype(A, choltype(A)) # _chol!. Internal methods for calling unpivoted Cholesky ## BLAS/LAPACK element types @@ -63,6 +66,13 @@ function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular}) C, info = LAPACK.potrf!('L', A) return LowerTriangular(C), info end +function _chol!(A::StridedMatrix) + if !ishermitian(A) # return with info = -1 if not Hermitian + return UpperTriangular(A), convert(BlasInt, -1) + else + return _chol!(A, UpperTriangular) + end +end ## Non BLAS/LAPACK element types (generic) function _chol!(A::AbstractMatrix, ::Type{UpperTriangular}) @@ -124,10 +134,6 @@ end chol!(x::Number, uplo) = ((C, info) = _chol!(x, uplo); @assertposdef C info) -non_hermitian_error(f) = throw(ArgumentError("matrix is not symmetric/" * - "Hermitian. This error can be avoided by calling $f(Hermitian(A)) " * - "which will ignore either the upper or lower triangle of the matrix.")) - # chol!. Destructive methods for computing Cholesky factor of real symmetric or Hermitian # matrix function chol!(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) @@ -135,8 +141,7 @@ function chol!(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) @assertposdef C info end function chol!(A::StridedMatrix) - ishermitian(A) || non_hermitian_error("chol!") - C, info = _chol!(A, UpperTriangular) + C, info = _chol!(A) @assertposdef C info end @@ -145,12 +150,11 @@ end # chol. Non-destructive methods for computing Cholesky factor of a real symmetric or # Hermitian matrix. Promotes elements to a type that is stable under square roots. function chol(A::RealHermSymComplexHerm) - T = promote_type(typeof(chol(one(eltype(A)))), Float32) - AA = similar(A, T, size(A)) + AA = similar(A, choltype(A), size(A)) if A.uplo == 'U' copy!(AA, A.data) else - Base.ctranspose!(AA, A.data) + Base.adjoint!(AA, A.data) end chol!(Hermitian(AA, :U)) end @@ -180,10 +184,7 @@ julia> U'U 2.0 50.0 ``` """ -function chol(A::AbstractMatrix) - ishermitian(A) || non_hermitian_error("chol") - return chol(Hermitian(A)) -end +chol(A::AbstractMatrix) = chol!(cholcopy(A)) ## Numbers """ @@ -235,8 +236,11 @@ ERROR: InexactError: convert(Int64, 6.782329983125268) ``` """ function cholfact!(A::StridedMatrix, ::Val{false}=Val(false)) - ishermitian(A) || non_hermitian_error("cholfact!") - return cholfact!(Hermitian(A), Val(false)) + if !ishermitian(A) # return with info = -1 if not Hermitian + return Cholesky(A, 'U', convert(BlasInt, -1)) + else + return cholfact!(Hermitian(A), Val(false)) + end end @@ -250,9 +254,8 @@ end ### Non BLAS/LAPACK element types (generic). Since generic fallback for pivoted Cholesky ### is not implemented yet we throw an error -cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; - tol = 0.0) = - throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet")) +cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Val{true}; tol = 0.0) = + throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet")) ### for StridedMatrices, check that matrix is symmetric/Hermitian """ @@ -264,17 +267,17 @@ factorization produces a number not representable by the element type of `A`, e.g. for integer types. """ function cholfact!(A::StridedMatrix, ::Val{true}; tol = 0.0) - ishermitian(A) || non_hermitian_error("cholfact!") - return cholfact!(Hermitian(A), Val(true); tol = tol) + if !ishermitian(A) # return with info = -1 if not Hermitian + return CholeskyPivoted(A, 'U', Vector{BlasInt}(),convert(BlasInt, 1), + tol, convert(BlasInt, -1)) + else + return cholfact!(Hermitian(A), Val(true); tol = tol) + end end # cholfact. Non-destructive methods for computing Cholesky factorization of real symmetric # or Hermitian matrix ## No pivoting (default) -cholfact(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}, ::Val{false}=Val(false)) = - cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32))) - -### for StridedMatrices, check that matrix is symmetric/Hermitian """ cholfact(A, Val(false)) -> Cholesky @@ -314,18 +317,11 @@ julia> C[:L] * C[:U] == A true ``` """ -function cholfact(A::StridedMatrix, ::Val{false}=Val(false)) - ishermitian(A) || non_hermitian_error("cholfact") - return cholfact(Hermitian(A)) -end +cholfact(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, + ::Val{false}=Val(false)) = cholfact!(cholcopy(A)) ## With pivoting -cholfact(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}, ::Val{true}; tol = 0.0) = - cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)), - Val(true); tol = tol) - -### for StridedMatrices, check that matrix is symmetric/Hermitian """ cholfact(A, Val(true); tol = 0.0) -> CholeskyPivoted @@ -338,10 +334,8 @@ The following functions are available for `PivotedCholesky` objects: The argument `tol` determines the tolerance for determining the rank. For negative values, the tolerance is the machine precision. """ -function cholfact(A::StridedMatrix, ::Val{true}; tol = 0.0) - ishermitian(A) || non_hermitian_error("cholfact") - return cholfact(Hermitian(A), Val(true); tol = tol) -end +cholfact(A::Union{StridedMatrix,RealHermSymComplexHerm{<:Real,<:StridedMatrix}}, + ::Val{true}; tol = 0.0) = cholfact!(cholcopy(A), Val(true); tol = tol) ## Number function cholfact(x::Number, uplo::Symbol=:U) diff --git a/base/linalg/conjarray.jl b/base/linalg/conjarray.jl index 0cd982761157b..c43bc0436b0b1 100644 --- a/base/linalg/conjarray.jl +++ b/base/linalg/conjarray.jl @@ -5,7 +5,7 @@ A lazy-view wrapper of an `AbstractArray`, taking the elementwise complex conjugate. This type is usually constructed (and unwrapped) via the [`conj`](@ref) function (or related -[`ctranspose`](@ref)), but currently this is the default behavior for `RowVector` only. For +[`adjoint`](@ref)), but currently this is the default behavior for `RowVector` only. For other arrays, the `ConjArray` constructor can be used directly. # Examples diff --git a/base/linalg/dense.jl b/base/linalg/dense.jl index bde22d2c2d58a..d716fc08738b3 100644 --- a/base/linalg/dense.jl +++ b/base/linalg/dense.jl @@ -357,9 +357,15 @@ kron(a::AbstractMatrix, b::AbstractVector) = kron(a, reshape(b, length(b), 1)) kron(a::AbstractVector, b::AbstractMatrix) = kron(reshape(a, length(a), 1), b) # Matrix power -(^)(A::AbstractMatrix, p::Integer) = p < 0 ? Base.power_by_squaring(inv(A), -p) : Base.power_by_squaring(A, p) +(^)(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 = Base.promote_op(^, T, typeof(p)) + TT = promote_op(^, T, typeof(p)) return (TT == T ? A : copy!(similar(A, TT), A))^Integer(p) end function schurpow(A::AbstractMatrix, p) @@ -368,8 +374,8 @@ function schurpow(A::AbstractMatrix, p) retmat = A ^ floor(p) # Real part if p - floor(p) == 0.5 - # special case: A^0.5 === sqrtm(A) - retmat = retmat * sqrtm(A) + # special case: A^0.5 === sqrt(A) + retmat = retmat * sqrt(A) else retmat = retmat * powm!(UpperTriangular(float.(A)), real(p - floor(p))) end @@ -379,8 +385,8 @@ function schurpow(A::AbstractMatrix, p) R = S ^ floor(p) # Real part if p - floor(p) == 0.5 - # special case: A^0.5 === sqrtm(A) - R = R * sqrtm(S) + # special case: A^0.5 === sqrt(A) + R = R * sqrt(S) else R = R * powm!(UpperTriangular(float.(S)), real(p - floor(p))) end @@ -399,7 +405,8 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T # Quicker return if A is diagonal if isdiag(A) - retmat = copy(A) + TT = promote_op(^, T, typeof(p)) + retmat = copy_oftype(A, TT) for i in 1:n retmat[i, i] = retmat[i, i] ^ p end @@ -420,12 +427,12 @@ function (^)(A::AbstractMatrix{T}, p::Real) where T # Otherwise, use Schur decomposition return schurpow(A, p) end -(^)(A::AbstractMatrix, p::Number) = expm(p*logm(A)) +(^)(A::AbstractMatrix, p::Number) = exp(p*log(A)) # Matrix exponential """ - expm(A) + exp(A::AbstractMatrix) Compute the matrix exponential of `A`, defined by @@ -445,22 +452,21 @@ julia> A = eye(2, 2) 1.0 0.0 0.0 1.0 -julia> expm(A) +julia> exp(A) 2×2 Array{Float64,2}: 2.71828 0.0 0.0 2.71828 ``` """ -expm(A::StridedMatrix{<:BlasFloat}) = expm!(copy(A)) -expm(A::StridedMatrix{<:Integer}) = expm!(float(A)) -expm(x::Number) = exp(x) +exp(A::StridedMatrix{<:BlasFloat}) = exp!(copy(A)) +exp(A::StridedMatrix{<:Integer}) = exp!(float(A)) ## Destructive matrix exponential using algorithm from Higham, 2008, ## "Functions of Matrices: Theory and Computation", SIAM -function expm!(A::StridedMatrix{T}) where T<:BlasFloat +function exp!(A::StridedMatrix{T}) where T<:BlasFloat n = checksquare(A) if ishermitian(A) - return full(expm(Hermitian(A))) + return full(exp(Hermitian(A))) end ilo, ihi, scale = LAPACK.gebal!('B', A) # modifies A nA = norm(A, 1) @@ -551,7 +557,7 @@ function rcswap!(i::Integer, j::Integer, X::StridedMatrix{<:Number}) end """ - logm(A{T}::StridedMatrix{T}) + log(A{T}::StridedMatrix{T}) 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 @@ -575,25 +581,25 @@ julia> A = 2.7182818 * eye(2) 2.71828 0.0 0.0 2.71828 -julia> logm(A) +julia> log(A) 2×2 Symmetric{Float64,Array{Float64,2}}: 1.0 0.0 0.0 1.0 ``` """ -function logm(A::StridedMatrix{T}) where T +function log(A::StridedMatrix{T}) where T # If possible, use diagonalization if issymmetric(A) && T <: Real - return logm(Symmetric(A)) + return log(Symmetric(A)) end if ishermitian(A) - return logm(Hermitian(A)) + return log(Hermitian(A)) end # Use Schur decomposition n = checksquare(A) if istriu(A) - return full(logm(UpperTriangular(complex(A)))) + return full(log(UpperTriangular(complex(A)))) else if isreal(A) SchurF = schurfact(real(A)) @@ -602,22 +608,17 @@ function logm(A::StridedMatrix{T}) where T end if !istriu(SchurF.T) SchurS = schurfact(complex(SchurF.T)) - logT = SchurS.Z * logm(UpperTriangular(SchurS.T)) * SchurS.Z' + logT = SchurS.Z * log(UpperTriangular(SchurS.T)) * SchurS.Z' return SchurF.Z * logT * SchurF.Z' else - R = logm(UpperTriangular(complex(SchurF.T))) + R = log(UpperTriangular(complex(SchurF.T))) return SchurF.Z * R * SchurF.Z' end end end -function logm(a::Number) - b = log(complex(a)) - return imag(b) == 0 ? real(b) : b -end -logm(a::Complex) = log(a) """ - sqrtm(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 @@ -641,40 +642,38 @@ julia> A = [4 0; 0 4] 4 0 0 4 -julia> sqrtm(A) +julia> sqrt(A) 2×2 Array{Float64,2}: 2.0 0.0 0.0 2.0 ``` """ -function sqrtm(A::StridedMatrix{<:Real}) +function sqrt(A::StridedMatrix{<:Real}) if issymmetric(A) - return full(sqrtm(Symmetric(A))) + return full(sqrt(Symmetric(A))) end n = checksquare(A) if istriu(A) - return full(sqrtm(UpperTriangular(A))) + return full(sqrt(UpperTriangular(A))) else SchurF = schurfact(complex(A)) - R = full(sqrtm(UpperTriangular(SchurF[:T]))) + R = full(sqrt(UpperTriangular(SchurF[:T]))) return SchurF[:vectors] * R * SchurF[:vectors]' end end -function sqrtm(A::StridedMatrix{<:Complex}) +function sqrt(A::StridedMatrix{<:Complex}) if ishermitian(A) - return full(sqrtm(Hermitian(A))) + return full(sqrt(Hermitian(A))) end n = checksquare(A) if istriu(A) - return full(sqrtm(UpperTriangular(A))) + return full(sqrt(UpperTriangular(A))) else SchurF = schurfact(A) - R = full(sqrtm(UpperTriangular(SchurF[:T]))) + R = full(sqrt(UpperTriangular(SchurF[:T]))) return SchurF[:vectors] * R * SchurF[:vectors]' end end -sqrtm(a::Number) = (b = sqrt(complex(a)); imag(b) == 0 ? real(b) : b) -sqrtm(a::Complex) = sqrt(a) function inv(A::StridedMatrix{T}) where T checksquare(A) @@ -892,10 +891,9 @@ function pinv(A::StridedMatrix{T}, tol::Real) where T return SVD.Vt' * (Diagonal(Sinv) * SVD.U') end function pinv(A::StridedMatrix{T}) where T - tol = eps(real(float(one(T))))*maximum(size(A)) + tol = eps(real(float(one(T))))*min(size(A)...) return pinv(A, tol) end -pinv(a::StridedVector) = pinv(reshape(a, length(a), 1)) function pinv(x::Number) xi = inv(x) return ifelse(isfinite(xi), xi, zero(xi)) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 439e409958c52..dfa8408594780 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -186,11 +186,11 @@ function A_mul_B!(D::Diagonal, B::UnitUpperTriangular) UpperTriangular(B.data) end -Ac_mul_B(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* B.diag) -Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(ctranspose(A), D) +Ac_mul_B(D::Diagonal, B::Diagonal) = Diagonal(adjoint.(D.diag) .* B.diag) +Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(adjoint(A), D) function Ac_mul_B(A::AbstractMatrix, D::Diagonal) Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) A_mul_B!(Ac, D) end @@ -202,12 +202,12 @@ function At_mul_B(A::AbstractMatrix, D::Diagonal) A_mul_B!(At, D) end -A_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* ctranspose.(B.diag)) -A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, ctranspose(B)) +A_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(D.diag .* adjoint.(B.diag)) +A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, adjoint(B)) A_mul_Bc(D::Diagonal, Q::Union{Base.LinAlg.QRCompactWYQ,Base.LinAlg.QRPackedQ}) = A_mul_Bc!(Array(D), Q) function A_mul_Bc(D::Diagonal, A::AbstractMatrix) Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) A_mul_B!(D, Ac) end @@ -219,7 +219,7 @@ function A_mul_Bt(D::Diagonal, A::AbstractMatrix) A_mul_B!(D, At) end -Ac_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(ctranspose.(D.diag) .* ctranspose.(B.diag)) +Ac_mul_Bc(D::Diagonal, B::Diagonal) = Diagonal(adjoint.(D.diag) .* adjoint.(B.diag)) At_mul_Bt(D::Diagonal, B::Diagonal) = Diagonal(transpose.(D.diag) .* transpose.(B.diag)) A_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(A_mul_B!, Tuple{Diagonal,Diagonal})) @@ -235,11 +235,11 @@ A_mul_Bc!(A::AbstractMatrix,B::Diagonal) = scale!(A,conj(B.diag)) # Get ambiguous method if try to unify AbstractVector/AbstractMatrix here using AbstractVecOrMat A_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= A.diag .* in -Ac_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= ctranspose.(A.diag) .* in +Ac_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= adjoint.(A.diag) .* in At_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= transpose.(A.diag) .* in A_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= A.diag .* in -Ac_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= ctranspose.(A.diag) .* in +Ac_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= adjoint.(A.diag) .* in At_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= transpose.(A.diag) .* in # ambiguities with Symmetric/Hermitian @@ -306,13 +306,13 @@ A_rdiv_Bt!(A::AbstractMatrix{T}, D::Diagonal{T}) where {T} = A_rdiv_B!(A, D) # Methods to resolve ambiguities with `Diagonal` @inline *(rowvec::RowVector, D::Diagonal) = transpose(D * transpose(rowvec)) @inline A_mul_Bt(D::Diagonal, rowvec::RowVector) = D*transpose(rowvec) -@inline A_mul_Bc(D::Diagonal, rowvec::RowVector) = D*ctranspose(rowvec) +@inline A_mul_Bc(D::Diagonal, rowvec::RowVector) = D*adjoint(rowvec) conj(D::Diagonal) = Diagonal(conj(D.diag)) transpose(D::Diagonal{<:Number}) = D transpose(D::Diagonal) = Diagonal(transpose.(D.diag)) -ctranspose(D::Diagonal{<:Number}) = conj(D) -ctranspose(D::Diagonal) = Diagonal(ctranspose.(D.diag)) +adjoint(D::Diagonal{<:Number}) = conj(D) +adjoint(D::Diagonal) = Diagonal(adjoint.(D.diag)) diag(D::Diagonal) = D.diag trace(D::Diagonal) = sum(D.diag) @@ -326,12 +326,9 @@ end eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n)) # Matrix functions -expm(D::Diagonal) = Diagonal(exp.(D.diag)) -expm(D::Diagonal{<:AbstractMatrix}) = Diagonal(expm.(D.diag)) -logm(D::Diagonal) = Diagonal(log.(D.diag)) -logm(D::Diagonal{<:AbstractMatrix}) = Diagonal(logm.(D.diag)) -sqrtm(D::Diagonal) = Diagonal(sqrt.(D.diag)) -sqrtm(D::Diagonal{<:AbstractMatrix}) = Diagonal(sqrtm.(D.diag)) +exp(D::Diagonal) = Diagonal(exp.(D.diag)) +log(D::Diagonal) = Diagonal(log.(D.diag)) +sqrt(D::Diagonal) = Diagonal(sqrt.(D.diag)) #Linear solver function A_ldiv_B!(D::Diagonal, B::StridedVecOrMat) diff --git a/base/linalg/exceptions.jl b/base/linalg/exceptions.jl index 4b0a72e251950..02d708642a0fd 100644 --- a/base/linalg/exceptions.jl +++ b/base/linalg/exceptions.jl @@ -11,18 +11,22 @@ struct LAPACKException <: Exception end struct ARPACKException <: Exception - info::String + info::BlasInt end -function ARPACKException(i::Integer) - if i == -8 - return ARPACKException("error return from calculation of a real Schur form.") - elseif i == -9 - return ARPACKException("error return from calculation of eigenvectors.") - elseif i == -14 - return ARPACKException("did not find any eigenvalues to sufficient accuracy. Try with a different starting vector or more Lanczos vectors by increasing the value of ncv.") +function Base.showerror(io::IO, ex::ARPACKException) + print(io, "ARPACKException: ") + if ex.info == -8 + print(io, "error return from calculation of a real Schur form.") + elseif ex.info == -9 + print(io, "error return from calculation of eigenvectors.") + elseif ex.info == -14 + print(io, string("did not find any eigenvalues to sufficient accuracy. ", + "Try with a different starting vector or more Lanczos vectors ", + "by increasing the value of ncv.")) + else + print(io, "unspecified ARPACK error: $(ex.info)") end - return ARPACKException("unspecified ARPACK error: $i") end struct SingularException <: Exception @@ -32,6 +36,15 @@ end 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 diff --git a/base/linalg/factorization.jl b/base/linalg/factorization.jl index e3b10d258857c..581f711657398 100644 --- a/base/linalg/factorization.jl +++ b/base/linalg/factorization.jl @@ -6,7 +6,7 @@ abstract type Factorization{T} end eltype(::Type{Factorization{T}}) where {T} = T transpose(F::Factorization) = error("transpose not implemented for $(typeof(F))") -ctranspose(F::Factorization) = error("ctranspose not implemented for $(typeof(F))") +adjoint(F::Factorization) = error("adjoint not implemented for $(typeof(F))") macro assertposdef(A, info) :($(esc(info)) == 0 ? $(esc(A)) : throw(PosDefException($(esc(info))))) diff --git a/base/linalg/generic.jl b/base/linalg/generic.jl index 8c69ebe1548b6..98b5b4c889fd3 100644 --- a/base/linalg/generic.jl +++ b/base/linalg/generic.jl @@ -705,13 +705,13 @@ dot(x::AbstractVector{<:Number}, y::AbstractVector{<:Number}) = vecdot(x, y) ########################################################################################### """ - rank(M[, tol::Real]) + rank(A[, tol::Real]) Compute the rank of a matrix by counting how many singular -values of `M` have magnitude greater than `tol`. -By default, the value of `tol` is the largest -dimension of `M` multiplied by the [`eps`](@ref) -of the [`eltype`](@ref) of `M`. +values of `A` have magnitude greater than `tol*σ₁` where `σ₁` is +`A`'s largest singular values. By default, the value of `tol` is the smallest +dimension of `A` multiplied by the [`eps`](@ref) +of the [`eltype`](@ref) of `A`. # Examples ```jldoctest @@ -728,14 +728,11 @@ julia> rank(diagm([1, 0.001, 2]), 0.00001) 3 ``` """ -rank(A::AbstractMatrix, tol::Real) = mapreduce(x -> x > tol, +, 0, svdvals(A)) -function rank(A::AbstractMatrix) - m,n = size(A) - (m == 0 || n == 0) && return 0 - sv = svdvals(A) - return sum(sv .> maximum(size(A))*eps(sv[1])) +function rank(A::AbstractMatrix, tol::Real = min(size(A)...)*eps(real(float(one(eltype(A)))))) + s = svdvals(A) + sum(x -> x > tol*s[1], s) end -rank(x::Number) = x==0 ? 0 : 1 +rank(x::Number) = x == 0 ? 0 : 1 """ trace(M) @@ -794,6 +791,19 @@ function inv(A::AbstractMatrix{T}) where T A_ldiv_B!(factorize(convert(AbstractMatrix{S}, A)), eye(S0, checksquare(A))) end +function pinv(v::AbstractVector{T}, tol::Real=real(zero(T))) where T + res = similar(v, typeof(zero(T) / (abs2(one(T)) + abs2(one(T)))))' + 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 .= v' ./ den + end + return res +end + """ \\(A, B) @@ -841,10 +851,11 @@ function (\)(A::AbstractMatrix, B::AbstractVecOrMat) return qrfact(A,Val(true)) \ B end -(\)(a::AbstractVector, b::AbstractArray) = reshape(a, length(a), 1) \ b +(\)(a::AbstractVector, b::AbstractArray) = pinv(a) * b (/)(A::AbstractVecOrMat, B::AbstractVecOrMat) = (B' \ A')' # \(A::StridedMatrix,x::Number) = inv(A)*x Should be added at some point when the old elementwise version has been deprecated long enough # /(x::Number,A::StridedMatrix) = x*inv(A) +/(x::Number, v::AbstractVector) = x*pinv(v) cond(x::Number) = x == 0 ? Inf : 1.0 cond(x::Number, p) = cond(x) @@ -942,7 +953,7 @@ function ishermitian(A::AbstractMatrix) return false end for i = indsn, j = i:last(indsn) - if A[i,j] != ctranspose(A[j,i]) + if A[i,j] != adjoint(A[j,i]) return false end end @@ -1155,6 +1166,16 @@ function axpy!(α, x::AbstractArray, rx::AbstractArray{<:Integer}, y::AbstractAr y end +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 + for (IX, IY) in zip(eachindex(x), eachindex(y)) + @inbounds y[IY] = x[IX]*α + y[IY]*β + end + y +end + # Elementary reflection similar to LAPACK. The reflector is not Hermitian but # ensures that tridiagonalization of Hermitian matrices become real. See lawn72 diff --git a/base/linalg/givens.jl b/base/linalg/givens.jl index d822499d94c43..de621983494d4 100644 --- a/base/linalg/givens.jl +++ b/base/linalg/givens.jl @@ -41,8 +41,8 @@ convert(::Type{Rotation{T}}, R::Rotation) where {T} = Rotation{T}([convert(Given convert(::Type{AbstractRotation{T}}, G::Givens) where {T} = convert(Givens{T}, G) convert(::Type{AbstractRotation{T}}, R::Rotation) where {T} = convert(Rotation{T}, R) -ctranspose(G::Givens) = Givens(G.i1, G.i2, conj(G.c), -G.s) -ctranspose(R::Rotation{T}) where {T} = Rotation{T}(reverse!([ctranspose(r) for r in R.rotations])) +adjoint(G::Givens) = Givens(G.i1, G.i2, conj(G.c), -G.s) +adjoint(R::Rotation{T}) where {T} = Rotation{T}(reverse!([adjoint(r) for r in R.rotations])) realmin2(::Type{Float32}) = reinterpret(Float32, 0x26000000) realmin2(::Type{Float64}) = reinterpret(Float64, 0x21a0000000000000) diff --git a/base/linalg/lapack.jl b/base/linalg/lapack.jl index 3a70e5a865d8a..c0ed8ca177137 100644 --- a/base/linalg/lapack.jl +++ b/base/linalg/lapack.jl @@ -92,14 +92,14 @@ function chkfinite(A::StridedMatrix) end # LAPACK version number -function laver() +function version() major = Ref{BlasInt}(0) minor = Ref{BlasInt}(0) patch = Ref{BlasInt}(0) ccall((@blasfunc(ilaver_), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - major, minor, patch) - return major[], minor[], patch[] + (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + major, minor, patch) + return VersionNumber(major[], minor[], patch[]) end # (GB) general banded matrices, LU decomposition and solver @@ -122,9 +122,9 @@ for (gbtrf, gbtrs, elty) in ipiv = similar(AB, BlasInt, mnmn) info = Ref{BlasInt}() ccall((@blasfunc($gbtrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &kl, &ku, AB, &max(1,stride(AB,2)), ipiv, info) + (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 @@ -147,11 +147,11 @@ for (gbtrf, gbtrs, elty) in throw(DimensionMismatch("matrix AB has dimensions $(size(AB)), but right hand side matrix B has dimensions $(size(B))")) end ccall((@blasfunc($gbtrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &trans, &n, &kl, &ku, &size(B,2), AB, &max(1,stride(AB,2)), ipiv, - B, &max(1,stride(B,2)), info) + trans, n, kl, ku, size(B,2), AB, max(1,stride(AB,2)), ipiv, + B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -201,9 +201,9 @@ for (gebal, gebak, elty, relty) in scale = similar(A, $relty, n) info = Ref{BlasInt}() ccall((@blasfunc($gebal), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &job, &n, A, &max(1,stride(A,2)), ilo, ihi, scale, info) + job, n, A, max(1,stride(A,2)), ilo, ihi, scale, info) chklapackerror(info[]) ilo[], ihi[], scale end @@ -223,9 +223,9 @@ for (gebal, gebak, elty, relty) in n = checksquare(V) info = Ref{BlasInt}() ccall((@blasfunc($gebak), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &job, &side, &size(V,1), &ilo, &ihi, scale, &n, V, &max(1,stride(V,2)), info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$relty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + job, side, size(V,1), ilo, ihi, scale, n, V, max(1,stride(V,2)), info) chklapackerror(info[]) V end @@ -287,12 +287,12 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gebrd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + m, n, A, max(1,stride(A,2)), d, e, tauq, taup, - work, &lwork, info) + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -320,9 +320,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelqf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, tau, work, &lwork, info) + (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])) @@ -350,9 +350,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqlf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, tau, work, &lwork, info) + (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])) @@ -391,20 +391,20 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geqp3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (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, + m, n, A, lda, + jpvt, tau, work, lwork, rwork, info) else ccall((@blasfunc($geqp3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, + m, n, A, lda, jpvt, tau, work, - &lwork, info) + lwork, info) end chklapackerror(info[]) if i == 1 @@ -428,11 +428,11 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, + (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, + m, n, nb, A, + lda, T, max(1,stride(T,2)), work, info) chklapackerror(info[]) end @@ -453,10 +453,10 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty if n > 0 info = Ref{BlasInt}() ccall((@blasfunc($geqrt3), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1, stride(A, 2)), - T, &max(1,stride(T,2)), info) + (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 @@ -479,9 +479,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geqrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (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 = BlasInt(real(work[1])) @@ -507,9 +507,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gerqf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (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 = BlasInt(real(work[1])) @@ -532,9 +532,9 @@ for (gebrd, gelqf, geqlf, geqrf, geqp3, geqrt, geqrt3, gerqf, getrf, elty, relty ipiv = similar(A, BlasInt, min(m,n)) info = Ref{BlasInt}() ccall((@blasfunc($getrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, ipiv, info) + (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 @@ -763,10 +763,10 @@ for (tzrzf, ormrz, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($tzrzf), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, A, &lda, - tau, work, &lwork, info) + (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])) @@ -800,14 +800,14 @@ for (tzrzf, ormrz, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrz), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, - &k, &l, A, &lda, - tau, C, &ldc, work, - &lwork, info) + (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}), + side, trans, m, n, + k, l, A, lda, + tau, C, ldc, work, + lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -864,11 +864,11 @@ for (gels, gesv, getrs, getri, elty) in lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gels), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &(btrn ? 'T' : 'N'), &m, &n, &size(B,2), A, &max(1,stride(A,2)), - B, &max(1,stride(B,2)), work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + (btrn ? 'T' : 'N'), m, n, size(B,2), A, max(1,stride(A,2)), + B, max(1,stride(B,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -904,9 +904,9 @@ for (gels, gesv, getrs, getri, elty) in ipiv = similar(A, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gesv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (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 @@ -928,9 +928,9 @@ for (gels, gesv, getrs, getri, elty) in nrhs = size(B, 2) info = Ref{BlasInt}() ccall((@blasfunc($getrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &trans, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + trans, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -953,9 +953,9 @@ for (gels, gesv, getrs, getri, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($getri), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, A, &lda, ipiv, work, &lwork, info) + (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])) @@ -1040,7 +1040,7 @@ for (gesvx, elty) in ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() ferr = similar(A, $elty, nrhs) berr = similar(A, $elty, nrhs) work = Vector{$elty}(4n) @@ -1048,13 +1048,13 @@ for (gesvx, elty) in info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (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}), - &fact, &trans, &n, &nrhs, A, &lda, AF, &ldaf, ipiv, &equed, R, C, B, - &ldb, X, &n, rcond, ferr, berr, work, iwork, info) + fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, + ldb, X, n, rcond, ferr, berr, work, iwork, info) chklapackerror(info[]) if info[] == n + 1 warn("matrix is singular to working precision") @@ -1062,7 +1062,7 @@ for (gesvx, elty) in chknonsingular(info[]) end #WORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[1], ferr, berr, work[1] + X, equed, R, C, B, rcond[], ferr, berr, work[1] end function gesvx!(A::StridedMatrix{$elty}, B::StridedVecOrMat{$elty}) @@ -1109,7 +1109,7 @@ for (gesvx, elty, relty) in ldaf = stride(AF,2) nrhs = size(B,2) ldb = stride(B,2) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}() ferr = similar(A, $relty, nrhs) berr = similar(A, $relty, nrhs) work = Vector{$elty}(2n) @@ -1117,13 +1117,13 @@ for (gesvx, elty, relty) in info = Ref{BlasInt}() X = similar(A, $elty, n, nrhs) ccall((@blasfunc($gesvx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{UInt8}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$relty}, + (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}), - &fact, &trans, &n, &nrhs, A, &lda, AF, &ldaf, ipiv, &equed, R, C, B, - &ldb, X, &n, rcond, ferr, berr, work, rwork, info) + fact, trans, n, nrhs, A, lda, AF, ldaf, ipiv, equed, R, C, B, + ldb, X, n, rcond, ferr, berr, work, rwork, info) chklapackerror(info[]) if info[] == n + 1 warn("matrix is singular to working precision") @@ -1131,7 +1131,7 @@ for (gesvx, elty, relty) in chknonsingular(info[]) end #RWORK(1) contains the reciprocal pivot growth factor norm(A)/norm(U) - X, equed, R, C, B, rcond[1], ferr, berr, rwork[1] + X, equed, R, C, B, rcond[], ferr, berr, rwork[1] end #Wrapper for the no-equilibration, no-transpose calculation @@ -1205,20 +1205,21 @@ for (gelsd, gelsy, elty) in end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $elty, min(m, n)) - rcond = convert($elty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) iwork = Vector{BlasInt}(1) for i = 1:2 # first call returns lwork as work[1] and iwork length as iwork[1] ccall((@blasfunc($gelsd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &size(B,2), A, &max(1,stride(A,2)), - newB, &max(1,stride(B,2),n), s, &rcond, rnk, work, &lwork, iwork, info) + (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])) @@ -1226,7 +1227,7 @@ for (gelsd, gelsy, elty) in resize!(iwork, iwork[1]) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end # SUBROUTINE DGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, @@ -1250,20 +1251,19 @@ for (gelsd, gelsy, elty) in lda = max(1, m) ldb = max(1, m, n) jpvt = zeros(BlasInt, n) - rcond = convert($elty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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, - &rcond, rnk, work, &lwork, + m, n, nrhs, A, + lda, newB, ldb, jpvt, + $elty(rcond), rnk, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -1271,7 +1271,7 @@ for (gelsd, gelsy, elty) in resize!(work, lwork) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end end end @@ -1298,8 +1298,7 @@ for (gelsd, gelsy, elty, relty) in end newB = [B; zeros($elty, max(0, n - size(B, 1)), size(B, 2))] s = similar(A, $relty, min(m, n)) - rcond = convert($relty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}() info = Ref{BlasInt}() work = Vector{$elty}(1) lwork = BlasInt(-1) @@ -1307,12 +1306,14 @@ for (gelsd, gelsy, elty, relty) in iwork = Vector{BlasInt}(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), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &size(B,2), A, &max(1,stride(A,2)), - newB, &max(1,stride(B,2),n), s, &rcond, rnk, work, &lwork, rwork, iwork, info) + (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])) @@ -1321,7 +1322,7 @@ for (gelsd, gelsy, elty, relty) in resize!(iwork, iwork[1]) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end # SUBROUTINE ZGELSY( M, N, NRHS, A, LDA, B, LDB, JPVT, RCOND, RANK, @@ -1345,21 +1346,20 @@ for (gelsd, gelsy, elty, relty) in lda = max(1, m) ldb = max(1, m, n) jpvt = zeros(BlasInt, n) - rcond = convert($relty, rcond) - rnk = Vector{BlasInt}(1) + rnk = Ref{BlasInt}(1) work = Vector{$elty}(1) lwork = BlasInt(-1) rwork = Vector{$relty}(2n) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gelsy), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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, - &rcond, rnk, work, &lwork, + m, n, nrhs, A, + lda, newB, ldb, jpvt, + $relty(rcond), rnk, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -1367,7 +1367,7 @@ for (gelsd, gelsy, elty, relty) in resize!(work, lwork) end end - subsetrows(B, newB, n), rnk[1] + subsetrows(B, newB, n), rnk[] end end end @@ -1427,12 +1427,12 @@ for (gglse, elty) in ((:dgglse_, :Float64), lwork = BlasInt(-1) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gglse), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (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) + 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])) @@ -1490,20 +1490,20 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($geev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &jobvl, &jobvr, &n, A, &max(1,stride(A,2)), W, VL, &n, VR, &n, - work, &lwork, rwork, info) + jobvl, jobvr, n, A, max(1,stride(A,2)), W, VL, n, VR, n, + work, lwork, rwork, info) else ccall((@blasfunc($geev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, &max(1,stride(A,2)), WR, WI, VL, &n, - VR, &n, work, &lwork, info) + (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}), + jobvl, jobvr, n, A, max(1,stride(A,2)), WR, WI, VL, n, + VR, n, work, lwork, info) end chklapackerror(info[]) if i == 1 @@ -1546,28 +1546,28 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in S = similar(A, $relty, minmn) cmplx = eltype(A)<:Complex if cmplx - rwork = Array{$relty}(job == 'N' ? 7*minmn : - minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) + rwork = Vector{$relty}(job == 'N' ? 7*minmn : + minmn*max(5*minmn+7, 2*max(m,n)+2*minmn+1)) end iwork = Vector{BlasInt}(8*minmn) info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesdd), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &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) + 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) else ccall((@blasfunc($gesdd), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &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) + 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) end chklapackerror(info[]) if i == 1 @@ -1622,20 +1622,20 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in for i in 1:2 # first call returns lwork as work[1] if cmplx ccall((@blasfunc($gesvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &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) + (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}), + 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) else ccall((@blasfunc($gesvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &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) + (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}), + 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) end chklapackerror(info[]) if i == 1 @@ -1701,31 +1701,31 @@ for (geev, gesvd, gesdd, ggsvd, elty, relty) in info = Ref{BlasInt}() if cmplx ccall((@blasfunc($ggsvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, work, rwork, iwork, info) else ccall((@blasfunc($ggsvd), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, work, iwork, info) end chklapackerror(info[]) @@ -1820,18 +1820,18 @@ for (f, elty) in ((:dggsvd3_, :Float64), info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, - work, &lwork, iwork, info) + (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}), + jobu, jobv, jobq, m, + n, p, k, l, + A, lda, B, ldb, + alpha, beta, U, ldu, + V, ldv, Q, ldq, + work, lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -1877,19 +1877,19 @@ for (f, elty, relty) in ((:zggsvd3_, :Complex128, :Float64), info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($f), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, + (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}), - &jobu, &jobv, &jobq, &m, - &n, &p, k, l, - A, &lda, B, &ldb, - alpha, beta, U, &ldu, - V, &ldv, Q, &ldq, - work, &lwork, rwork, iwork, + 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) chklapackerror(info[]) if i == 1 @@ -1985,18 +1985,18 @@ for (geevx, ggev, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + (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}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &balanc, &jobvl, &jobvr, &sense, - &n, A, &lda, wr, - wi, VL, &max(1,ldvl), VR, - &max(1,ldvr), ilo, ihi, scale, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), + 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) + lwork, iwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -2050,15 +2050,15 @@ for (geevx, ggev, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, - &lda, B, &ldb, alphar, - alphai, beta, vl, &ldvl, - vr, &ldvr, work, &lwork, + jobvl, jobvr, n, A, + lda, B, ldb, alphar, + alphai, beta, vl, ldvl, + vr, ldvr, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2130,17 +2130,17 @@ for (geevx, ggev, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($geevx), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}, Ptr{BlasInt}, + Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &balanc, &jobvl, &jobvr, &sense, - &n, A, &lda, w, - VL, &max(1,ldvl), VR, &max(1,ldvr), + balanc, jobvl, jobvr, sense, + n, A, lda, w, + VL, max(1,ldvl), VR, max(1,ldvr), ilo, ihi, scale, abnrm, - rconde, rcondv, work, &lwork, + rconde, rcondv, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -2196,15 +2196,15 @@ for (geevx, ggev, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ggev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, + (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}, Ptr{BlasInt}), - &jobvl, &jobvr, &n, A, - &lda, B, &ldb, alpha, - beta, vl, &ldvl, vr, - &ldvr, work, &lwork, rwork, + jobvl, jobvr, n, A, + lda, B, ldb, alpha, + beta, vl, ldvl, vr, + ldvr, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 @@ -2269,11 +2269,11 @@ for (laic1, elty) in s = Vector{$elty}(1) c = Vector{$elty}(1) ccall((@blasfunc($laic1), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (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, + job, j, x, sest, + w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end @@ -2302,11 +2302,11 @@ for (laic1, elty, relty) in s = Vector{$elty}(1) c = Vector{$elty}(1) ccall((@blasfunc($laic1), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$relty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$relty}, Ptr{$elty}, + (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, + job, j, x, sest, + w, gamma, sestpr, s, c) sestpr[1], s[1], c[1] end @@ -2343,9 +2343,9 @@ for (gtsv, gttrf, gttrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($gtsv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), dl, d, du, B, &max(1,stride(B,2)), info) + (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 @@ -2369,9 +2369,9 @@ for (gtsv, gttrf, gttrs, elty) in ipiv = similar(d, BlasInt, n) info = Ref{BlasInt}() ccall((@blasfunc($gttrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, dl, d, du, du2, ipiv, info) + n, dl, d, du, du2, ipiv, info) chklapackerror(info[]) dl, d, du, du2, ipiv end @@ -2400,10 +2400,10 @@ for (gtsv, gttrf, gttrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($gttrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &trans, &n, &size(B,2), dl, d, du, du2, ipiv, B, &max(1,stride(B,2)), info) + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + trans, n, size(B,2), dl, d, du, du2, ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -2466,9 +2466,9 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orglq), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, &max(1,stride(A,2)), tau, work, &lwork, info) + (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])) @@ -2499,10 +2499,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgqr), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (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 @@ -2534,10 +2534,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgql), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (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 @@ -2569,10 +2569,10 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orgrq), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &m, &n, &k, A, - &max(1,stride(A,2)), tau, work, &lwork, + (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 @@ -2619,11 +2619,11 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormlq), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, &k, A, &max(1,stride(A,2)), tau, - C, &max(1,stride(C,2)), work, &lwork, info) + (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}), + side, trans, m, n, k, A, max(1,stride(A,2)), tau, + C, max(1,stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -2665,13 +2665,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormqr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &side, &trans, &m, &n, - &k, A, &max(1,stride(A,2)), tau, - C, &max(1, stride(C,2)), work, &lwork, + side, trans, m, n, + k, A, max(1,stride(A,2)), tau, + C, max(1, stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2714,13 +2714,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormql), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &side, &trans, &m, &n, - &k, A, &max(1,stride(A,2)), tau, - C, &max(1, stride(C,2)), work, &lwork, + side, trans, m, n, + k, A, max(1,stride(A,2)), tau, + C, max(1, stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 @@ -2763,11 +2763,11 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormrq), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &m, &n, &k, A, &max(1,stride(A,2)), tau, - C, &max(1,stride(C,2)), work, &lwork, info) + (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}), + side, trans, m, n, k, A, max(1,stride(A,2)), tau, + C, max(1,stride(C,2)), work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -2818,13 +2818,13 @@ for (orglq, orgqr, orgql, orgrq, ormlq, ormqr, ormql, ormrq, gemqrt, elty) in work = Vector{$elty}(wss) info = Ref{BlasInt}() ccall((@blasfunc($gemqrt), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &side, &trans, &m, &n, - &k, &nb, V, &ldv, - T, &max(1,stride(T,2)), C, &max(1,ldc), + side, trans, m, n, + k, nb, V, ldv, + T, max(1,stride(T,2)), C, max(1,ldc), work, info) chklapackerror(info[]) return C @@ -2936,9 +2936,9 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in end info = Ref{BlasInt}() ccall((@blasfunc($posv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), B, max(1,stride(B,2)), info) chkargsok(info[]) chkposdef(info[]) A, B @@ -2960,8 +2960,8 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in end info = Ref{BlasInt}() ccall((@blasfunc($potrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &size(A,1), A, &lda, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, size(A,1), A, lda, info) chkargsok(info[]) #info[] > 0 means the leading minor of order info[] is not positive definite #ordinarily, throw Exception here, but return error code here @@ -2980,8 +2980,8 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in chkuplo(uplo) info = Ref{BlasInt}() ccall((@blasfunc($potri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &size(A,1), A, &max(1,stride(A,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, size(A,1), A, max(1,stride(A,2)), info) chkargsok(info[]) chknonsingular(info[]) A @@ -3008,10 +3008,10 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in ldb = max(1,stride(B,2)) info = Ref{BlasInt}() ccall((@blasfunc($potrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &nrhs, A, - &lda, B, &ldb, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, nrhs, A, + lda, B, ldb, info) chklapackerror(info[]) return B end @@ -3033,9 +3033,9 @@ for (posv, potrf, potri, potrs, pstrf, elty, rtyp) in work = Vector{$rtyp}(2n) info = Ref{BlasInt}() ccall((@blasfunc($pstrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), piv, rank, &tol, work, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{BlasInt}, Ref{$rtyp}, Ptr{$rtyp}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), piv, rank, tol, work, info) chkargsok(info[]) A, piv, rank[1], info[] #Stored in PivotedCholesky end @@ -3121,9 +3121,9 @@ for (ptsv, pttrf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($ptsv), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (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 @@ -3141,8 +3141,8 @@ for (ptsv, pttrf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrf), liblapack), Void, - (Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), - &n, D, E, info) + (Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}), + n, D, E, info) chklapackerror(info[]) D, E end @@ -3187,9 +3187,9 @@ for (pttrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (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 @@ -3220,9 +3220,9 @@ for (pttrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($pttrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), D, E, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), D, E, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3259,9 +3259,9 @@ for (trtri, trtrs, elty) in lda = max(1,stride(A, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trtri), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &uplo, &diag, &n, A, &lda, info) + uplo, diag, n, A, lda, info) chklapackerror(info[]) A end @@ -3284,10 +3284,10 @@ for (trtri, trtrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($trtrs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &trans, &diag, &n, &size(B,2), A, &max(1,stride(A,2)), - B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, trans, diag, n, size(B,2), A, max(1,stride(A,2)), + B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -3334,17 +3334,17 @@ for (trcon, trevc, trrfs, elty) in chkdiag(diag) n = checksquare(A) chkuplo(uplo) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() work = Vector{$elty}(3n) iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &norm, &uplo, &diag, &n, - A, &max(1,stride(A,2)), rcond, work, iwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), + norm, uplo, diag, n, + A, max(1,stride(A,2)), rcond, work, iwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end # SUBROUTINE DTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, @@ -3377,13 +3377,13 @@ for (trcon, trevc, trrfs, elty) in info = Ref{BlasInt}() ccall((@blasfunc($trevc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt},Ptr{BlasInt}, Ptr{BlasInt}, + (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}), - &side, &howmny, select, &n, - T, &ldt, VL, &ldvl, - VR, &ldvr, &mm, m, + side, howmny, select, n, + T, ldt, VL, ldvl, + VR, ldvr, mm, m, work, info) chklapackerror(info[]) @@ -3432,11 +3432,11 @@ for (trcon, trevc, trrfs, elty) in iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &uplo, &trans, &diag, &n, - &nrhs, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), X, &max(1,stride(X,2)), + 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) chklapackerror(info[]) Ferr, Berr @@ -3462,17 +3462,17 @@ for (trcon, trevc, trrfs, elty, relty) in n = checksquare(A) chkuplo(uplo) chkdiag(diag) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}(1) work = Vector{$elty}(2n) rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trcon), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &norm, &uplo, &diag, &n, - A, &max(1,stride(A,2)), rcond, work, rwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), + norm, uplo, diag, n, + A, max(1,stride(A,2)), rcond, work, rwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end # SUBROUTINE ZTREVC( SIDE, HOWMNY, SELECT, N, T, LDT, VL, LDVL, VR, @@ -3506,13 +3506,13 @@ for (trcon, trevc, trrfs, elty, relty) in rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trevc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (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}), - &side, &howmny, select, &n, - T, &ldt, VL, &ldvl, - VR, &ldvr, &mm, m, + side, howmny, select, n, + T, ldt, VL, ldvl, + VR, ldvr, mm, m, work, rwork, info) chklapackerror(info[]) @@ -3561,11 +3561,11 @@ for (trcon, trevc, trrfs, elty, relty) in rwork = Vector{$relty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trrfs), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &uplo, &trans, &diag, &n, - &nrhs, A, &max(1,stride(A,2)), B, &max(1,stride(B,2)), X, &max(1,stride(X,2)), + 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) chklapackerror(info[]) Ferr, Berr @@ -3631,9 +3631,9 @@ for (stev, stebz, stegr, stein, elty) in work = Vector{$elty}(max(1, 2n-2)) info = Ref{BlasInt}() ccall((@blasfunc($stev), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &job, &n, dv, ev, Zmat, &n, work, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, + Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + job, n, dv, ev, Zmat, n, work, info) chklapackerror(info[]) dv, Zmat end @@ -3658,13 +3658,13 @@ for (stev, stebz, stegr, stein, elty) in iwork = Vector{BlasInt}(3*n) info = Ref{BlasInt}() ccall((@blasfunc($stebz), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + (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}), - &range, &order, &n, &vl, - &vu, &il, &iu, &abstol, + range, order, n, vl, + vu, il, iu, abstol, dv, ev, m, nsplit, w, iblock, isplit, work, iwork, info) @@ -3692,16 +3692,16 @@ for (stev, stebz, stegr, stein, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($stegr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &range, &n, dv, - eev, &vl, &vu, &il, - &iu, abstol, m, w, - Z, &ldz, isuppz, work, - &lwork, iwork, &liwork, info) + (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}), + jobz, range, n, dv, + eev, vl, vu, il, + iu, abstol, m, w, + Z, ldz, isuppz, work, + lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -3749,11 +3749,11 @@ for (stev, stebz, stegr, stein, elty) in ifail = Vector{BlasInt}(m) info = Ref{BlasInt}() ccall((@blasfunc($stein), liblapack), Void, - (Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, + Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &n, dv, ev, &m, w, iblock, isplit, z, &ldz, work, iwork, ifail, info) + n, dv, ev, m, w, iblock, isplit, z, ldz, work, iwork, ifail, info) chklapackerror(info[]) if any(ifail .!= 0) # TODO: better error message / type @@ -3837,9 +3837,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &'C', &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A, work end @@ -3865,10 +3865,10 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -3899,9 +3899,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &stride(A,2), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, stride(A,2), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -3954,9 +3954,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chkargsok(info[]) chknonsingular(info[]) A @@ -3980,9 +3980,9 @@ for (syconv, sysv, sytrf, sytri, sytrs, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4015,10 +4015,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4049,9 +4049,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &stride(A,2), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, stride(A,2), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4075,9 +4075,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chkargsok(info[]) chknonsingular(info[]) A @@ -4101,9 +4101,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4138,10 +4138,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty) in info = Ref{BlasInt}() ccall((@blasfunc($syconvf), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &way, &n, A, - &lda, ipiv, e, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + uplo, way, n, A, + lda, ipiv, e, info) chklapackerror(info[]) return A, e @@ -4171,9 +4171,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($syconv), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &'C', &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, 'C', n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A, work end @@ -4199,10 +4199,10 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4230,9 +4230,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4287,9 +4287,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4311,9 +4311,9 @@ for (syconv, hesv, hetrf, hetri, hetrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4345,10 +4345,10 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hesv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4376,9 +4376,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in info = Ref{BlasInt}() for i in 1:2 # first call returns lwork as work[1] ccall((@blasfunc($hetrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4403,9 +4403,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($hetri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4427,9 +4427,9 @@ for (hesv, hetrf, hetri, hetrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($hetrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4462,10 +4462,10 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4497,9 +4497,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4553,9 +4553,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4578,9 +4578,9 @@ for (sysv, sytrf, sytri, sytrs, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4613,10 +4613,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sysv), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), - work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), + work, lwork, info) chkargsok(info[]) chknonsingular(info[]) if i == 1 @@ -4648,9 +4648,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($sytrf), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, &lwork, info) + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, A, max(1,stride(A,2)), ipiv, work, lwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4675,9 +4675,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($sytri), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &n, A, &max(1,stride(A,2)), ipiv, work, info) + uplo, n, A, max(1,stride(A,2)), ipiv, work, info) chklapackerror(info[]) A end @@ -4700,9 +4700,9 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in end info = Ref{BlasInt}() ccall((@blasfunc($sytrs), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &uplo, &n, &size(B,2), A, &max(1,stride(A,2)), ipiv, B, &max(1,stride(B,2)), info) + (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + uplo, n, size(B,2), A, max(1,stride(A,2)), ipiv, B, max(1,stride(B,2)), info) chklapackerror(info[]) B end @@ -4738,10 +4738,10 @@ for (sysv, sytrf, sytri, sytrs, syconvf, elty, relty) in info = Ref{BlasInt}() ccall((@blasfunc($syconvf), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &uplo, &way, &n, A, - &max(1, lda), ipiv, e, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), + uplo, way, n, A, + max(1, lda), ipiv, e, info) chklapackerror(info[]) return A, e @@ -4869,9 +4869,9 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &uplo, &n, A, &max(1,stride(A,2)), W, work, &lwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), + jobz, uplo, n, A, max(1,stride(A,2)), W, work, lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -4919,17 +4919,17 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($syevr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (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}), - &jobz, &range, &uplo, &n, - A, &max(1,lda), &vl, &vu, - &il, &iu, &abstol, m, - w, Z, &max(1,ldz), isuppz, - work, &lwork, iwork, &liwork, + 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) chklapackerror(info[]) if i == 1 @@ -4970,14 +4970,14 @@ for (syev, syevr, sygvd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($sygvd), liblapack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}), - &itype, &jobz, &uplo, &n, - A, &lda, B, &ldb, - w, work, &lwork, iwork, - &liwork, info) + (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}), + itype, jobz, uplo, n, + A, lda, B, ldb, + w, work, lwork, iwork, + liwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(work[1]) @@ -5014,9 +5014,9 @@ for (syev, syevr, sygvd, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($syev), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &jobz, &uplo, &n, A, &stride(A,2), W, work, &lwork, rwork, info) + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), + jobz, uplo, n, A, stride(A,2), W, work, lwork, rwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5069,18 +5069,18 @@ for (syev, syevr, sygvd, elty, relty) in 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), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &jobz, &range, &uplo, &n, - A, &lda, &vl, &vu, - &il, &iu, &abstol, m, - w, Z, &ldz, isuppz, - work, &lwork, rwork, &lrwork, - iwork, &liwork, info) + (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}), + jobz, range, uplo, n, + A, lda, vl, vu, + il, iu, abstol, m, + w, Z, ldz, isuppz, + work, lwork, rwork, lrwork, + iwork, liwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5124,14 +5124,14 @@ for (syev, syevr, sygvd, elty, relty) in 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), liblapack), Void, - (Ptr{BlasInt}, Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}), - &itype, &jobz, &uplo, &n, - A, &lda, B, &ldb, - w, work, &lwork, rwork, - &lrwork, iwork, &liwork, info) + (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}), + itype, jobz, uplo, n, + A, lda, B, ldb, + w, work, lwork, rwork, + lrwork, iwork, liwork, info) chkargsok(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5222,14 +5222,14 @@ for (bdsqr, relty, elty) in work = Vector{$relty}(4n) info = Ref{BlasInt}() ccall((@blasfunc($bdsqr), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$relty}, Ptr{BlasInt}), - &uplo, &n, &ncvt, &nru, - &ncc, d, e_, Vt, - &ldvt, U, &ldu, C, - &ldc, work, info) + (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}), + uplo, n, ncvt, nru, + ncc, d, e_, Vt, + ldvt, U, ldu, C, + ldc, work, info) chklapackerror(info[]) d, Vt, U, C #singular values in descending order, P**T * VT, U * Q, Q**T * C end @@ -5292,14 +5292,14 @@ for (bdsdc, elty) in iwork = Vector{BlasInt}(8n) info = Ref{BlasInt}() ccall((@blasfunc($bdsdc), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &uplo, &compq, &n, d, e_, - u, &ldu, vt, &ldvt, + uplo, compq, n, d, e_, + u, ldu, vt, ldvt, q, iq, work, iwork, info) chklapackerror(info[]) - d, e, u, vt, q, iq + d, e_, u, vt, q, iq end end end @@ -5337,18 +5337,18 @@ for (gecon, elty) in chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) - rcond = Vector{$elty}(1) + rcond = Ref{$elty}() work = Vector{$elty}(4n) iwork = Vector{BlasInt}(n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$elty}, Ref{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}), - &normtype, &n, A, &lda, &anorm, rcond, work, iwork, + normtype, n, A, lda, anorm, rcond, work, iwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end end end @@ -5371,18 +5371,18 @@ for (gecon, elty, relty) in chkstride1(A) n = checksquare(A) lda = max(1, stride(A, 2)) - rcond = Vector{$relty}(1) + rcond = Ref{$relty}() work = Vector{$elty}(2n) rwork = Vector{$relty}(2n) info = Ref{BlasInt}() ccall((@blasfunc($gecon), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$relty}, Ptr{$relty}, Ptr{$elty}, Ptr{$relty}, + (Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{$relty}, Ref{$relty}, Ptr{$elty}, Ptr{$relty}, Ptr{BlasInt}), - &normtype, &n, A, &lda, &anorm, rcond, work, rwork, + normtype, n, A, lda, anorm, rcond, work, rwork, info) chklapackerror(info[]) - rcond[1] + rcond[] end end end @@ -5419,11 +5419,11 @@ for (gehrd, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gehrd), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (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, + n, ilo, ihi, A, + max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -5469,11 +5469,11 @@ for (orghr, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($orghr), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, + (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, + n, ilo, ihi, A, + max(1, stride(A, 2)), tau, work, lwork, info) chklapackerror(info[]) if i == 1 @@ -5525,14 +5525,14 @@ for (ormhr, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($ormhr), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}), - &side, &trans, &mC, &nC, - &ilo, &ihi, A, &max(1, stride(A, 2)), - tau, C, &max(1, stride(C, 2)), work, - &lwork, info) + (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}), + side, trans, mC, nC, + ilo, ihi, A, max(1, stride(A, 2)), + tau, C, max(1, stride(C, 2)), work, + lwork, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5558,25 +5558,25 @@ for (gees, gges, elty) in # $ WR( * ) function gees!(jobvs::Char, A::StridedMatrix{$elty}) chkstride1(A) - n = checksquare(A) - sdim = Vector{BlasInt}(1) - wr = similar(A, $elty, n) - wi = similar(A, $elty, n) - ldvs = jobvs == 'V' ? n : 1 - vs = similar(A, $elty, ldvs, n) - work = Vector{$elty}(1) + n = checksquare(A) + sdim = Vector{BlasInt}(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}(1) lwork = BlasInt(-1) - info = Ref{BlasInt}() + info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{Void}, Ptr{BlasInt}), - &jobvs, &'N', C_NULL, &n, - A, &max(1, stride(A, 2)), sdim, wr, - wi, vs, &ldvs, work, - &lwork, C_NULL, info) + (Ref{UInt8}, Ref{UInt8}, Ptr{Void}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, + Ref{BlasInt}, Ptr{Void}, Ptr{BlasInt}), + jobvs, 'N', C_NULL, n, + A, max(1, stride(A, 2)), sdim, wr, + wi, vs, ldvs, work, + lwork, C_NULL, info) chklapackerror(info[]) if i == 1 lwork = BlasInt(real(work[1])) @@ -5614,17 +5614,17 @@ for (gees, gges, elty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Void}, + 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{Void}, Ptr{BlasInt}), - &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, + 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) chklapackerror(info[]) if i == 1 @@ -5651,25 +5651,25 @@ for (gees, gges, elty, relty) in # COMPLEX*16 A( LDA, * ), VS( LDVS, * ), W( * ), WORK( * ) function gees!(jobvs::Char, A::StridedMatrix{$elty}) chkstride1(A) - n = checksquare(A) - sort = 'N' - sdim = BlasInt(0) - w = similar(A, $elty, n) - ldvs = jobvs == 'V' ? n : 1 - vs = similar(A, $elty, ldvs, n) - work = Vector{$elty}(1) + 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}(1) lwork = BlasInt(-1) rwork = Vector{$relty}(n) - info = Ref{BlasInt}() + info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gees), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{Void}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ptr{Void}, Ptr{BlasInt}), - &jobvs, &sort, C_NULL, &n, - A, &max(1, stride(A, 2)), &sdim, w, - vs, &ldvs, work, &lwork, + jobvs, sort, C_NULL, n, + A, max(1, stride(A, 2)), sdim, w, + vs, ldvs, work, lwork, rwork, C_NULL, info) chklapackerror(info[]) if i == 1 @@ -5709,17 +5709,17 @@ for (gees, gges, elty, relty) in info = Ref{BlasInt}() for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($gges), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Void}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$relty}, Ptr{Void}, + (Ref{UInt8}, Ref{UInt8}, Ref{UInt8}, Ptr{Void}, + 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{Void}, Ptr{BlasInt}), - &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, + 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) chklapackerror(info[]) if i == 1 @@ -5774,13 +5774,13 @@ for (trexc, trsen, tgsen, elty) in work = Vector{$elty}(n) info = Ref{BlasInt}() ccall((@blasfunc($trexc), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, Ptr{BlasInt}), - &compq, &n, - T, &ldt, Q, &ldq, - &ifst, &ilst, + compq, n, + T, ldt, Q, ldq, + ifst, ilst, work, info) chklapackerror(info[]) T, Q @@ -5814,15 +5814,15 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($trsen), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &job, select, &n, - T, &ldt, Q, &ldq, - wr, wi, &m, C_NULL, C_NULL, - work, &lwork, iwork, &liwork, + compq, job, select, n, + T, ldt, Q, ldq, + wr, wi, m, C_NULL, C_NULL, + work, lwork, iwork, liwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -5879,19 +5879,19 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (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{Void}, Ptr{Void}, Ptr{Void}, + 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, + 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 @@ -5923,13 +5923,13 @@ for (trexc, trsen, tgsen, elty) in ldq = max(1, stride(Q, 2)) info = Ref{BlasInt}() ccall((@blasfunc($trexc), liblapack), Void, - (Ptr{UInt8}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ref{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &n, - T, &ldt, Q, &ldq, - &ifst, &ilst, + compq, n, + T, ldt, Q, ldq, + ifst, ilst, info) chklapackerror(info[]) T, Q @@ -5959,15 +5959,15 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] ccall((@blasfunc($trsen), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, + (Ref{UInt8}, Ref{UInt8}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ref{BlasInt}, Ptr{Void}, Ptr{Void}, + Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}), - &compq, &job, select, &n, - T, &ldt, Q, &ldq, - w, &m, C_NULL, C_NULL, - work, &lwork, + compq, job, select, n, + T, ldt, Q, ldq, + w, m, C_NULL, C_NULL, + work, lwork, info) chklapackerror(info[]) if i == 1 # only estimated optimal lwork, liwork @@ -6021,19 +6021,19 @@ for (trexc, trsen, tgsen, elty) in select = convert(Array{BlasInt}, select) for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] ccall((@blasfunc($tgsen), liblapack), Void, - (Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, - Ptr{BlasInt}, Ptr{$elty}, Ptr{$elty}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, - Ptr{BlasInt}, Ptr{Void}, Ptr{Void}, Ptr{Void}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, + (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{Void}, Ptr{Void}, Ptr{Void}, + 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, + 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 @@ -6100,11 +6100,11 @@ for (fn, elty, relty) in ((:dtrsyl_, :Float64, :Float64), scale = Vector{$relty}(1) info = Ref{BlasInt}() ccall((@blasfunc($fn), liblapack), Void, - (Ptr{UInt8}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, - Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, Ptr{$elty}, Ptr{BlasInt}, + (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}), - &transa, &transb, &isgn, &m, &n, - A, &lda, B, &ldb, C, &ldc, + transa, transb, isgn, m, n, + A, lda, B, ldb, C, ldc, scale, info) chklapackerror(info[]) C, scale[1] diff --git a/base/linalg/linalg.jl b/base/linalg/linalg.jl index a510f0efc8b51..09816b9e0a73c 100644 --- a/base/linalg/linalg.jl +++ b/base/linalg/linalg.jl @@ -6,10 +6,10 @@ import Base: \, /, *, ^, +, -, == import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac_mul_B, Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!, - ctranspose, eltype, eye, findmax, findmin, fill!, floor, full, getindex, - hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, map, + adjoint, eltype, exp, eye, findmax, findmin, fill!, floor, full, getindex, + hcat, imag, indices, inv, isapprox, isone, IndexStyle, kron, length, log, map, ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round, - setindex!, show, similar, size, transpose, trunc, typed_hcat + setindex!, show, similar, size, sqrt, transpose, trunc, typed_hcat using Base: hvcat_fill, iszero, IndexLinear, _length, promote_op, promote_typeof, @propagate_inbounds, @pure, reduce, typed_vcat # We use `_length` because of non-1 indices; releases after julia 0.5 @@ -53,6 +53,7 @@ export # Functions axpy!, + axpby!, bkfact, bkfact!, chol, @@ -63,8 +64,8 @@ export copy!, copy_transpose!, cross, - ctranspose, - ctranspose!, + adjoint, + adjoint!, det, diag, diagind, @@ -80,7 +81,6 @@ export eigvals, eigvals!, eigvecs, - expm, eye, factorize, givens, @@ -101,7 +101,6 @@ export linreg, logabsdet, logdet, - logm, lu, lufact, lufact!, @@ -125,7 +124,6 @@ export schur, schurfact!, schurfact, - sqrtm, svd, svdfact!, svdfact, diff --git a/base/linalg/lq.jl b/base/linalg/lq.jl index b6b43289385fb..3c535433e6cf5 100644 --- a/base/linalg/lq.jl +++ b/base/linalg/lq.jl @@ -56,7 +56,7 @@ convert(::Type{Matrix}, A::LQ) = convert(Array, convert(AbstractArray, A)) convert(::Type{Array}, A::LQ) = convert(Matrix, A) full(A::LQ) = convert(AbstractArray, A) -ctranspose(A::LQ{T}) where {T} = QR{T,typeof(A.factors)}(A.factors', A.τ) +adjoint(A::LQ{T}) where {T} = QR{T,typeof(A.factors)}(A.factors', A.τ) function getindex(A::LQ, d::Symbol) m, n = size(A) @@ -164,7 +164,7 @@ for (f1, f2) in ((:A_mul_Bc, :A_mul_B!), function ($f1)(A::LQPackedQ, B::StridedVecOrMat) TAB = promote_type(eltype(A), eltype(B)) BB = similar(B, TAB, (size(B, 2), size(B, 1))) - ctranspose!(BB, B) + adjoint!(BB, B) return ($f2)(A, BB) end end @@ -198,7 +198,7 @@ for (f1, f2) in ((:Ac_mul_B, :A_mul_B!), function ($f1)(A::StridedMatrix, B::LQPackedQ) TAB = promote_type(eltype(A), eltype(B)) AA = similar(A, TAB, (size(A, 2), size(A, 1))) - ctranspose!(AA, A) + adjoint!(AA, A) return ($f2)(AA, B) end end diff --git a/base/linalg/lu.jl b/base/linalg/lu.jl index d25f61a2a406f..8e0c0451f41b4 100644 --- a/base/linalg/lu.jl +++ b/base/linalg/lu.jl @@ -241,42 +241,63 @@ function show(io::IO, F::LU) print(io, "\nsuccessful: $(issuccess(F))") end +_apply_ipiv!(A::LU, B::StridedVecOrMat) = _ipiv!(A, 1 : length(A.ipiv), B) +_apply_inverse_ipiv!(A::LU, B::StridedVecOrMat) = _ipiv!(A, length(A.ipiv) : -1 : 1, B) + +function _ipiv!(A::LU, order::OrdinalRange, B::StridedVecOrMat) + for i = order + if i != A.ipiv[i] + _swap_rows!(B, i, A.ipiv[i]) + end + end + B +end + +function _swap_rows!(B::StridedVector, i::Integer, j::Integer) + B[i], B[j] = B[j], B[i] + B +end + +function _swap_rows!(B::StridedMatrix, 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 + A_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('N', A.factors, A.ipiv, B) A.info -A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - A_ldiv_B!(UpperTriangular(A.factors), - A_ldiv_B!(UnitLowerTriangular(A.factors), b[ipiv2perm(A.ipiv, length(b))])) -A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - A_ldiv_B!(UpperTriangular(A.factors), - A_ldiv_B!(UnitLowerTriangular(A.factors), B[ipiv2perm(A.ipiv, size(B, 1)),:])) + +function A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + _apply_ipiv!(A, B) + A_ldiv_B!(UpperTriangular(A.factors), A_ldiv_B!(UnitLowerTriangular(A.factors), B)) +end At_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, B) A.info -At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - At_ldiv_B!(UnitLowerTriangular(A.factors), - At_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))] -At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - At_ldiv_B!(UnitLowerTriangular(A.factors), - At_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:] + +function At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + At_ldiv_B!(UnitLowerTriangular(A.factors), At_ldiv_B!(UpperTriangular(A.factors), B)) + _apply_inverse_ipiv!(A, B) +end Ac_ldiv_B!(F::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:Real} = At_ldiv_B!(F, B) Ac_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, B) A.info -Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) = - Ac_ldiv_B!(UnitLowerTriangular(A.factors), - Ac_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))] -Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) = - Ac_ldiv_B!(UnitLowerTriangular(A.factors), - Ac_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:] + +function Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedVecOrMat) + Ac_ldiv_B!(UnitLowerTriangular(A.factors), Ac_ldiv_B!(UpperTriangular(A.factors), B)) + _apply_inverse_ipiv!(A, B) +end At_ldiv_Bt(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = @assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, transpose(B)) A.info At_ldiv_Bt(A::LU, B::StridedVecOrMat) = At_ldiv_B(A, transpose(B)) Ac_ldiv_Bc(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = - @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, ctranspose(B)) A.info -Ac_ldiv_Bc(A::LU, B::StridedVecOrMat) = Ac_ldiv_B(A, ctranspose(B)) + @assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, adjoint(B)) A.info +Ac_ldiv_Bc(A::LU, B::StridedVecOrMat) = Ac_ldiv_B(A, adjoint(B)) function det(F::LU{T}) where T n = checksquare(F) @@ -327,14 +348,14 @@ end # Tridiagonal # See dgttrf.f -function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(true)) where T +function lufact!(A::Tridiagonal{T,V}, pivot::Union{Val{false}, Val{true}} = Val(true)) where {T,V} n = size(A, 1) info = 0 ipiv = Vector{BlasInt}(n) dl = A.dl d = A.d du = A.du - du2 = A.du2 + du2 = fill!(similar(d, n-2), 0)::V @inbounds begin for i = 1:n @@ -389,12 +410,13 @@ function lufact!(A::Tridiagonal{T}, pivot::Union{Val{false}, Val{true}} = Val(tr end end end - LU{T,Tridiagonal{T}}(A, ipiv, convert(BlasInt, info)) + B = Tridiagonal{T,V}(dl, d, du, du2) + LU{T,Tridiagonal{T,V}}(B, ipiv, convert(BlasInt, info)) end factorize(A::Tridiagonal) = lufact(A) -function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T +function getindex(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} m, n = size(F) if d == :L L = Array(Bidiagonal(ones(T, n), F.factors.dl, d)) @@ -419,7 +441,7 @@ function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T end # See dgtts2.f -function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function A_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} 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")) @@ -450,7 +472,7 @@ function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T return B end -function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function At_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} 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")) @@ -485,7 +507,7 @@ function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T end # Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B) -function Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T +function Ac_ldiv_B!(A::LU{T,Tridiagonal{T,V}}, B::AbstractVecOrMat) where {T,V} 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")) @@ -528,7 +550,7 @@ convert(::Type{Matrix}, F::LU) = convert(Array, convert(AbstractArray, F)) convert(::Type{Array}, F::LU) = convert(Matrix, F) full(F::LU) = convert(AbstractArray, F) -function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where T +function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T,V}}) where {T,V} n = size(F, 1) dl = copy(F.factors.dl) @@ -562,12 +584,12 @@ function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where end return Tridiagonal(dl, d, du) end -convert(::Type{AbstractMatrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractMatrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Tridiagonal, F) -convert(::Type{AbstractArray}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{AbstractArray}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractMatrix, F) -convert(::Type{Matrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Matrix}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Array, convert(AbstractArray, F)) -convert(::Type{Array}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = +convert(::Type{Array}, F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(Matrix, F) -full(F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = convert(AbstractArray, F) +full(F::LU{T,Tridiagonal{T,V}}) where {T,V} = convert(AbstractArray, F) diff --git a/base/linalg/matmul.jl b/base/linalg/matmul.jl index 6112d29be3b54..7badc4be6176b 100644 --- a/base/linalg/matmul.jl +++ b/base/linalg/matmul.jl @@ -663,14 +663,14 @@ function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat if tA == 'T' A11 = transpose(A[1,1]); A12 = transpose(A[2,1]); A21 = transpose(A[1,2]); A22 = transpose(A[2,2]) elseif tA == 'C' - A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2]) + A11 = adjoint(A[1,1]); A12 = adjoint(A[2,1]); A21 = adjoint(A[1,2]); A22 = adjoint(A[2,2]) else A11 = A[1,1]; A12 = A[1,2]; A21 = A[2,1]; A22 = A[2,2] end if tB == 'T' B11 = transpose(B[1,1]); B12 = transpose(B[2,1]); B21 = transpose(B[1,2]); B22 = transpose(B[2,2]) elseif tB == 'C' - B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2]) + B11 = adjoint(B[1,1]); B12 = adjoint(B[2,1]); B21 = adjoint(B[1,2]); B22 = adjoint(B[2,2]) else B11 = B[1,1]; B12 = B[1,2]; B21 = B[2,1]; B22 = B[2,2] end @@ -697,9 +697,9 @@ function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat A21 = transpose(A[1,2]); A22 = transpose(A[2,2]); A23 = transpose(A[3,2]) A31 = transpose(A[1,3]); A32 = transpose(A[2,3]); A33 = transpose(A[3,3]) elseif tA == 'C' - A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A13 = ctranspose(A[3,1]) - A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2]); A23 = ctranspose(A[3,2]) - A31 = ctranspose(A[1,3]); A32 = ctranspose(A[2,3]); A33 = ctranspose(A[3,3]) + A11 = adjoint(A[1,1]); A12 = adjoint(A[2,1]); A13 = adjoint(A[3,1]) + A21 = adjoint(A[1,2]); A22 = adjoint(A[2,2]); A23 = adjoint(A[3,2]) + A31 = adjoint(A[1,3]); A32 = adjoint(A[2,3]); A33 = adjoint(A[3,3]) else A11 = A[1,1]; A12 = A[1,2]; A13 = A[1,3] A21 = A[2,1]; A22 = A[2,2]; A23 = A[2,3] @@ -711,9 +711,9 @@ function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMat B21 = transpose(B[1,2]); B22 = transpose(B[2,2]); B23 = transpose(B[3,2]) B31 = transpose(B[1,3]); B32 = transpose(B[2,3]); B33 = transpose(B[3,3]) elseif tB == 'C' - B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B13 = ctranspose(B[3,1]) - B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2]); B23 = ctranspose(B[3,2]) - B31 = ctranspose(B[1,3]); B32 = ctranspose(B[2,3]); B33 = ctranspose(B[3,3]) + B11 = adjoint(B[1,1]); B12 = adjoint(B[2,1]); B13 = adjoint(B[3,1]) + B21 = adjoint(B[1,2]); B22 = adjoint(B[2,2]); B23 = adjoint(B[3,2]) + B31 = adjoint(B[1,3]); B32 = adjoint(B[2,3]); B33 = adjoint(B[3,3]) else B11 = B[1,1]; B12 = B[1,2]; B13 = B[1,3] B21 = B[2,1]; B22 = B[2,2]; B23 = B[2,3] diff --git a/base/linalg/qr.jl b/base/linalg/qr.jl index a6e1e7522c429..c7e266f98b2d9 100644 --- a/base/linalg/qr.jl +++ b/base/linalg/qr.jl @@ -194,7 +194,7 @@ function qrfactPivotedUnblocked!(A::StridedMatrix) end # LAPACK version -qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{false}) = QRCompactWY(LAPACK.geqrt!(A, min(minimum(size(A)), 36))...) +qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{false}) = QRCompactWY(LAPACK.geqrt!(A, min(min(size(A)...), 36))...) qrfact!(A::StridedMatrix{<:BlasFloat}, ::Val{true}) = QRPivoted(LAPACK.geqp3!(A)...) qrfact!(A::StridedMatrix{<:BlasFloat}) = qrfact!(A, Val(false)) @@ -458,7 +458,7 @@ convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ) where {T} = convert(QRPackedQ{T convert(::Type{QRCompactWYQ{S}}, Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T)) convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ{S}) where {S} = Q convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ) where {S} = convert(QRCompactWYQ{S}, Q) -convert(::Type{Matrix}, A::AbstractQ{T}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), minimum(size(A.factors)))) +convert(::Type{Matrix}, A::AbstractQ{T}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), min(size(A.factors)...))) convert(::Type{Array}, A::AbstractQ) = convert(Matrix, A) """ @@ -595,7 +595,7 @@ for (f1, f2) in ((:A_mul_Bc, :A_mul_B!), function ($f1)(Q::AbstractQ, B::StridedVecOrMat) TQB = promote_type(eltype(Q), eltype(B)) Bc = similar(B, TQB, (size(B, 2), size(B, 1))) - ctranspose!(Bc, B) + adjoint!(Bc, B) return ($f2)(convert(AbstractMatrix{TQB}, Q), Bc) end end @@ -678,7 +678,7 @@ function A_mul_Bc(A::StridedMatrix, B::AbstractQ) throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(B))")) end end -@inline A_mul_Bc(rowvec::RowVector, B::AbstractQ) = ctranspose(B*ctranspose(rowvec)) +@inline A_mul_Bc(rowvec::RowVector, B::AbstractQ) = adjoint(B*adjoint(rowvec)) ### AcQ/AcQc @@ -688,7 +688,7 @@ for (f1, f2) in ((:Ac_mul_B, :A_mul_B!), function ($f1)(A::StridedVecOrMat, Q::AbstractQ) TAQ = promote_type(eltype(A), eltype(Q)) Ac = similar(A, TAQ, (size(A, 2), size(A, 1))) - ctranspose!(Ac, A) + adjoint!(Ac, A) return ($f2)(Ac, convert(AbstractMatrix{TAQ}, Q)) end end @@ -732,8 +732,10 @@ function A_ldiv_B!(A::QRPivoted{T}, B::StridedMatrix{T}, rcond::Real) where T<:B B[1:nA,:] = view(B, 1:nA, :)[invperm(A[:p]::Vector{BlasInt}),:] return B, rnk end -A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = vec(A_ldiv_B!(A,reshape(B,length(B),1))) -A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A_ldiv_B!(A, B, maximum(size(A))*eps(real(float(one(eltype(B))))))[1] +A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = + vec(A_ldiv_B!(A,reshape(B,length(B),1))) +A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = + A_ldiv_B!(A, B, min(size(A)...)*eps(real(float(one(eltype(B))))))[1] function A_ldiv_B!(A::QR{T}, B::StridedMatrix{T}) where T m, n = size(A) minmn = min(m,n) diff --git a/base/linalg/rowvector.jl b/base/linalg/rowvector.jl index eb24fa5792517..37af261e3ea35 100644 --- a/base/linalg/rowvector.jl +++ b/base/linalg/rowvector.jl @@ -6,7 +6,7 @@ A lazy-view wrapper of an [`AbstractVector`](@ref), which turns a length-`n` vector into a `1×n` shaped row vector and represents the transpose of a vector (the elements are also transposed recursively). This type is usually constructed (and unwrapped) via the [`transpose`](@ref) -function or `.'` operator (or related [`ctranspose`](@ref) or `'` operator). +function or `.'` operator (or related [`adjoint`](@ref) or `'` operator). By convention, a vector can be multiplied by a matrix on its left (`A * v`) whereas a row vector can be multiplied by a matrix on its right (such that `v.' * A = (A.' * v).'`). It @@ -75,12 +75,12 @@ julia> transpose(v) ``` """ @inline transpose(vec::AbstractVector) = RowVector(vec) -@inline ctranspose(vec::AbstractVector) = RowVector(_conj(vec)) +@inline adjoint(vec::AbstractVector) = RowVector(_conj(vec)) @inline transpose(rowvec::RowVector) = rowvec.vec @inline transpose(rowvec::ConjRowVector) = copy(rowvec.vec) # remove the ConjArray wrapper from any raw vector -@inline ctranspose(rowvec::RowVector) = conj(rowvec.vec) -@inline ctranspose(rowvec::RowVector{<:Real}) = rowvec.vec +@inline adjoint(rowvec::RowVector) = conj(rowvec.vec) +@inline adjoint(rowvec::RowVector{<:Real}) = rowvec.vec parent(rowvec::RowVector) = rowvec.vec @@ -208,27 +208,31 @@ At_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch( # Conjugated forms A_mul_Bc(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat * ctranspose(rowvec)) -@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * ctranspose(rowvec2) +@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = adjoint(mat * adjoint(rowvec)) +@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * adjoint(rowvec2) A_mul_Bc(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * ctranspose(vec2) -@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * ctranspose(rowvec) +@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * adjoint(vec2) +@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * adjoint(rowvec) -@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = ctranspose(rowvec) * ctranspose(vec) -@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(mat * vec) +@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = adjoint(rowvec) * adjoint(vec) +@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = adjoint(mat * vec) Ac_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = ctranspose(vec)*ctranspose(rowvec) +@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = adjoint(vec)*adjoint(rowvec) Ac_mul_Bc(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * ctranspose(rowvec) +@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * adjoint(rowvec) Ac_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors")) -@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(Ac_mul_B(mat,vec)) -@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = ctranspose(rowvec1) * rowvec2 +@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = adjoint(Ac_mul_B(mat,vec)) +@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = adjoint(rowvec1) * rowvec2 Ac_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = ctranspose(vec1)*vec2 +@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = adjoint(vec1)*vec2 + +# Pseudo-inverse +pinv(v::RowVector, tol::Real=0) = pinv(v', tol)' # Left Division # +\(rowvec1::RowVector, rowvec2::RowVector) = pinv(rowvec1) * rowvec2 \(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) At_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix")) @@ -237,4 +241,4 @@ Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Can @inline /(rowvec::RowVector, mat::AbstractMatrix) = transpose(transpose(mat) \ transpose(rowvec)) @inline A_rdiv_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat \ transpose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat \ ctranspose(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = adjoint(mat \ adjoint(rowvec)) diff --git a/base/linalg/special.jl b/base/linalg/special.jl index e7ba87788d40b..d41078fe2a238 100644 --- a/base/linalg/special.jl +++ b/base/linalg/special.jl @@ -3,12 +3,13 @@ # Methods operating on different special matrix types # Interconversion between special matrix types -convert(::Type{Bidiagonal}, A::Diagonal{T}) where {T} = - Bidiagonal(A.diag, zeros(T, size(A.diag,1)-1), :U) -convert(::Type{SymTridiagonal}, A::Diagonal{T}) where {T} = - SymTridiagonal(A.diag, zeros(T, size(A.diag,1)-1)) -convert(::Type{Tridiagonal}, A::Diagonal{T}) where {T} = - Tridiagonal(zeros(T, size(A.diag,1)-1), A.diag, zeros(T, size(A.diag,1)-1)) +convert(::Type{Bidiagonal}, A::Diagonal) = + Bidiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0), :U) +convert(::Type{SymTridiagonal}, A::Diagonal) = + SymTridiagonal(A.diag, fill!(similar(A.diag, length(A.diag)-1), 0)) +convert(::Type{Tridiagonal}, A::Diagonal) = + Tridiagonal(fill!(similar(A.diag, length(A.diag)-1), 0), A.diag, + fill!(similar(A.diag, length(A.diag)-1), 0)) function convert(::Type{Diagonal}, A::Union{Bidiagonal, SymTridiagonal}) if !iszero(A.ev) @@ -25,8 +26,8 @@ function convert(::Type{SymTridiagonal}, A::Bidiagonal) end convert(::Type{Tridiagonal}, A::Bidiagonal{T}) where {T} = - Tridiagonal(A.uplo == 'U' ? zeros(T, size(A.dv,1)-1) : A.ev, A.dv, - A.uplo == 'U' ? A.ev : zeros(T, size(A.dv,1)-1)) + Tridiagonal(A.uplo == 'U' ? fill!(similar(A.ev), 0) : A.ev, A.dv, + A.uplo == 'U' ? A.ev : fill!(similar(A.ev), 0)) function convert(::Type{Bidiagonal}, A::SymTridiagonal) if !iszero(A.ev) diff --git a/base/linalg/svd.jl b/base/linalg/svd.jl index 3cb28e9a2bb23..f18faf9ded8b3 100644 --- a/base/linalg/svd.jl +++ b/base/linalg/svd.jl @@ -193,7 +193,7 @@ end """ function svdfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (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) @@ -290,7 +290,7 @@ end function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (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) diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 8d98709daaf7f..adf1c0762f71d 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -177,7 +177,7 @@ function copy!(dest::Hermitian, src::Hermitian) if src.uplo == dest.uplo copy!(dest.data, src.data) else - ctranspose!(dest.data, src.data) + adjoint!(dest.data, src.data) end return dest end @@ -212,16 +212,16 @@ issymmetric(A::Hermitian{<:Complex}) = isreal(A) issymmetric(A::Symmetric) = true transpose(A::Symmetric) = A transpose(A::Hermitian{<:Real}) = A -ctranspose(A::Symmetric{<:Real}) = A -function ctranspose(A::Symmetric) - AC = ctranspose(A.data) +adjoint(A::Symmetric{<:Real}) = A +function adjoint(A::Symmetric) + AC = adjoint(A.data) return Symmetric(AC, ifelse(A.uplo == 'U', :L, :U)) end function transpose(A::Hermitian) AT = transpose(A.data) return Hermitian(AT, ifelse(A.uplo == 'U', :L, :U)) end -ctranspose(A::Hermitian) = A +adjoint(A::Hermitian) = A trace(A::Hermitian) = real(trace(A.data)) Base.conj(A::HermOrSym) = typeof(A)(conj(A.data), A.uplo) @@ -307,7 +307,7 @@ A_mul_B!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatri At_mul_B(A::RealHermSymComplexSym, B::AbstractVector) = A*B At_mul_B(A::RealHermSymComplexSym, B::AbstractMatrix) = A*B A_mul_Bt(A::AbstractMatrix, B::RealHermSymComplexSym) = A*B -## Hermitian{<:Number} and Symmetric{<:Real} are invariant to ctranspose; peel off the c +## Hermitian{<:Number} and Symmetric{<:Real} are invariant to adjoint; peel off the c Ac_mul_B(A::RealHermSymComplexHerm, B::AbstractVector) = A*B Ac_mul_B(A::RealHermSymComplexHerm, B::AbstractMatrix) = A*B A_mul_Bc(A::AbstractMatrix, B::RealHermSymComplexHerm) = A*B @@ -589,11 +589,11 @@ function ^(A::Hermitian{T}, p::Real) where T end end -function expm(A::Symmetric) +function exp(A::Symmetric) F = eigfact(A) return Symmetric((F.vectors * Diagonal(exp.(F.values))) * F.vectors') end -function expm(A::Hermitian{T}) where T +function exp(A::Hermitian{T}) where T n = checksquare(A) F = eigfact(A) retmat = (F.vectors * Diagonal(exp.(F.values))) * F.vectors' @@ -607,9 +607,9 @@ function expm(A::Hermitian{T}) where T end end -for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt]) +for func in (:log, :sqrt) @eval begin - function ($funm)(A::Symmetric{T}) where T<:Real + function ($func)(A::Symmetric{T}) where T<:Real F = eigfact(A) if all(λ -> λ ≥ 0, F.values) retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors' @@ -619,7 +619,7 @@ for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt]) return Symmetric(retmat) end - function ($funm)(A::Hermitian{T}) where T + function ($func)(A::Hermitian{T}) where T n = checksquare(A) F = eigfact(A) if all(λ -> λ ≥ 0, F.values) diff --git a/base/linalg/transpose.jl b/base/linalg/transpose.jl index c19730f9f58fc..0baf0f9393f6e 100644 --- a/base/linalg/transpose.jl +++ b/base/linalg/transpose.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -ctranspose(a::AbstractArray) = error("ctranspose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.") +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 ## @@ -16,14 +16,14 @@ regions. transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) """ - ctranspose!(dest,src) + 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. """ -ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A) +adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) function transpose!(B::AbstractVector, A::AbstractMatrix) indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) copy!(B, A) @@ -32,11 +32,11 @@ function transpose!(B::AbstractMatrix, A::AbstractVector) indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose")) copy!(B, A) end -function ctranspose!(B::AbstractVector, A::AbstractMatrix) +function adjoint!(B::AbstractVector, A::AbstractMatrix) indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end -function ctranspose!(B::AbstractMatrix, A::AbstractVector) +function adjoint!(B::AbstractMatrix, A::AbstractVector) indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose")) ccopy!(B, A) end @@ -85,11 +85,11 @@ function ccopy!(B, A) RB, RA = eachindex(B), eachindex(A) if RB == RA for i = RB - B[i] = ctranspose(A[i]) + B[i] = adjoint(A[i]) end else for (i,j) = zip(RB, RA) - B[i] = ctranspose(A[j]) + B[i] = adjoint(A[j]) end end end @@ -119,14 +119,14 @@ function transpose(A::AbstractMatrix) B = similar(A, (ind2, ind1)) transpose!(B, A) end -function ctranspose(A::AbstractMatrix) +function adjoint(A::AbstractMatrix) ind1, ind2 = indices(A) B = similar(A, (ind2, ind1)) - ctranspose!(B, A) + adjoint!(B, A) end -@inline ctranspose(A::AbstractVector{<:Real}) = transpose(A) -@inline ctranspose(A::AbstractMatrix{<:Real}) = transpose(A) +@inline adjoint(A::AbstractVector{<:Real}) = transpose(A) +@inline adjoint(A::AbstractMatrix{<:Real}) = transpose(A) function copy_transpose!(B::AbstractVecOrMat, ir_dest::Range{Int}, jr_dest::Range{Int}, A::AbstractVecOrMat, ir_src::Range{Int}, jr_src::Range{Int}) diff --git a/base/linalg/triangular.jl b/base/linalg/triangular.jl index 79ba4820a7fb1..df46c9239931d 100644 --- a/base/linalg/triangular.jl +++ b/base/linalg/triangular.jl @@ -329,19 +329,19 @@ transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data)) transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data)) transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data)) transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data)) -ctranspose(A::LowerTriangular) = UpperTriangular(ctranspose(A.data)) -ctranspose(A::UnitLowerTriangular) = UnitUpperTriangular(ctranspose(A.data)) -ctranspose(A::UpperTriangular) = LowerTriangular(ctranspose(A.data)) -ctranspose(A::UnitUpperTriangular) = UnitLowerTriangular(ctranspose(A.data)) +adjoint(A::LowerTriangular) = UpperTriangular(adjoint(A.data)) +adjoint(A::UnitLowerTriangular) = UnitUpperTriangular(adjoint(A.data)) +adjoint(A::UpperTriangular) = LowerTriangular(adjoint(A.data)) +adjoint(A::UnitUpperTriangular) = UnitLowerTriangular(adjoint(A.data)) transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L')) transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L')) transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U')) transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U')) -ctranspose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true)) -ctranspose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true)) -ctranspose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true)) -ctranspose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true)) +adjoint!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true)) +adjoint!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true)) +adjoint!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true)) +adjoint!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true)) diag(A::LowerTriangular) = diag(A.data) diag(A::UnitLowerTriangular) = ones(eltype(A), size(A,1)) @@ -441,12 +441,17 @@ scale!(c::Number, A::Union{UpperTriangular,LowerTriangular}) = scale!(A,c) A_mul_B!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B) A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::Tridiagonal) = A_mul_B!(C, full(A), B) A_mul_B!(C::AbstractMatrix, A::Tridiagonal, B::AbstractTriangular) = A_mul_B!(C, A, full(B)) -A_mul_B!(C::AbstractVector, A::AbstractTriangular, B::AbstractVector) = A_mul_B!(A, copy!(C, B)) -A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) -A_mul_B!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B)) A_mul_Bt!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, transpose!(C, B)) -A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B)) -A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B)) +A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) +A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, adjoint!(C, B)) +# The three methods are neceesary to avoid ambiguities with definitions in matmul.jl +for f in (:A_mul_B!, :Ac_mul_B!, :At_mul_B!) + @eval begin + ($f)(C::AbstractVector , A::AbstractTriangular, B::AbstractVector) = ($f)(A, copy!(C, B)) + ($f)(C::AbstractMatrix , A::AbstractTriangular, B::AbstractVecOrMat) = ($f)(A, copy!(C, B)) + ($f)(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = ($f)(A, copy!(C, B)) + end +end for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'), (:UnitLowerTriangular, 'L', 'U'), @@ -1667,9 +1672,9 @@ end # below might compute an unnecessary copy. Eliminating the copy requires adding # all the promotion logic here once again. Since these methods are probably relatively # rare, we chose not to bother for now. -Ac_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(ctranspose(A), B) +Ac_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(adjoint(A), B) At_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(transpose(A), B) -A_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, ctranspose(B)) +A_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, adjoint(B)) A_mul_Bt(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, transpose(B)) Ac_mul_Bc(A::AbstractTriangular, B::AbstractTriangular) = Ac_mul_B(A, B') Ac_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = Ac_mul_B(A, B') @@ -1683,9 +1688,9 @@ At_mul_Bt(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bt(A.', B) @inline A_mul_Bt(rowvec::RowVector, A::AbstractTriangular) = transpose(A * transpose(rowvec)) @inline A_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A * transpose(rowvec) @inline At_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A.' * transpose(rowvec) -@inline A_mul_Bc(rowvec::RowVector, A::AbstractTriangular) = ctranspose(A * ctranspose(rowvec)) -@inline A_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A * ctranspose(rowvec) -@inline Ac_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A' * ctranspose(rowvec) +@inline A_mul_Bc(rowvec::RowVector, A::AbstractTriangular) = adjoint(A * adjoint(rowvec)) +@inline A_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A * adjoint(rowvec) +@inline Ac_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A' * adjoint(rowvec) @inline /(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(transpose(A) \ transpose(rowvec)) @inline /(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(transpose(A) \ transpose(rowvec)) @@ -1693,8 +1698,8 @@ At_mul_Bt(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bt(A.', B) @inline A_rdiv_Bt(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(A \ transpose(rowvec)) @inline A_rdiv_Bt(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(A \ transpose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = ctranspose(A \ ctranspose(rowvec)) -@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = ctranspose(A \ ctranspose(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = adjoint(A \ adjoint(rowvec)) +@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = adjoint(A \ adjoint(rowvec)) \(::Union{UpperTriangular,LowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector")) \(::Union{UnitUpperTriangular,UnitLowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector")) @@ -1784,7 +1789,7 @@ powm(A::LowerTriangular, p::Real) = powm(A.', p::Real).' # 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 -function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} +function log(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} maxsqrt = 100 theta = [1.586970738772063e-005, 2.313807884242979e-003, @@ -1810,7 +1815,7 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} end s0 = s for k = 1:min(s, maxsqrt) - A = sqrtm(A) + A = sqrt(A) end AmI = A - I @@ -1831,7 +1836,8 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} d4 = norm(AmI^4, 1)^(1/4) alpha3 = max(d3, d4) if alpha3 <= theta[tmax] - for j = 3:tmax + local j + for outer j = 3:tmax if alpha3 <= theta[j] break end @@ -1851,7 +1857,7 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} eta = min(alpha3, alpha4) if eta <= theta[tmax] j = 0 - for j = 6:tmax + for outer j = 6:tmax if eta <= theta[j] m = j break @@ -1865,7 +1871,7 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} m = tmax break end - A = sqrtm(A) + A = sqrt(A) AmI = A - I s = s + 1 end @@ -1955,9 +1961,9 @@ function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}} return UpperTriangular(Y) end -logm(A::LowerTriangular) = logm(A.').' +log(A::LowerTriangular) = log(A.').' -# Auxiliary functions for logm and matrix power +# Auxiliary functions for matrix logarithm and matrix power # Compute accurate diagonal of A = A0^s - I # Al-Mohy, "A more accurate Briggs method for the logarithm", @@ -2009,7 +2015,7 @@ function invsquaring(A0::UpperTriangular, theta) end s0 = s for k = 1:min(s, maxsqrt) - A = sqrtm(A) + A = sqrt(A) end AmI = A - I @@ -2030,7 +2036,8 @@ function invsquaring(A0::UpperTriangular, theta) d4 = norm(AmI^4, 1)^(1/4) alpha3 = max(d3, d4) if alpha3 <= theta[tmax] - for j = 3:tmax + local j + for outer j = 3:tmax if alpha3 <= theta[j] break elseif alpha3 / 2 <= theta[5] && p < 2 @@ -2054,7 +2061,7 @@ function invsquaring(A0::UpperTriangular, theta) eta = min(alpha3, alpha4) if eta <= theta[tmax] j = 0 - for j = 6:tmax + for outer j = 6:tmax if eta <= theta[j] m = j break @@ -2066,7 +2073,7 @@ function invsquaring(A0::UpperTriangular, theta) m = tmax break end - A = sqrtm(A) + A = sqrt(A) AmI = A - I s = s + 1 end @@ -2110,9 +2117,9 @@ end unw(x::Real) = 0 unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) -# End of auxiliary functions for logm and matrix power +# End of auxiliary functions for matrix logarithm and matrix power -function sqrtm(A::UpperTriangular) +function sqrt(A::UpperTriangular) realmatrix = false if isreal(A) realmatrix = true @@ -2124,9 +2131,9 @@ function sqrtm(A::UpperTriangular) end end end - sqrtm(A,Val(realmatrix)) + sqrt(A,Val(realmatrix)) end -function sqrtm(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} +function sqrt(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} B = A.data n = checksquare(B) t = realmatrix ? typeof(sqrt(zero(T))) : typeof(sqrt(complex(zero(T)))) @@ -2144,7 +2151,7 @@ function sqrtm(A::UpperTriangular{T},::Val{realmatrix}) where {T,realmatrix} end return UpperTriangular(R) end -function sqrtm(A::UnitUpperTriangular{T}) where T +function sqrt(A::UnitUpperTriangular{T}) where T B = A.data n = checksquare(B) t = typeof(sqrt(zero(T))) @@ -2162,8 +2169,8 @@ function sqrtm(A::UnitUpperTriangular{T}) where T end return UnitUpperTriangular(R) end -sqrtm(A::LowerTriangular) = sqrtm(A.').' -sqrtm(A::UnitLowerTriangular) = sqrtm(A.').' +sqrt(A::LowerTriangular) = sqrt(A.').' +sqrt(A::UnitLowerTriangular) = sqrt(A.').' # Generic eigensystems eigvals(A::AbstractTriangular) = diag(A) diff --git a/base/linalg/tridiag.jl b/base/linalg/tridiag.jl index 5fcaf92009201..299bd571b6dd7 100644 --- a/base/linalg/tridiag.jl +++ b/base/linalg/tridiag.jl @@ -125,7 +125,7 @@ broadcast(::typeof(floor), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = Sy broadcast(::typeof(ceil), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(ceil.(T, M.dv), ceil.(T, M.ev)) transpose(M::SymTridiagonal) = M #Identity operation -ctranspose(M::SymTridiagonal) = conj(M) +adjoint(M::SymTridiagonal) = conj(M) function diag(M::SymTridiagonal{T}, n::Integer=0) where T absn = abs(n) @@ -399,71 +399,58 @@ function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer) end ## Tridiagonal matrices ## -struct Tridiagonal{T} <: AbstractMatrix{T} - dl::Vector{T} # sub-diagonal - d::Vector{T} # diagonal - du::Vector{T} # sup-diagonal - du2::Vector{T} # supsup-diagonal for pivoting +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}(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} + n = length(d) + if (length(dl) != n-1 || length(du) != n-1) + 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 lufact! + function Tridiagonal{T,V}(dl::V, d::V, du::V, du2::V) where {T,V<:AbstractVector{T}} + new{T,V}(dl, d, du, du2) + end end """ - Tridiagonal(dl, d, du) + 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 +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] -3-element Array{Int64,1}: - 1 - 2 - 3 +julia> dl = [1, 2, 3]; -julia> du = [4; 5; 6] -3-element Array{Int64,1}: - 4 - 5 - 6 +julia> du = [4, 5, 6]; -julia> d = [7; 8; 9; 0] -4-element Array{Int64,1}: - 7 - 8 - 9 - 0 +julia> d = [7, 8, 9, 0]; julia> Tridiagonal(dl, d, du) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 7 4 ⋅ ⋅ 1 8 5 ⋅ ⋅ 2 9 6 ⋅ ⋅ 3 0 ``` """ -# Basic constructor takes in three dense vectors of same type -function Tridiagonal(dl::Vector{T}, d::Vector{T}, du::Vector{T}) where T - n = length(d) - if (length(dl) != n-1 || length(du) != n-1) - throw(ArgumentError("cannot make Tridiagonal from incompatible lengths of subdiagonal, diagonal and superdiagonal: ($(length(dl)), $(length(d)), $(length(du))")) - end - Tridiagonal(dl, d, du, zeros(T,n-2)) -end - -# Construct from diagonals of any abstract vector, any eltype -function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu} - Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...) -end +Tridiagonal(dl::V, d::V, du::V) where {T,V<:AbstractVector{T}} = Tridiagonal{T}(dl, d, du) -# Provide a constructor Tridiagonal(A) similar to the triangulars, diagonal, symmetric """ Tridiagonal(A) -returns a `Tridiagonal` array based on (abstract) matrix `A`, using its first lower diagonal, -main diagonal, and first upper diagonal. +Construct a tridiagonal matrix from the first sub-diagonal, +diagonal and first super-diagonal of the matrix `A`. # Examples ```jldoctest @@ -475,16 +462,14 @@ julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4] 1 2 3 4 julia> Tridiagonal(A) -4×4 Tridiagonal{Int64}: +4×4 Tridiagonal{Int64,Array{Int64,1}}: 1 2 ⋅ ⋅ 1 2 3 ⋅ ⋅ 2 3 4 ⋅ ⋅ 3 4 ``` """ -function Tridiagonal(A::AbstractMatrix) - return Tridiagonal(diag(A,-1), diag(A), diag(A,+1)) -end +Tridiagonal(A::AbstractMatrix) = Tridiagonal(diag(A,-1), diag(A,0), diag(A,1)) size(M::Tridiagonal) = (length(M.d), length(M.d)) function size(M::Tridiagonal, d::Integer) @@ -512,34 +497,34 @@ convert(::Type{Matrix}, M::Tridiagonal{T}) where {T} = convert(Matrix{T}, M) convert(::Type{Array}, M::Tridiagonal) = convert(Matrix, M) full(M::Tridiagonal) = convert(Array, M) function similar(M::Tridiagonal, ::Type{T}) where T - Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T), similar(M.du2, T)) + Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T)) end # Operations on Tridiagonal matrices -copy!(dest::Tridiagonal, src::Tridiagonal) = Tridiagonal(copy!(dest.dl, src.dl), copy!(dest.d, src.d), copy!(dest.du, src.du), copy!(dest.du2, src.du2)) +copy!(dest::Tridiagonal, src::Tridiagonal) = (copy!(dest.dl, src.dl); copy!(dest.d, src.d); copy!(dest.du, src.du); dest) #Elementary operations -broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du), abs.(M.du2)) -broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du), round.(M.du2)) -broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du), trunc.(M.du2)) -broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du), floor.(M.du2)) -broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du), ceil.(M.du2)) +broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du)) +broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du)) +broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du)) +broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du)) +broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du)) for func in (:conj, :copy, :real, :imag) @eval function ($func)(M::Tridiagonal) - Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du), ($func)(M.du2)) + Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du)) end end broadcast(::typeof(round), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du), round.(T, M.du2)) + Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du)) broadcast(::typeof(trunc), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du), trunc.(T, M.du2)) + Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du)) broadcast(::typeof(floor), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du), floor.(T, M.du2)) + Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du)) broadcast(::typeof(ceil), ::Type{T}, M::Tridiagonal) where {T<:Integer} = - Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du), ceil.(T, M.du2)) + Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du)) transpose(M::Tridiagonal) = Tridiagonal(M.du, M.d, M.dl) -ctranspose(M::Tridiagonal) = conj(transpose(M)) +adjoint(M::Tridiagonal) = conj(transpose(M)) function diag(M::Tridiagonal{T}, n::Integer=0) where T if n == 0 @@ -646,7 +631,8 @@ end inv(A::Tridiagonal) = inv_usmani(A.dl, A.d, A.du) det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du) -convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = Tridiagonal(convert(Vector{T}, M.dl), convert(Vector{T}, M.d), convert(Vector{T}, M.du), convert(Vector{T}, M.du2)) +convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = + Tridiagonal(convert(AbstractVector{T}, M.dl), convert(AbstractVector{T}, M.d), convert(AbstractVector{T}, M.du)) convert(::Type{AbstractMatrix{T}},M::Tridiagonal) where {T} = convert(Tridiagonal{T}, M) convert(::Type{Tridiagonal{T}}, M::SymTridiagonal{T}) where {T} = Tridiagonal(M) function convert(::Type{SymTridiagonal{T}}, M::Tridiagonal) where T diff --git a/base/linalg/uniformscaling.jl b/base/linalg/uniformscaling.jl index b0feaac7d74f0..9273659baadbc 100644 --- a/base/linalg/uniformscaling.jl +++ b/base/linalg/uniformscaling.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -import Base: copy, ctranspose, getindex, show, transpose, one, zero, inv, +import Base: copy, adjoint, getindex, show, transpose, one, zero, inv, hcat, vcat, hvcat import Base.LinAlg: SingularException @@ -63,7 +63,7 @@ end copy(J::UniformScaling) = UniformScaling(J.λ) transpose(J::UniformScaling) = J -ctranspose(J::UniformScaling) = UniformScaling(conj(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}) @@ -168,6 +168,16 @@ end inv(J::UniformScaling) = UniformScaling(inv(J.λ)) norm(J::UniformScaling, p::Real=2) = abs(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 + *(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)) diff --git a/base/loading.jl b/base/loading.jl index b366015918401..c3be02d771fb7 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -46,7 +46,7 @@ elseif Sys.isapple() isfile(path) || return false path_basename = String(basename(path)) local casepreserved_basename - const header_size = 12 + header_size = 12 buf = Vector{UInt8}(length(path_basename) + header_size + 1) while true ret = ccall(:getattrlist, Cint, @@ -509,7 +509,7 @@ function create_expr_cache(input::String, output::String, concrete_deps::Vector{ """ io = open(pipeline(detach(`$(julia_cmd()) -O0 --output-ji $output --output-incremental=yes - --startup-file=no --history-file=no + --startup-file=no --history-file=no --warn-overwrite=yes --color=$(have_color ? "yes" : "no") --eval $code_object`), stderr=STDERR), "w", STDOUT) diff --git a/base/markdown/Markdown.jl b/base/markdown/Markdown.jl index 40f8b8955b273..9f2155fb2c3b5 100644 --- a/base/markdown/Markdown.jl +++ b/base/markdown/Markdown.jl @@ -56,8 +56,8 @@ macro doc_str(s::AbstractString, t...) docexpr(__source__, __module__, s, t...) end -function Base.display(d::Base.REPL.REPLDisplay, md::Vector{MD}) - for md in md +function Base.display(d::Base.REPL.REPLDisplay, mds::Vector{MD}) + for md in mds display(d, md) end end diff --git a/base/markdown/render/latex.jl b/base/markdown/render/latex.jl index fb0650c5d6603..1e885a578d588 100644 --- a/base/markdown/render/latex.jl +++ b/base/markdown/render/latex.jl @@ -46,8 +46,8 @@ function latexinline(io::IO, code::Code) end function latex(io::IO, md::Paragraph) - for md in md.content - latexinline(io, md) + for mdc in md.content + latexinline(io, mdc) end println(io) println(io) diff --git a/base/markdown/render/rst.jl b/base/markdown/render/rst.jl index d9fadc4e7910e..377aee7399926 100644 --- a/base/markdown/render/rst.jl +++ b/base/markdown/render/rst.jl @@ -90,8 +90,8 @@ end function rst(io::IO, l::LaTeX) println(io, ".. math::\n") - for l in lines(l.formula) - println(io, " ", l) + for line in lines(l.formula) + println(io, " ", line) end end diff --git a/base/math.jl b/base/math.jl index 07e17d69d57c1..5cb9cd0f09710 100644 --- a/base/math.jl +++ b/base/math.jl @@ -21,11 +21,11 @@ import Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, exp10, expm1, log1p using Base: sign_mask, exponent_mask, exponent_one, - exponent_half, fpinttype, significand_mask + exponent_half, uinttype, significand_mask using Core.Intrinsics: sqrt_llvm -const IEEEFloat = Union{Float16, Float32, Float64} +using Base.IEEEFloat @noinline function throw_complex_domainerror(f, x) throw(DomainError(x, string("$f will only return a complex result if called with a ", @@ -436,7 +436,7 @@ julia> log1p(0) ``` """ log1p(x) -for f in (:sin, :cos, :tan, :asin, :acos, :acosh, :atanh, :log, :log2, :log10, +for f in (:sin, :cos, :tan, :acos, :acosh, :atanh, :log, :log2, :log10, :lgamma, :log1p) @eval begin @inline ($f)(x::Float64) = nan_dom_err(ccall(($(string(f)), libm), Float64, (Float64,), x), x) @@ -445,6 +445,8 @@ for f in (:sin, :cos, :tan, :asin, :acos, :acosh, :atanh, :log, :log2, :log10, end end +@inline asin(x::Real) = asin(float(x)) + """ sincos(x) @@ -588,7 +590,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat return flipsign(T(Inf), x) end if k > 0 # normal case - xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T)) + 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 @@ -598,7 +600,7 @@ function ldexp(x::T, e::Integer) where T<:IEEEFloat end k += significand_bits(T) z = T(2.0)^-significand_bits(T) - xu = (xu & ~exponent_mask(T)) | (rem(k, fpinttype(T)) << significand_bits(T)) + xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) return z*reinterpret(T, xu) end end @@ -758,9 +760,9 @@ end @inline literal_pow(::typeof(^), x::Float16, ::Val{p}) where {p} = Float16(literal_pow(^,Float32(x),Val(p))) function angle_restrict_symm(theta) - const P1 = 4 * 7.8539812564849853515625e-01 - const P2 = 4 * 3.7748947079307981766760e-08 - const P3 = 4 * 2.6951514290790594840552e-15 + P1 = 4 * 7.8539812564849853515625e-01 + P2 = 4 * 3.7748947079307981766760e-08 + P3 = 4 * 2.6951514290790594840552e-15 y = 2*floor(theta/(2*pi)) r = ((theta - y*P1) - y*P2) - y*P3 diff --git a/base/mathconstants.jl b/base/mathconstants.jl new file mode 100644 index 0000000000000..d82d61e5414d1 --- /dev/null +++ b/base/mathconstants.jl @@ -0,0 +1,92 @@ +# 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 π 3.14159265358979323846 pi +Base.@irrational ℯ 2.71828182845904523536 exp(big(1)) +Base.@irrational γ 0.57721566490153286061 euler +Base.@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 +Base.@irrational catalan 0.91596559417721901505 catalan + +# aliases +""" + π + pi + +The constant pi. + +```jldoctest +julia> pi +π = 3.1415926535897... +``` +""" +π, const pi = π + +""" + ℯ + e + +The constant ℯ. + +```jldoctest +julia> ℯ +ℯ = 2.7182818284590... +``` +""" +ℯ, const e = ℯ + +""" + γ + eulergamma + +Euler's constant. + +```jldoctest +julia> MathConstants.eulergamma +γ = 0.5772156649015... +``` +""" +γ, const eulergamma = γ + +""" + φ + golden + +The golden ratio. + +```jldoctest +julia> MathConstants.golden +φ = 1.6180339887498... +``` +""" +φ, const golden = φ + +""" + catalan + +Catalan's constant. + +```jldoctest +julia> MathConstants.catalan +catalan = 0.9159655941772... +``` +""" +catalan + +# loop over types to prevent ambiguities for ^(::Number, x) +for T in (Irrational, Rational, Integer, Number) + Base.:^(::Irrational{:ℯ}, x::T) = exp(x) +end + +Base.log(::Irrational{:ℯ}) = 1 # use 1 to correctly promote expressions like log(x)/log(ℯ) +Base.log(::Irrational{:ℯ}, x::Number) = log(x) + +end # module diff --git a/base/methodshow.jl b/base/methodshow.jl index 095cb2f56b72f..286980c197be0 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -144,7 +144,8 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru n = length(ms) if header m = n==1 ? "method" : "methods" - ns = isself ? string(name) : string("(::", name, ")") + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", name, ")") what = startswith(ns, '@') ? "macro" : "generic function" print(io, "# $n $m for ", what, " \"", ns, "\":") end diff --git a/base/mmap.jl b/base/mmap.jl index edc8c332ee7a4..c8d111e037330 100644 --- a/base/mmap.jl +++ b/base/mmap.jl @@ -11,7 +11,14 @@ mutable struct Anonymous <: IO create::Bool end +""" + Mmap.Anonymous(name, readonly, create) + +Create an `IO`-like object for creating zeroed-out mmapped-memory that is not tied to a file +for use in `Mmap.mmap`. Used by `SharedArray` for creating shared memory arrays. +""" Anonymous() = Anonymous("",false,true) + Base.isopen(::Anonymous) = true Base.isreadable(::Anonymous) = true Base.iswritable(a::Anonymous) = !a.readonly @@ -94,6 +101,65 @@ else end # os-test # core implementation of mmap + +""" + Mmap.mmap(io::Union{IOStream,AbstractString,Mmap.AnonymousMmap}[, type::Type{Array{T,N}}, dims, offset]; grow::Bool=true, shared::Bool=true) + Mmap.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` 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) +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.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)),), @@ -165,6 +231,17 @@ mmap(file::AbstractString, ::Type{T}, len::Integer, offset::Integer=Int64(0); gr 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.mmap(io, BitArray, [dims, offset]) + +Create a `BitArray` 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.mmap), but +the byte representation is different. + +**Example**: `B = Mmap.mmap(s, BitArray, (25,30000))` + +This would create a 25-by-30000 `BitArray`, linked to the file associated with stream `s`. +""" 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) @@ -204,6 +281,12 @@ 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` and the on-disk version. +""" function sync!(m::Array{T}, flags::Integer=MS_SYNC) where T offset = rem(UInt(pointer(m)), PAGESIZE) ptr = pointer(m) - offset diff --git a/base/mpfr.jl b/base/mpfr.jl index 29644bd2e7bf4..864c6b6f650c5 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -7,7 +7,7 @@ export setprecision import - Base: (*), +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, + Base: *, +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float, @@ -27,8 +27,11 @@ import Base.Math.lgamma_r import Base.FastMath.sincos_fast -get_version() = VersionNumber(unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ()))) -get_patches() = split(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())),' ') +function version() + version = unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ())) + build = replace(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())), ' ', '.') + isempty(build) ? VersionNumber(version) : VersionNumber(version * '+' * build) +end function __init__() try @@ -36,13 +39,12 @@ function __init__() set_emin!(get_emin_min()) set_emax!(get_emax_max()) catch ex - Base.showerror_nostdio(ex, - "WARNING: Error during initialization of module MPFR") + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module MPFR") end end const ROUNDING_MODE = Ref{Cint}(0) -const DEFAULT_PRECISION = [256] +const DEFAULT_PRECISION = Ref(256) # Basic type and initialization definitions @@ -60,7 +62,7 @@ mutable struct BigFloat <: AbstractFloat function BigFloat() prec = precision(BigFloat) z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Void, (Ptr{BigFloat}, Clong), &z, prec) + ccall((:mpfr_init2,:libmpfr), Void, (Ref{BigFloat}, Clong), z, prec) finalizer(z, cglobal((:mpfr_clear, :libmpfr))) return z end @@ -102,7 +104,7 @@ for (fJ, fC) in ((:si,:Clong), (:ui,:Culong), (:d,:Float64)) @eval begin function convert(::Type{BigFloat}, x::($fC)) z = BigFloat() - ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Int32, (Ptr{BigFloat}, ($fC), Int32), &z, x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Int32, (Ref{BigFloat}, $fC, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -110,21 +112,21 @@ end function convert(::Type{BigFloat}, x::BigInt) z = BigFloat() - ccall((:mpfr_set_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_set_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Int32), z, x, ROUNDING_MODE[]) return z end convert(::Type{BigFloat}, x::Integer) = BigFloat(BigInt(x)) -convert(::Type{BigFloat}, x::Union{Bool,Int8,Int16,Int32}) = BigFloat(convert(Clong,x)) -convert(::Type{BigFloat}, x::Union{UInt8,UInt16,UInt32}) = BigFloat(convert(Culong,x)) +convert(::Type{BigFloat}, x::Union{Bool,Int8,Int16,Int32}) = BigFloat(convert(Clong, x)) +convert(::Type{BigFloat}, x::Union{UInt8,UInt16,UInt32}) = BigFloat(convert(Culong, x)) convert(::Type{BigFloat}, x::Union{Float16,Float32}) = BigFloat(Float64(x)) convert(::Type{BigFloat}, x::Rational) = BigFloat(numerator(x)) / BigFloat(denominator(x)) function tryparse(::Type{BigFloat}, s::AbstractString, base::Int=0) z = BigFloat() - err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ptr{BigFloat}, Cstring, Int32, Int32), &z, s, base, ROUNDING_MODE[]) + err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, Int32), z, s, base, ROUNDING_MODE[]) err == 0 ? Nullable(z) : Nullable{BigFloat}() end @@ -177,12 +179,10 @@ BigFloat(x::String) = parse(BigFloat, x) ## BigFloat -> Integer function unsafe_cast(::Type{Int64}, x::BigFloat, ri::Cint) - ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, - (Ptr{BigFloat}, Cint), &x, ri) + ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, (Ref{BigFloat}, Cint), x, ri) end function unsafe_cast(::Type{UInt64}, x::BigFloat, ri::Cint) - ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, - (Ptr{BigFloat}, Cint), &x, ri) + ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, (Ref{BigFloat}, Cint), x, ri) end function unsafe_cast(::Type{T}, x::BigFloat, ri::Cint) where T<:Signed @@ -195,32 +195,31 @@ end function unsafe_cast(::Type{BigInt}, x::BigFloat, ri::Cint) # actually safe, just keep naming consistent z = BigInt() - ccall((:mpfr_get_z, :libmpfr), Int32, (Ptr{BigInt}, Ptr{BigFloat}, Int32), - &z, &x, ri) - z + ccall((:mpfr_get_z, :libmpfr), Int32, (Ref{BigInt}, Ref{BigFloat}, Int32), z, x, ri) + return z end -unsafe_cast(::Type{Int128}, x::BigFloat, ri::Cint) = Int128(unsafe_cast(BigInt,x,ri)) -unsafe_cast(::Type{UInt128}, x::BigFloat, ri::Cint) = UInt128(unsafe_cast(BigInt,x,ri)) -unsafe_cast(::Type{T}, x::BigFloat, r::RoundingMode) where {T<:Integer} = unsafe_cast(T,x,to_mpfr(r)) +unsafe_cast(::Type{Int128}, x::BigFloat, ri::Cint) = Int128(unsafe_cast(BigInt, x, ri)) +unsafe_cast(::Type{UInt128}, x::BigFloat, ri::Cint) = UInt128(unsafe_cast(BigInt, x, ri)) +unsafe_cast(::Type{T}, x::BigFloat, r::RoundingMode) where {T<:Integer} = unsafe_cast(T, x, to_mpfr(r)) -unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T,x,RoundToZero) +unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T, x, RoundToZero) function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x)) - unsafe_cast(T,x,RoundToZero) + unsafe_cast(T, x, RoundToZero) end function floor(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:floor, T, x)) - unsafe_cast(T,x,RoundDown) + unsafe_cast(T, x, RoundDown) end function ceil(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:ceil, T, x)) - unsafe_cast(T,x,RoundUp) + unsafe_cast(T, x, RoundUp) end function round(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} (typemin(T) <= x <= typemax(T)) || throw(InexactError(:round, T, x)) - unsafe_cast(T,x,ROUNDING_MODE[]) + unsafe_cast(T, x, ROUNDING_MODE[]) end trunc(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundToZero) @@ -234,8 +233,11 @@ floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x) ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x) round(::Type{Integer}, x::BigFloat) = round(BigInt, x) -convert(::Type{Bool}, x::BigFloat) = x==0 ? false : x==1 ? true : +function convert(::Type{Bool}, x::BigFloat) + iszero(x) && return false + isone(x) && return true throw(InexactError(:convert, Bool, x)) +end function convert(::Type{BigInt},x::BigFloat) isinteger(x) || throw(InexactError(:convert, BigInt, x)) trunc(BigInt,x) @@ -252,19 +254,18 @@ end ## BigFloat -> AbstractFloat convert(::Type{Float64}, x::BigFloat) = - ccall((:mpfr_get_d,:libmpfr), Float64, (Ptr{BigFloat}, Int32), &x, ROUNDING_MODE[]) + ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{BigFloat}, Int32), x, ROUNDING_MODE[]) convert(::Type{Float32}, x::BigFloat) = - ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat}, Int32), &x, ROUNDING_MODE[]) + ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, Int32), x, ROUNDING_MODE[]) # TODO: avoid double rounding convert(::Type{Float16}, x::BigFloat) = convert(Float16, convert(Float32, x)) Float64(x::BigFloat, r::RoundingMode) = - ccall((:mpfr_get_d,:libmpfr), Float64, (Ptr{BigFloat}, Int32), &x, to_mpfr(r)) + ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{BigFloat}, Int32), x, to_mpfr(r)) Float32(x::BigFloat, r::RoundingMode) = - ccall((:mpfr_get_flt,:libmpfr), Float32, (Ptr{BigFloat}, Int32), &x, to_mpfr(r)) + ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, Int32), x, to_mpfr(r)) # TODO: avoid double rounding -Float16(x::BigFloat, r::RoundingMode) = - convert(Float16, Float32(x, r)) +Float16(x::BigFloat, r::RoundingMode) = convert(Float16, Float32(x, r)) promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat @@ -273,9 +274,9 @@ promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat big(::Type{<:AbstractFloat}) = BigFloat function convert(::Type{Rational{BigInt}}, x::AbstractFloat) - if isnan(x); return zero(BigInt)//zero(BigInt); end - if isinf(x); return copysign(one(BigInt),x)//zero(BigInt); end - if x == 0; return zero(BigInt) // one(BigInt); end + 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 @@ -286,14 +287,14 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CulongMax, x::BigFloat) = ($fJ)(x,c) @@ -301,7 +302,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # Signed Integer function ($fJ)(x::BigFloat, c::ClongMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::ClongMax, x::BigFloat) = ($fJ)(x,c) @@ -309,7 +310,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # Float32/Float64 function ($fJ)(x::BigFloat, c::CdoubleMax) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::CdoubleMax, x::BigFloat) = ($fJ)(x,c) @@ -317,7 +318,7 @@ for (fJ, fC) in ((:+,:add), (:*,:mul)) # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, ROUNDING_MODE[]) return z end ($fJ)(c::BigInt, x::BigFloat) = ($fJ)(x,c) @@ -329,50 +330,50 @@ for (fJ, fC) in ((:-,:sub), (:/,:div)) # BigFloat function ($fJ)(x::BigFloat, y::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CulongMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:ui_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Culong, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:ui_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::ClongMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:si_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Clong, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:si_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, ROUNDING_MODE[]) return z end function ($fJ)(c::CdoubleMax, x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,:d_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Cdouble, Ptr{BigFloat}, Int32), &z, c, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,:d_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end # BigInt function ($fJ)(x::BigFloat, c::BigInt) z = BigFloat() - ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, ROUNDING_MODE[]) return z end # no :mpfr_z_div function @@ -381,13 +382,13 @@ end function -(c::BigInt, x::BigFloat) z = BigFloat() - ccall((:mpfr_z_sub, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}, Ptr{BigFloat}, Int32), &z, &c, &x, ROUNDING_MODE[]) + ccall((:mpfr_z_sub, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Ref{BigFloat}, Int32), z, c, x, ROUNDING_MODE[]) return z end function fma(x::BigFloat, y::BigFloat, z::BigFloat) r = BigFloat() - ccall(("mpfr_fma",:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &r, &x, &y, &z, ROUNDING_MODE[]) + ccall(("mpfr_fma",:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), r, x, y, z, ROUNDING_MODE[]) return r end @@ -395,58 +396,58 @@ end # BigFloat function div(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_div,:libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div,:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, to_mpfr(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, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, c, to_mpfr(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, (Ptr{BigFloat}, Culong, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_ui_div, :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, Int32), z, c, x, to_mpfr(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, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, c, to_mpfr(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, (Ptr{BigFloat}, Clong, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_si_div, :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, Int32), z, c, x, to_mpfr(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, (Ptr{BigFloat}, Ptr{BigFloat}, Cdouble, Int32), &z, &x, c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_d, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), z, x, c, to_mpfr(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, (Ptr{BigFloat}, Cdouble, Ptr{BigFloat}, Int32), &z, c, &x, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_d_div, :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, Int32), z, c, x, to_mpfr(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, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &c, to_mpfr(RoundToZero)) - ccall((:mpfr_trunc, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &z) + ccall((:mpfr_div_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, c, to_mpfr(RoundToZero)) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) return z end @@ -456,23 +457,23 @@ 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, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &d, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), 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, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &a, &b, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &c, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &d, ROUNDING_MODE[]) - ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &z, &e, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, d, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, z, e, ROUNDING_MODE[]) return z end end @@ -480,17 +481,15 @@ end function -(x::BigFloat) z = BigFloat() - ccall((:mpfr_neg, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_neg, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end function sqrt(x::BigFloat) isnan(x) && return x z = BigFloat() - ccall((:mpfr_sqrt, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) - if isnan(z) - throw(DomainError(x, "NaN result for non-NaN input.")) - end + ccall((:mpfr_sqrt, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end @@ -498,25 +497,25 @@ sqrt(x::BigInt) = sqrt(BigFloat(x)) function ^(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_pow, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_pow, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::CulongMax) z = BigFloat() - ccall((:mpfr_pow_ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, y, ROUNDING_MODE[]) + ccall((:mpfr_pow_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::ClongMax) z = BigFloat() - ccall((:mpfr_pow_si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, y, ROUNDING_MODE[]) + ccall((:mpfr_pow_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, y, ROUNDING_MODE[]) return z end function ^(x::BigFloat, y::BigInt) z = BigFloat() - ccall((:mpfr_pow_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigInt}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_pow_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -526,7 +525,7 @@ end 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, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -534,8 +533,7 @@ end function sincos_fast(v::BigFloat) s = BigFloat() c = BigFloat() - ccall((:mpfr_sin_cos, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &s, &c, &v, ROUNDING_MODE[]) + ccall((:mpfr_sin_cos, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), s, c, v, ROUNDING_MODE[]) return (s, c) end sincos(v::BigFloat) = sincos_fast(v) @@ -543,24 +541,23 @@ sincos(v::BigFloat) = sincos_fast(v) # return log(2) function big_ln2() c = BigFloat() - ccall((:mpfr_const_log2, :libmpfr), Cint, (Ptr{BigFloat}, Int32), - &c, MPFR.ROUNDING_MODE[]) + ccall((:mpfr_const_log2, :libmpfr), Cint, (Ref{BigFloat}, Int32), c, MPFR.ROUNDING_MODE[]) return c end function ldexp(x::BigFloat, n::Clong) z = BigFloat() - ccall((:mpfr_mul_2si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &x, n, ROUNDING_MODE[]) + ccall((:mpfr_mul_2si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, x, n, ROUNDING_MODE[]) return z end function ldexp(x::BigFloat, n::Culong) z = BigFloat() - ccall((:mpfr_mul_2ui, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Culong, Int32), &z, &x, n, ROUNDING_MODE[]) + ccall((:mpfr_mul_2ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), 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)) +ldexp(x::BigFloat, n::Integer) = x * exp2(BigFloat(n)) function factorial(x::BigFloat) if x < 0 || !isinteger(x) @@ -568,13 +565,13 @@ function factorial(x::BigFloat) end ui = convert(Culong, x) z = BigFloat() - ccall((:mpfr_fac_ui, :libmpfr), Int32, (Ptr{BigFloat}, Culong, Int32), &z, ui, ROUNDING_MODE[]) + ccall((:mpfr_fac_ui, :libmpfr), Int32, (Ref{BigFloat}, Culong, Int32), z, ui, ROUNDING_MODE[]) return z end function hypot(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_hypot, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_hypot, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -585,7 +582,7 @@ for f in (:log, :log2, :log10) "with a complex argument. Try ", $f, "(complex(x))."))) end z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end end @@ -593,10 +590,10 @@ end function log1p(x::BigFloat) if x < -1 throw(DomainError(x, string("log1p will only return a complex result if called ", - "with a complex argument. Try log1p(complex(x))."))) + "with a complex argument. Try log1p(complex(x))."))) end z = BigFloat() - ccall((:mpfr_log1p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_log1p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) return z end @@ -604,7 +601,7 @@ function max(x::BigFloat, y::BigFloat) isnan(x) && return x isnan(y) && return y z = BigFloat() - ccall((:mpfr_max, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_max, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -612,29 +609,27 @@ function min(x::BigFloat, y::BigFloat) isnan(x) && return x isnan(y) && return y z = BigFloat() - ccall((:mpfr_min, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_min, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function modf(x::BigFloat) - if isinf(x) - return (BigFloat(NaN), x) - end + isinf(x) && return (BigFloat(NaN), x) zint = BigFloat() zfloat = BigFloat() - ccall((:mpfr_modf, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &zint, &zfloat, &x, ROUNDING_MODE[]) + ccall((:mpfr_modf, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), zint, zfloat, x, ROUNDING_MODE[]) return (zfloat, zint) end function rem(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_fmod, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_fmod, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function rem(x::BigFloat, y::BigFloat, ::RoundingMode{:Nearest}) z = BigFloat() - ccall((:mpfr_remainder, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_remainder, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end @@ -645,25 +640,19 @@ function sum(arr::AbstractArray{BigFloat}) z = BigFloat(0) for i in arr ccall((:mpfr_add, :libmpfr), Int32, - (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), - &z, &z, &i, 0) + (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Cint), z, z, i, 0) 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, :gamma) +for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh, :gamma) @eval begin function ($f)(x::BigFloat) - if isnan(x) - return x - end + isnan(x) && return x z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, ROUNDING_MODE[]) - if isnan(z) - throw(DomainError(x, "NaN result for non-NaN input.")) - end + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) return z end end @@ -673,7 +662,7 @@ end const lgamma_signp = Ref{Cint}() function lgamma(x::BigFloat) z = BigFloat() - ccall((:mpfr_lgamma,:libmpfr), Cint, (Ptr{BigFloat}, Ptr{Cint}, Ptr{BigFloat}, Int32), &z, lgamma_signp, &x, ROUNDING_MODE[]) + ccall((:mpfr_lgamma,:libmpfr), Cint, (Ref{BigFloat}, Ref{Cint}, Ref{BigFloat}, Int32), z, lgamma_signp, x, ROUNDING_MODE[]) return z end @@ -681,28 +670,28 @@ lgamma_r(x::BigFloat) = (lgamma(x), lgamma_signp[]) function atan2(y::BigFloat, x::BigFloat) z = BigFloat() - ccall((:mpfr_atan2, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &y, &x, ROUNDING_MODE[]) + ccall((:mpfr_atan2, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, y, x, ROUNDING_MODE[]) return z end # Utility functions -==(x::BigFloat, y::BigFloat) = ccall((:mpfr_equal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 -<=(x::BigFloat, y::BigFloat) = ccall((:mpfr_lessequal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 ->=(x::BigFloat, y::BigFloat) = ccall((:mpfr_greaterequal_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 -<(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 ->(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &x, &y) != 0 +==(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) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigInt}), &x, &y) + ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}), x, y) end function cmp(x::BigFloat, y::ClongMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ptr{BigFloat}, Clong), &x, y) + ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ref{BigFloat}, Clong), x, y) end function cmp(x::BigFloat, y::CulongMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) - ccall((:mpfr_cmp_ui, :libmpfr), Int32, (Ptr{BigFloat}, Culong), &x, y) + 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) @@ -710,29 +699,29 @@ cmp(x::Integer, y::BigFloat) = -cmp(y,x) function cmp(x::BigFloat, y::CdoubleMax) isnan(x) && throw(DomainError(x, "`x` cannot be NaN.")) isnan(y) && throw(DomainError(y, "`y` cannot be NaN.")) - ccall((:mpfr_cmp_d, :libmpfr), Int32, (Ptr{BigFloat}, Cdouble), &x, y) + 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::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::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::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, (Ptr{BigFloat},), &x) != 0 +signbit(x::BigFloat) = ccall((:mpfr_signbit, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 function precision(x::BigFloat) # precision of an object of type BigFloat - return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ptr{BigFloat},), &x) + return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ref{BigFloat},), x) end """ @@ -740,7 +729,7 @@ end Get the precision (in bits) currently used for [`BigFloat`](@ref) arithmetic. """ -precision(::Type{BigFloat}) = DEFAULT_PRECISION[end] # precision of the type BigFloat itself +precision(::Type{BigFloat}) = DEFAULT_PRECISION[] # precision of the type BigFloat itself """ setprecision([T=BigFloat,] precision::Int) @@ -751,7 +740,7 @@ function setprecision(::Type{BigFloat}, precision::Int) if precision < 2 throw(DomainError(precision, "`precision` cannot be less than 2.")) end - DEFAULT_PRECISION[end] = precision + DEFAULT_PRECISION[] = precision end setprecision(precision::Int) = setprecision(BigFloat, precision) @@ -790,43 +779,43 @@ setrounding(::Type{BigFloat},r::RoundingMode) = setrounding_raw(BigFloat,to_mpfr function copysign(x::BigFloat, y::BigFloat) z = BigFloat() - ccall((:mpfr_copysign, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Ptr{BigFloat}, Int32), &z, &x, &y, ROUNDING_MODE[]) + ccall((:mpfr_copysign, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, y, ROUNDING_MODE[]) return z end function exponent(x::BigFloat) - if x == 0 || !isfinite(x) + 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, (Ptr{BigFloat},), &x) - 1 + 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}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), c, &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, Cint), 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}, Ptr{BigFloat}, Ptr{BigFloat}, Cint), c, &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, Cint), c, z, x, ROUNDING_MODE[]) # Double the significand to make it work as Base.significand - ccall((:mpfr_mul_si, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Clong, Int32), &z, &z, 2, ROUNDING_MODE[]) + ccall((:mpfr_mul_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, Int32), z, z, 2, ROUNDING_MODE[]) return z end function isinteger(x::BigFloat) - return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end for f in (:ceil, :floor, :trunc) @eval begin function ($f)(x::BigFloat) z = BigFloat() - ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &x) + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z end end @@ -834,21 +823,21 @@ end function round(x::BigFloat) z = BigFloat() - ccall((:mpfr_rint, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Cint), &z, &x, ROUNDING_MODE[]) + ccall((:mpfr_rint, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cint), z, x, ROUNDING_MODE[]) return z end function round(x::BigFloat,::RoundingMode{:NearestTiesAway}) z = BigFloat() - ccall((:mpfr_round, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}), &z, &x) + ccall((:mpfr_round, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) return z end function isinf(x::BigFloat) - return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end function isnan(x::BigFloat) - return ccall((:mpfr_nan_p, :libmpfr), Int32, (Ptr{BigFloat},), &x) != 0 + return ccall((:mpfr_nan_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 end isfinite(x::BigFloat) = !isinf(x) && !isnan(x) @@ -856,22 +845,22 @@ 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 typemax(::Type{BigFloat}) = $(BigFloat(Inf)) @eval typemin(::Type{BigFloat}) = $(BigFloat(-Inf)) function nextfloat(x::BigFloat) z = BigFloat() - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &z, &x, ROUNDING_MODE[]) - ccall((:mpfr_nextabove, :libmpfr), Int32, (Ptr{BigFloat},), &z) != 0 + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), + z, x, ROUNDING_MODE[]) + ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), z) != 0 return z end function prevfloat(x::BigFloat) z = BigFloat() - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &z, &x, ROUNDING_MODE[]) - ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ptr{BigFloat},), &z) != 0 + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), + z, x, ROUNDING_MODE[]) + ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ref{BigFloat},), z) != 0 return z end @@ -905,30 +894,63 @@ end setprecision(f::Function, precision::Integer) = setprecision(f, BigFloat, precision) -function string(x::BigFloat) - if isnan(x) || isinf(x) - return string("BigFloat(", Float64(x), ", ", precision(x), ")") - end +function string_mpfr(x::BigFloat, fmt::String) + buf = Base.StringVector(0) + s = _calculate_buffer_size!(buf, fmt, x) + resize!(buf, s) + _fill_buffer!(buf, fmt, x) + String(buf) +end + +function _calculate_buffer_size!(buf, fmt, x::BigFloat) + ccall((:mpfr_snprintf,:libmpfr), + Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + buf, 0, fmt, x) +end + +function _fill_buffer!(buf, fmt, x::BigFloat) + s = length(buf) + # we temporarily need one more item in buffer to capture null termination + resize!(buf, s + 1) + n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, fmt, x) + @assert n + 1 == length(buf) + @assert last(buf) == 0x00 + resize!(buf, s) +end - # In general, the number of decimal places needed to read back the number exactly - # is, excluding the most significant, ceil(log(10, 2^precision(x))) - k = ceil(Int32, precision(x) * 0.3010299956639812) - lng = k + Int32(8) # Add space for the sign, the most significand digit, the dot and the exponent - buf = Base.StringVector(lng + 1) - # format strings are guaranteed to contain no NUL, so we don't use Cstring - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), buf, lng + 1, "%.Re", &x) - if lng < k + 5 # print at least k decimal places - lng = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ptr{BigFloat}...), buf, "%.$(k)Re", &x) - elseif lng > k + 8 - buf = Base.StringVector(lng + 1) - lng = ccall((:mpfr_snprintf,:libmpfr), Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), buf, lng + 1, "%.Re", &x) +function _prettify_bigfloat(s::String)::String + mantissa, exponent = split(s, 'e') + if !contains(mantissa, '.') + mantissa = string(mantissa, '.') end - n = (1 <= x < 10 || -10 < x <= -1 || x == 0) ? lng - 4 : lng - return String(resize!(buf,n)) + mantissa = rstrip(mantissa, '0') + if endswith(mantissa, '.') + mantissa = string(mantissa, '0') + end + if exponent == "+00" + mantissa + 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)) -show(io::IO, b::BigFloat) = print(io, string(b)) +function show(io::IO, b::BigFloat) + if get(io, :compact, false) + 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, ()) @@ -943,15 +965,12 @@ set_emax!(x) = ccall((:mpfr_set_emax, :libmpfr), Void, (Clong,), x) set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Void, (Clong,), x) function Base.deepcopy_internal(x::BigFloat, stackdict::ObjectIdDict) - if haskey(stackdict, x) - return stackdict[x] - end - N = precision(x) + haskey(stackdict, x) && return stackdict[x] + prec = precision(x) y = BigFloat(zero(Clong), zero(Cint), zero(Clong), C_NULL) - ccall((:mpfr_init2,:libmpfr), Void, (Ptr{BigFloat}, Clong), &y, N) + ccall((:mpfr_init2,:libmpfr), Void, (Ref{BigFloat}, Clong), y, prec) finalizer(y, cglobal((:mpfr_clear, :libmpfr))) - ccall((:mpfr_set, :libmpfr), Int32, (Ptr{BigFloat}, Ptr{BigFloat}, Int32), - &y, &x, ROUNDING_MODE[]) + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), y, x, ROUNDING_MODE[]) stackdict[x] = y return y end diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 36afa13727d0b..b07572a5e98e5 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -4,7 +4,7 @@ module IteratorsMD import Base: eltype, length, size, start, done, next, first, last, in, getindex, setindex!, IndexStyle, min, max, zero, one, isless, eachindex, - ndims, iteratorsize, convert + ndims, iteratorsize, convert, show import Base: +, -, * import Base: simd_outer_range, simd_inner_length, simd_index @@ -80,6 +80,7 @@ module IteratorsMD @inline _flatten(i, I...) = (i, _flatten(I...)...) @inline _flatten(i::CartesianIndex, I...) = (i.I..., _flatten(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 @@ -132,6 +133,12 @@ module IteratorsMD return h end + # nextind with CartesianIndex + function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} + _, ni = next(CartesianRange(indices(a)), i) + return ni + end + # Iteration """ CartesianRange(sz::Dims) -> R @@ -394,7 +401,7 @@ wrapped with `LogicalIndex` upon calling `to_indices`. struct LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T} mask::A sum::Int - LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, countnz(mask)) + 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) @@ -416,7 +423,7 @@ end r = CartesianRange(indices(L.mask)) return (r, start(r), 1) end -@inline function next(L::LogicalIndex, s) +@propagate_inbounds function next(L::LogicalIndex, s) # We're looking for the n-th true element, using iterator r at state i r, i, n = s while true @@ -442,15 +449,6 @@ done(L::LogicalIndex, s) = s[3] > length(L) end @inline done(L::LogicalIndex{Int,<:BitArray}, s) = s[2] > length(L) -# Checking bounds with LogicalIndex{Int} is tricky since we allow linear indexing over trailing dimensions -@inline checkbounds_indices(::Type{Bool},IA::Tuple{},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where {N} = - checkindex(Bool, IA, I[1]) -@inline checkbounds_indices(::Type{Bool},IA::Tuple{Any},I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where {N} = - checkindex(Bool, IA[1], I[1]) -@inline function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{LogicalIndex{Int,AbstractArray{Bool,N}}}) where N - IA1, IArest = IteratorsMD.split(IA, Val(N)) - checkindex(Bool, IA1, I[1]) -end @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) = linearindices(A) == linearindices(I.mask) @inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = indices(A) == indices(I.mask) @@ -490,15 +488,8 @@ _maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i) (uncolon(inds, I), to_indices(A, _maybetail(inds), tail(I))...) const CI0 = Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}} -uncolon(inds::Tuple{}, I::Tuple{Colon}) = Slice(OneTo(1)) uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = Slice(OneTo(1)) -uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(1)) -uncolon(inds::Tuple{Any}, I::Tuple{Colon}) = Slice(inds[1]) -uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) -uncolon(inds::Tuple{Any}, I::Tuple{Colon, Vararg{CI0}}) = Slice(inds[1]) uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) -uncolon(inds::Tuple, I::Tuple{Colon}) = Slice(OneTo(trailingsize(inds))) -uncolon(inds::Tuple, I::Tuple{Colon, Vararg{CI0}}) = Slice(OneTo(trailingsize(inds))) ### From abstractarray.jl: Internal multidimensional indexing definitions ### getindex(x::Number, i::CartesianIndex{0}) = x @@ -574,9 +565,12 @@ end ## +# small helper function since we cannot use a closure in a generated function +_countnz(x) = x != 0 + @generated function findn(A::AbstractArray{T,N}) where {T,N} quote - nnzA = countnz(A) + nnzA = count(_countnz, A) @nexprs $N d->(I_d = Vector{Int}(nnzA)) k = 1 @nloops $N i A begin @@ -841,9 +835,9 @@ end @noinline function _accumulate!(op, B, A, R1, ind, R2) # Copy the initial element in each 1d vector along dimension `axis` - i = first(ind) + ii = first(ind) @inbounds for J in R2, I in R1 - B[I, i, J] = A[I, i, J] + B[I, ii, J] = A[I, ii, J] end # Accumulate @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 @@ -854,6 +848,38 @@ end ### from abstractarray.jl +""" + 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 Array{Float64,2}: + 0.0 0.0 0.0 + 0.0 0.0 0.0 + +julia> fill!(A, 2.) +2×3 Array{Float64,2}: + 2.0 2.0 2.0 + 2.0 2.0 2.0 + +julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(3), a); a[1] = 2; A +3-element Array{Array{Int64,1},1}: + [2, 1, 1] + [2, 1, 1] + [2, 1, 1] + +julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(3), f()) +3-element Array{Int64,1}: + 1 + 1 + 1 +``` +""" function fill!(A::AbstractArray{T}, x) where T xT = convert(T, x) for I in eachindex(A) @@ -914,7 +940,7 @@ circshift!(dest::AbstractArray, src, ::Tuple{}) = copy!(dest, src) """ circshift!(dest, src, shifts) -Circularly shift the data in `src`, storing the result in +Circularly shift, i.e. rotate, the data in `src`, storing the result in `dest`. `shifts` specifies the amount to shift in each dimension. The `dest` array must be distinct from the `src` array (they cannot @@ -1269,7 +1295,7 @@ end @generated function findn(B::BitArray{N}) where N quote - nnzB = countnz(B) + nnzB = count(B) I = ntuple(x->Vector{Int}(nnzB), Val($N)) if nnzB > 0 count = 1 @@ -1511,26 +1537,23 @@ function extrema(A::AbstractArray, dims) return extrema!(B, A) end -@generated function extrema!(B, A::AbstractArray{T,N}) where {T,N} - return quote - sA = size(A) - sB = size(B) - @nloops $N i B begin - AI = @nref $N A i - (@nref $N B i) = (AI, AI) - end - Bmax = sB - Istart = Int[sB[i] == 1 != sA[i] ? 2 : 1 for i = 1:ndims(A)] - @inbounds @nloops $N i d->(Istart[d]:size(A,d)) begin - AI = @nref $N A i - @nexprs $N d->(j_d = min(Bmax[d], i_{d})) - BJ = @nref $N B j - if AI < BJ[1] - (@nref $N B j) = (AI, BJ[2]) - elseif AI > BJ[2] - (@nref $N B j) = (BJ[1], AI) - end +@noinline function extrema!(B, A) + sA = size(A) + sB = size(B) + for I in CartesianRange(sB) + AI = A[I] + B[I] = (AI, AI) + end + Bmax = CartesianIndex(sB) + @inbounds @simd for I in CartesianRange(sA) + J = min(Bmax,I) + BJ = B[J] + AI = A[I] + if AI < BJ[1] + B[J] = (AI, BJ[2]) + elseif AI > BJ[2] + B[J] = (BJ[1], AI) end - return B end + return B end diff --git a/base/multimedia.jl b/base/multimedia.jl index 31e35bfbd4ad8..59fdd6aab7d20 100644 --- a/base/multimedia.jl +++ b/base/multimedia.jl @@ -44,6 +44,37 @@ false mimewritable(::MIME{mime}, x) where {mime} = method_exists(show, Tuple{IO, MIME{mime}, typeof(x)}) +""" + show(stream, 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` (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(stream, ::MIME"mime", x::T) = ...`, +where `mime` is a MIME-type string and the function body calls `write` (or similar) to write +that representation of `x` to `stream`. (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(stream, ::MIME"image/png", x::MyImage) = ...` to allow +your images to be displayed on any PNG-capable `Display` (such as IJulia). As usual, be sure +to `import Base.show` in order to add new methods to the built-in Julia function +`show`. + +The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` +output that calls `show` with 2 arguments. Therefore, this case should be handled by +defining a 2-argument `show(stream::IO, x::MyType)` method. + +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 first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. +See [`IOContext`](@ref) for details. +""" +show(stream, mime, x) + # it is convenient to accept strings instead of ::MIME show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) mimewritable(m::AbstractString, x) = mimewritable(MIME(m), x) diff --git a/base/nullable.jl b/base/nullable.jl index 8d7d6f685825a..64e0069999c8c 100644 --- a/base/nullable.jl +++ b/base/nullable.jl @@ -1,5 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +""" + NullException() + +An attempted access to a [`Nullable`](@ref) with no defined value. + +# Examples +```jldoctest +julia> a = Nullable{Int}() +Nullable{Int64}() + +julia> get(a) +ERROR: NullException() +Stacktrace: + [1] get(::Nullable{Int64}) at ./nullable.jl:118 +``` +""" struct NullException <: Exception end @@ -124,7 +140,7 @@ Nullable{String}() julia> unsafe_get(x) ERROR: UndefRefError: access to undefined reference Stacktrace: - [1] unsafe_get(::Nullable{String}) at ./nullable.jl:136 + [1] unsafe_get(::Nullable{String}) at ./nullable.jl:152 julia> x = 1 1 diff --git a/base/nullabletype.jl b/base/nullabletype.jl new file mode 100644 index 0000000000000..8c1c9c3d1d91c --- /dev/null +++ b/base/nullabletype.jl @@ -0,0 +1,9 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Nullable{T} + hasvalue::Bool + value::T + + Nullable{T}() where {T} = new(false) + Nullable{T}(value::T, hasvalue::Bool=true) where {T} = new(hasvalue, value) +end diff --git a/base/number.jl b/base/number.jl index 255ceebf9af6c..c1c22a5866423 100644 --- a/base/number.jl +++ b/base/number.jl @@ -49,6 +49,7 @@ ndims(::Type{<:Number}) = 0 length(x::Number) = 1 endof(x::Number) = 1 iteratorsize(::Type{<:Number}) = HasShape() +keys(::Number) = OneTo(1) getindex(x::Number) = x function getindex(x::Number, i::Integer) @@ -146,11 +147,26 @@ julia> flipsign(5, -3) ``` """ 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 -ctranspose(x::Number) = conj(x) +adjoint(x::Number) = conj(x) angle(z::Real) = atan2(zero(z), z) """ @@ -291,8 +307,10 @@ julia> factorial(6) 720 julia> factorial(21) -ERROR: OverflowError() -[...] +ERROR: OverflowError: 21 is too large to look up in the table +Stacktrace: + [1] factorial_lookup at ./combinatorics.jl:19 [inlined] + [2] factorial(::Int64) at ./combinatorics.jl:27 julia> factorial(21.0) 5.109094217170944e19 diff --git a/base/operators.jl b/base/operators.jl index 0228d78bc6d4a..138e6b7c2dcf1 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -50,6 +50,21 @@ end ## generic comparison ## +""" + ==(x, y) + +Generic equality operator, giving a single [`Bool`](@ref) result. Falls back to `===`. +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. + +Follows IEEE semantics for floating-point numbers. + +Collections should generally implement `==` by calling `==` recursively on all contents. + +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. +""" ==(x, y) = x === y """ @@ -95,6 +110,17 @@ isequal(x::AbstractFloat, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal( isequal(x::Real, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) isequal(x::AbstractFloat, y::Real ) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) +""" + isless(x, y) + +Test whether `x` is less than `y`, according to a canonical total order. Values that are +normally unordered, such as `NaN`, are ordered in an arbitrary but consistent fashion. This +is the default comparison used by [`sort`](@ref). Non-numeric types with a canonical total order +should implement this function. Numeric types only need to implement it if they have special +values such as `NaN`. +""" +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) @@ -714,7 +740,7 @@ fldmod1(x::T, y::T) where {T<:Integer} = (fld1(x,y), mod1(x,y)) # transpose """ - ctranspose(A) + adjoint(A) The conjugate transposition operator (`'`). @@ -725,13 +751,13 @@ julia> A = [3+2im 9+2im; 8+7im 4+6im] 3+2im 9+2im 8+7im 4+6im -julia> ctranspose(A) +julia> adjoint(A) 2×2 Array{Complex{Int64},2}: 3-2im 8-7im 9-2im 4-6im ``` """ -ctranspose(x) = conj(transpose(x)) +adjoint(x) = conj(transpose(x)) conj(x) = x # transposed multiply @@ -741,21 +767,21 @@ conj(x) = x For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ⋅B``. """ -Ac_mul_B(a,b) = ctranspose(a)*b +Ac_mul_B(a,b) = adjoint(a)*b """ A_mul_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A⋅Bᴴ``. """ -A_mul_Bc(a,b) = a*ctranspose(b) +A_mul_Bc(a,b) = a*adjoint(b) """ Ac_mul_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ Bᴴ``. """ -Ac_mul_Bc(a,b) = ctranspose(a)*ctranspose(b) +Ac_mul_Bc(a,b) = adjoint(a)*adjoint(b) """ At_mul_B(A, B) @@ -785,21 +811,21 @@ At_mul_Bt(a,b) = transpose(a)*transpose(b) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ / B``. """ -Ac_rdiv_B(a,b) = ctranspose(a)/b +Ac_rdiv_B(a,b) = adjoint(a)/b """ A_rdiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A / Bᴴ``. """ -A_rdiv_Bc(a,b) = a/ctranspose(b) +A_rdiv_Bc(a,b) = a/adjoint(b) """ Ac_rdiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ / Bᴴ``. """ -Ac_rdiv_Bc(a,b) = ctranspose(a)/ctranspose(b) +Ac_rdiv_Bc(a,b) = adjoint(a)/adjoint(b) """ At_rdiv_B(A, B) @@ -827,21 +853,21 @@ At_rdiv_Bt(a,b) = transpose(a)/transpose(b) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ`` \\ ``B``. """ -Ac_ldiv_B(a,b) = ctranspose(a)\b +Ac_ldiv_B(a,b) = adjoint(a)\b """ A_ldiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``A`` \\ ``Bᴴ``. """ -A_ldiv_Bc(a,b) = a\ctranspose(b) +A_ldiv_Bc(a,b) = a\adjoint(b) """ Ac_ldiv_Bc(A, B) For matrices or vectors ``A`` and ``B``, calculates ``Aᴴ`` \\ ``Bᴴ``. """ -Ac_ldiv_Bc(a,b) = ctranspose(a)\ctranspose(b) +Ac_ldiv_Bc(a,b) = adjoint(a)\adjoint(b) """ At_ldiv_B(A, B) diff --git a/base/options.jl b/base/options.jl index 872fa771ec291..b4a4583aabfab 100644 --- a/base/options.jl +++ b/base/options.jl @@ -3,6 +3,7 @@ # NOTE: This type needs to be kept in sync with jl_options in src/julia.h struct JLOptions quiet::Int8 + banner::Int8 julia_home::Ptr{UInt8} julia_bin::Ptr{UInt8} eval::Ptr{UInt8} @@ -23,6 +24,7 @@ struct JLOptions debug_level::Int8 check_bounds::Int8 depwarn::Int8 + warn_overwrite::Int8 can_inline::Int8 polly::Int8 fast_math::Int8 diff --git a/base/ordering.jl b/base/ordering.jl index 95b86f4be57f0..52d065b4d36ad 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -48,12 +48,12 @@ lt(o::By, a, b) = isless(o.by(a),o.by(b)) lt(o::Lt, a, b) = o.lt(a,b) lt(o::LexicographicOrdering, a, b) = lexcmp(a,b) < 0 -function lt(p::Perm, a::Integer, b::Integer) +Base.@propagate_inbounds function lt(p::Perm, a::Integer, b::Integer) da = p.data[a] db = p.data[b] lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b)) end -function lt(p::Perm{LexicographicOrdering}, a::Integer, b::Integer) +Base.@propagate_inbounds function lt(p::Perm{LexicographicOrdering}, a::Integer, b::Integer) c = lexcmp(p.data[a], p.data[b]) c != 0 ? c < 0 : a < b end diff --git a/base/parse.jl b/base/parse.jl index 87b7c6aed6c92..91e1d0f5ab422 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -4,6 +4,29 @@ import Base.Checked: add_with_overflow, mul_with_overflow ## string to integer functions ## +""" + parse(type, str, [base]) + +Parse a string as a number. If the type is an integer type, then a base can be specified +(the default is 10). If the type is a floating point type, the string is parsed as a decimal +floating point number. If the string does not contain a valid number, an error is raised. + +```jldoctest +julia> parse(Int, "1234") +1234 + +julia> parse(Int, "1234", 5) +194 + +julia> parse(Int, "afc", 16) +2812 + +julia> parse(Float64, "1.2e-3") +0.0012 +``` +""" +parse(T::Type, str, base=Int) + function parse(::Type{T}, c::Char, base::Integer=36) where T<:Integer a::Int = (base <= 36 ? 10 : 36) 2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) @@ -217,6 +240,35 @@ float(a::AbstractArray{<:AbstractString}) = map!(float, similar(a,typeof(float(0 ## interface to parser ## +""" + ParseError(msg) + +The expression passed to the `parse` function could not be interpreted as a valid Julia +expression. +""" +mutable struct ParseError <: Exception + msg::AbstractString +end + +""" + parse(str, start; greedy=true, raise=true) + +Parse the expression string and return an expression (which could later be passed to eval +for execution). `start` is the index of the first character to start parsing. 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. + +```jldoctest +julia> parse("x = 3, y = 5", 7) +(:(y = 5), 13) + +julia> parse("x = 3, y = 5", 5) +(:((3, y) = 5), 13) +``` +""" function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=true) # pos is one based byte offset. # returns (expr, end_pos). expr is () in case of parse error. @@ -234,6 +286,30 @@ function parse(str::AbstractString, pos::Int; greedy::Bool=true, raise::Bool=tru return ex, pos+1 # C is zero-based, Julia is 1-based end +""" + parse(str; raise=true) + +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. + +```jldoctest +julia> parse("x = 3") +:(x = 3) + +julia> parse("x = ") +:($(Expr(:incomplete, "incomplete: premature end of input"))) + +julia> parse("1.0.2") +ERROR: ParseError("invalid numeric constant \\\"1.0.\\\"") +Stacktrace: +[...] + +julia> parse("1.0.2"; raise = false) +:($(Expr(:error, "invalid numeric constant \"1.0.\""))) +``` +""" function parse(str::AbstractString; raise::Bool=true) ex, pos = parse(str, 1, greedy=true, raise=raise) if isa(ex,Expr) && ex.head === :error diff --git a/base/path.jl b/base/path.jl index c042bef833e1f..9934602737e31 100644 --- a/base/path.jl +++ b/base/path.jl @@ -88,6 +88,7 @@ end Determines whether a path is absolute (begins at the root directory). +# Examples ```jldoctest julia> isabspath("/home") true @@ -103,6 +104,7 @@ isabspath(path::AbstractString) Determines whether a path refers to a directory (for example, ends with a path separator). +# Examples ```jldoctest julia> isdirpath("/home") false @@ -118,6 +120,7 @@ isdirpath(path::String) = ismatch(path_directory_re, splitdrive(path)[2]) Split a path into a tuple of the directory name and file name. +# Examples ```jldoctest julia> splitdir("/home/myuser") ("/home", "myuser") @@ -136,6 +139,7 @@ end Get the directory part of a path. +# Examples ```jldoctest julia> dirname("/home/myuser") "/home" @@ -150,6 +154,7 @@ See also: [`basename`](@ref) Get the file name part of a path. +# Examples ```jldoctest julia> basename("/home/myuser/example.jl") "example.jl" @@ -166,6 +171,7 @@ If the last component of a path contains a dot, split the path into everything b dot and everything including and after the dot. Otherwise, return a tuple of the argument unmodified and the empty string. +# Examples ```jldoctest julia> splitext("/home/myuser/example.jl") ("/home/myuser/example", ".jl") @@ -194,11 +200,13 @@ joinpath(a::AbstractString) = a """ joinpath(parts...) -> AbstractString -Join path components into a full path. If some argument is an absolute path, then prior -components are dropped. +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. +# Examples ```jldoctest -julia> joinpath("/home/myuser","example.jl") +julia> joinpath("/home/myuser", "example.jl") "/home/myuser/example.jl" ``` """ @@ -208,7 +216,7 @@ function joinpath(a::String, b::String) isabspath(b) && return b A, a = splitdrive(a) B, b = splitdrive(b) - !isempty(B) && A != B && throw(ArgumentError("drive mismatch: $A$a $B$b")) + !isempty(B) && A != B && return string(B,b) C = isempty(B) ? A : B isempty(a) ? string(C,b) : ismatch(path_separator_re, a[end:end]) ? string(C,a,b) : @@ -221,6 +229,7 @@ joinpath(a::AbstractString, b::AbstractString) = joinpath(String(a), String(b)) Normalize a path, removing "." and ".." entries. +# Examples ```jldoctest julia> normpath("/home/myuser/../example.jl") "/home/example.jl" @@ -329,6 +338,7 @@ expanduser(path::AbstractString) = path # on windows, ~ means "temporary file" else function expanduser(path::AbstractString) i = start(path) + if done(path,i) return path end c, i = next(path,i) if c != '~' return path end if done(path,i) return homedir() end diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index 58487eac71db1..c2bce41e075b0 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -4,8 +4,7 @@ module Entry import Base: thispatch, nextpatch, nextminor, nextmajor, check_new_version import ..Reqs, ..Read, ..Query, ..Resolve, ..Cache, ..Write, ..Dir -import ...LibGit2 -importall ...LibGit2 +using ...LibGit2 import ...Pkg.PkgError using ..Types @@ -615,7 +614,7 @@ function build(pkg::AbstractString, build_file::AbstractString, errfile::Abstrac --eval $code ``` - success(pipeline(cmd, stderr=STDERR)) + success(pipeline(cmd, stdout=STDOUT, stderr=STDERR)) end function build!(pkgs::Vector, seen::Set, errfile::AbstractString) @@ -719,6 +718,7 @@ function test!(pkg::AbstractString, --color=$(Base.have_color ? "yes" : "no") --compilecache=$(Bool(Base.JLOptions().use_compilecache) ? "yes" : "no") --check-bounds=yes + --warn-overwrite=yes --startup-file=$(Base.JLOptions().startupfile != 2 ? "yes" : "no") $test_path ``` diff --git a/base/pkg/read.jl b/base/pkg/read.jl index cd1f4461d1259..23945d4330210 100644 --- a/base/pkg/read.jl +++ b/base/pkg/read.jl @@ -134,7 +134,7 @@ function installed_version(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::D end isempty(head) && return typemin(VersionNumber) - vers = collect(keys(filter((ver,info)->info.sha1==head, avail))) + vers = collect(keys(filter(#=ver,info=#p->p[2].sha1==head, avail))) !isempty(vers) && return maximum(vers) cache = Cache.path(pkg) diff --git a/base/pkg/resolve/versionweight.jl b/base/pkg/resolve/versionweight.jl index 440483181c4c9..38c091cafc12f 100644 --- a/base/pkg/resolve/versionweight.jl +++ b/base/pkg/resolve/versionweight.jl @@ -74,7 +74,7 @@ struct VWPreBuildItem i::Int end VWPreBuildItem() = VWPreBuildItem(0, HierarchicalValue(Int), 0) -VWPreBuildItem(i::Int) = VWPreBuildItem(1, HierarchicalValue(Int), i) +VWPreBuildItem(i::Integer) = VWPreBuildItem(1, HierarchicalValue(Int), i) VWPreBuildItem(s::String) = VWPreBuildItem(1, HierarchicalValue(Int[s...]), 0) Base.zero(::Type{VWPreBuildItem}) = VWPreBuildItem() @@ -105,7 +105,7 @@ end const _vwprebuild_zero = VWPreBuild(0, HierarchicalValue(VWPreBuildItem)) -function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Int,String}}}) +function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Integer,String}}}) isempty(desc) && return _vwprebuild_zero desc == ("",) && return VWPreBuild(ispre ? -1 : 1, HierarchicalValue(VWPreBuildItem[])) nonempty = ispre ? -1 : 0 diff --git a/base/pkg/write.jl b/base/pkg/write.jl index ffc2c6f84fb50..bf5078a05c0ea 100644 --- a/base/pkg/write.jl +++ b/base/pkg/write.jl @@ -2,8 +2,8 @@ module Write -import ...LibGit2, ..Cache, ..Read, ...Pkg.PkgError -importall ...LibGit2 +import ..Cache, ..Read, ...Pkg.PkgError +using ...LibGit2 function prefetch(pkg::AbstractString, sha1::AbstractString) isempty(Cache.prefetch(pkg, Read.url(pkg), sha1)) && return diff --git a/base/pointer.jl b/base/pointer.jl index fee9420a39950..2e3e0e957d8e5 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -32,6 +32,30 @@ convert(::Type{Ptr{T}}, p::Ptr{T}) where {T} = p convert(::Type{Ptr{T}}, p::Ptr) where {T} = bitcast(Ptr{T}, p) # 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) = convert(Ptr{UInt8}, pointer_from_objref(s)+sizeof(Int)) diff --git a/base/poll.jl b/base/poll.jl index 87b157b1208b3..7f1ef55034cd7 100644 --- a/base/poll.jl +++ b/base/poll.jl @@ -12,7 +12,7 @@ export import Base: @handle_as, wait, close, uvfinalize, eventloop, notify_error, stream_wait, _sizeof_uv_poll, _sizeof_uv_fs_poll, _sizeof_uv_fs_event, _uv_hook_close, - associate_julia_struct, disassociate_julia_struct, | + associate_julia_struct, disassociate_julia_struct, isreadable, iswritable, | if Sys.iswindows() import Base.WindowsRawSocket end @@ -82,9 +82,10 @@ mutable struct PollingFileWatcher interval::UInt32 notify::Condition active::Bool + busy_polling::Int32 function PollingFileWatcher(file::AbstractString, interval::Float64=5.007) # same default as nodejs handle = Libc.malloc(_sizeof_uv_fs_poll) - this = new(handle, file, round(UInt32, interval * 1000), Condition(), false) + this = new(handle, file, round(UInt32, interval * 1000), Condition(), false, 0) associate_julia_struct(handle, this) err = ccall(:uv_fs_poll_init, Int32, (Ptr{Void}, Ptr{Void}), eventloop(), handle) if err != 0 @@ -250,7 +251,7 @@ end function _uv_hook_close(uv::PollingFileWatcher) uv.handle = C_NULL uv.active = false - notify(uv.notify, (StatStruct(), StatStruct())) + notify(uv.notify, (StatStruct(), EOFError())) nothing end @@ -299,11 +300,10 @@ end function uv_fspollcb(handle::Ptr{Void}, status::Int32, prev::Ptr, curr::Ptr) t = @handle_as handle PollingFileWatcher - if status != 0 - notify_error(t.notify, UVError("PollingFileWatcher", status)) - else + if status == 0 || status != t.busy_polling + t.busy_polling = status prev_stat = StatStruct(convert(Ptr{UInt8}, prev)) - curr_stat = StatStruct(convert(Ptr{UInt8}, curr)) + curr_stat = (status == 0) ? StatStruct(convert(Ptr{UInt8}, curr)) : UVError("PollingFileWatcher", status) notify(t.notify, (prev_stat, curr_stat)) end nothing @@ -511,15 +511,20 @@ function watch_file(s::AbstractString, timeout_s::Real=-1) end """ - poll_file(path::AbstractString, interval_s::Real=5.007, timeout_s::Real=-1) -> (previous::StatStruct, current::StatStruct) + 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 `StatStruct` objects `(previous, current)` when a change is detected. +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 `mtime(prev) != mtime(current)` to detect +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. """ @@ -532,7 +537,12 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R @schedule begin try - result = wait(pfw) + statdiff = wait(pfw) + if statdiff[1] == StatStruct() && isa(statdiff[2], UVError) + # file didn't initially exist, continue watching for it to be created (or the error to change) + statdiff = wait(pfw) + end + result = statdiff catch e notify_error(wt, e) return @@ -543,7 +553,7 @@ function poll_file(s::AbstractString, interval_seconds::Real=5.007, timeout_s::R wait(wt) if result === :timeout - return (StatStruct(), StatStruct()) + return (StatStruct(), EOFError()) end return result else diff --git a/base/precompile.jl b/base/precompile.jl index 03a87add90607..1f41808501d51 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -492,7 +492,7 @@ precompile(Tuple{typeof(Base.read), Base.TTY, Type{UInt8}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{UInt8, 1}, Tuple{Base.UnitRange{Int64}}}) precompile(Tuple{typeof(Base.deleteat!), Array{UInt8, 1}, Base.UnitRange{Int64}}) precompile(Tuple{typeof(Base.splice!), Array{UInt8, 1}, Base.UnitRange{Int64}, Array{UInt8, 1}}) -precompile(Tuple{typeof(Base.LineEdit.splice_buffer!), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.UnitRange{Int64}, String}) +precompile(Tuple{typeof(Base.LineEdit.edit_splice!), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.Pair{Int,Int}, String}) precompile(Tuple{typeof(Base.LineEdit.refresh_multi_line), Base.Terminals.TerminalBuffer, Base.LineEdit.PromptState}) precompile(Tuple{typeof(Base.LineEdit.edit_insert), Base.GenericIOBuffer{Array{UInt8, 1}}, String}) precompile(Tuple{typeof(Base.LineEdit.edit_insert), Base.LineEdit.PromptState, String}) diff --git a/base/printf.jl b/base/printf.jl index 47ade06597171..dff3d28412df7 100644 --- a/base/printf.jl +++ b/base/printf.jl @@ -57,7 +57,7 @@ function parse(s::AbstractString) i = 1 while i < length(list) if isa(list[i],AbstractString) - for j = i+1:length(list) + for outer j = i+1:length(list) if !isa(list[j],AbstractString) j -= 1 break @@ -1102,7 +1102,7 @@ ini_hex(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::In ini_HEX(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) ini_hex(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) ini_HEX(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) = bigfloat_printf(out, d, flags, width, precision, c) -function bigfloat_printf(out, d, flags::String, width::Int, precision::Int, c::Char) +function bigfloat_printf(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char) fmt_len = sizeof(flags)+4 if width > 0 fmt_len += ndigits(width) @@ -1130,8 +1130,8 @@ function bigfloat_printf(out, d, flags::String, width::Int, precision::Int, c::C @assert length(printf_fmt) == fmt_len bufsiz = length(DIGITS) lng = ccall((:mpfr_snprintf,:libmpfr), Int32, - (Ptr{UInt8}, Culong, Ptr{UInt8}, Ptr{BigFloat}...), - DIGITS, bufsiz, printf_fmt, &d) + (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + DIGITS, bufsiz, printf_fmt, d) lng > 0 || error("invalid printf formatting for BigFloat") unsafe_write(out, pointer(DIGITS), min(lng, bufsiz-1)) return (false, ()) @@ -1188,13 +1188,13 @@ function _printf(macroname, io, fmt, args) end unshift!(blk.args, :(out = $io)) - Expr(:let, blk) + Expr(:let, Expr(:block), blk) end """ @printf([io::IOStream], "%Fmt", args...) -Print `args` using C `printf()` style format specification string, with some caveats: +Print `args` using C `printf` style format specification string, with some 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 @@ -1241,7 +1241,7 @@ macro sprintf(args...) isa(args[1], AbstractString) || is_str_expr(args[1]) || throw(ArgumentError("@sprintf: first argument must be a format string")) letexpr = _printf("@sprintf", :(IOBuffer()), args[1], args[2:end]) - push!(letexpr.args[1].args, :(String(take!(out)))) + push!(letexpr.args[2].args, :(String(take!(out)))) letexpr end diff --git a/base/process.jl b/base/process.jl index 31791b8d83af8..cc188a084e675 100644 --- a/base/process.jl +++ b/base/process.jl @@ -550,6 +550,11 @@ spawn_opts_inherit(stdios::StdIOSet) = (stdios,) spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::Redirectable=RawFD(2), args...) = ((in, out, err), args...) +""" + spawn(command) + +Run a command object asynchronously, returning the resulting `Process` object. +""" spawn(cmds::AbstractCmd, args...; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) = spawn(cmds, spawn_opts_swallow(args...)...; chain=chain) @@ -817,7 +822,7 @@ wait(x::ProcessChain) = for p in x.processes; wait(p); end 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, :endof, :start, :eachindex, :eltype, :first, :last) +for f in (:length, :endof, :start, :keys, :eltype, :first, :last) @eval $f(cmd::Cmd) = $f(cmd.exec) end for f in (:next, :done, :getindex) diff --git a/base/promotion.jl b/base/promotion.jl index ca79a451bfa07..57c18a2e00293 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -2,6 +2,11 @@ ## type join (closest common ancestor, or least upper bound) ## +""" + typejoin(T, S) + +Compute a type that contains both `T` and `S`. +""" typejoin() = (@_pure_meta; Bottom) typejoin(@nospecialize(t)) = (@_pure_meta; t) typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) @@ -163,6 +168,15 @@ function promote_type(::Type{T}, ::Type{S}) where {T,S} 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{<:Any}, ::Type{<:Any}) = Bottom promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@_inline_meta; promote_type(T,S)) @@ -170,21 +184,33 @@ promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@ # case use typejoin on the original types instead. promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@_inline_meta; typejoin(T, S)) -promote() = () -promote(x) = (x,) -function promote(x::T, y::S) where {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. + +# Examples +```jldoctest +julia> promote(Int8(1), Float16(4.5), Float32(4.1)) +(1.0f0, 4.5f0, 4.1f0) +``` +""" +function promote end + +function _promote(x::T, y::S) where {T,S} @_inline_meta (convert(promote_type(T,S),x), convert(promote_type(T,S),y)) end promote_typeof(x) = typeof(x) promote_typeof(x, xs...) = (@_inline_meta; promote_type(typeof(x), promote_typeof(xs...))) -function promote(x, y, z) +function _promote(x, y, z) @_inline_meta (convert(promote_typeof(x,y,z), x), convert(promote_typeof(x,y,z), y), convert(promote_typeof(x,y,z), z)) end -function promote(x, y, zs...) +function _promote(x, y, zs...) @_inline_meta (convert(promote_typeof(x,y,zs...), x), convert(promote_typeof(x,y,zs...), y), @@ -213,39 +239,38 @@ promote_to_supertype(::Type{T}, ::Type{S}, ::Type{S}) where {T<:Number,S<:Number promote_to_supertype(::Type{T}, ::Type{S}, ::Type) where {T<:Number,S<:Number} = error("no promotion exists for ", T, " and ", S) -# promotion with a check for circularity. Can be used to catch what -# would otherwise become StackOverflowErrors. -function promote_noncircular(x, y) +promote() = () +promote(x) = (x,) + +function promote(x, y) @_inline_meta - px, py = promote(x, y) - not_all_sametype((x,px), (y,py)) + px, py = _promote(x, y) + not_sametype((x,y), (px,py)) px, py end -function promote_noncircular(x, y, z) +function promote(x, y, z) @_inline_meta - px, py, pz = promote(x, y, z) - not_all_sametype((x,px), (y,py), (z,pz)) + px, py, pz = _promote(x, y, z) + not_sametype((x,y,z), (px,py,pz)) px, py, pz end -function promote_noncircular(x, y, z, a...) - p = promote(x, y, z, a...) - not_all_sametype(map(identity, (x, y, z, a...), p)) +function promote(x, y, z, a...) + p = _promote(x, y, z, a...) + not_sametype((x, y, z, a...), p) p end -not_all_sametype(x, y) = nothing -not_all_sametype(x, y, z) = nothing -not_all_sametype(x::Tuple{S,S}, y::Tuple{T,T}) where {S,T} = sametype_error(x[1], y[1]) -not_all_sametype(x::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}) where {R,S,T} = sametype_error(x[1], y[1], z[1]) -function not_all_sametype(::Tuple{R,R}, y::Tuple{S,S}, z::Tuple{T,T}, args...) where {R,S,T} - @_inline_meta - not_all_sametype(y, z, args...) -end -not_all_sametype() = error("promotion failed to change any input types") -function sametype_error(input...) + +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_meta - error("circular method definition: promotion of types ", + error("promotion of types ", join(map(x->string(typeof(x)), input), ", ", " and "), - " failed to change any input types") + " failed to change any arguments") end +(x::Number, y::Number) = +(promote(x,y)...) @@ -362,3 +387,5 @@ minmax(x::Real) = (x, x) max(x::T, y::T) where {T<:Real} = select_value(y < x, x, y) min(x::T, y::T) where {T<:Real} = select_value(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) diff --git a/base/random/RNGs.jl b/base/random/RNGs.jl new file mode 100644 index 0000000000000..ae5c4d32d9ce5 --- /dev/null +++ b/base/random/RNGs.jl @@ -0,0 +1,464 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## RandomDevice + +const BoolBitIntegerType = Union{Type{Bool},Base.BitIntegerType} +const BoolBitIntegerArray = Union{Array{Bool},Base.BitIntegerArray} + +if Sys.iswindows() + struct RandomDevice <: AbstractRNG + buffer::Vector{UInt128} + + RandomDevice() = new(Vector{UInt128}(1)) + end + + function rand(rd::RandomDevice, T::BoolBitIntegerType) + rand!(rd, rd.buffer) + @inbounds return rd.buffer[1] % T + end + + function rand!(rd::RandomDevice, A::BoolBitIntegerArray) + ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Void}, UInt32), + A, sizeof(A)) + A + end +else # !windows + struct RandomDevice <: AbstractRNG + file::IOStream + unlimited::Bool + + RandomDevice(unlimited::Bool=true) = + new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) + end + + rand(rd::RandomDevice, T::BoolBitIntegerType) = read( rd.file, T) + rand!(rd::RandomDevice, A::BoolBitIntegerArray) = read!(rd.file, A) +end # os-test + +""" + 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. +""" +RandomDevice + +RandomDevice(::Void) = RandomDevice() +srand(rng::RandomDevice) = rng + +### generation of floats + +@inline rand(r::RandomDevice, I::FloatInterval) = rand_generic(r, I) + + +## MersenneTwister + +const MTCacheLength = dsfmt_get_min_array_size() + +mutable struct MersenneTwister <: AbstractRNG + seed::Vector{UInt32} + state::DSFMT_state + vals::Vector{Float64} + idx::Int + + function MersenneTwister(seed, state, vals, idx) + length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength || + throw(DomainError((length(vals), idx), + "`length(vals)` and `idx` must be consistent with $MTCacheLength")) + new(seed, state, vals, idx) + end +end + +MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = + MersenneTwister(seed, state, zeros(Float64, MTCacheLength), MTCacheLength) + +""" + 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 [`srand`](@ref) function for reseeding an already existing +`MersenneTwister` object. + + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> x1 = rand(rng, 2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> rng = MersenneTwister(1234); + +julia> x2 = rand(rng, 2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> x1 == x2 +true +``` +""" +MersenneTwister(seed=nothing) = + srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) + +function copy!(dst::MersenneTwister, src::MersenneTwister) + copy!(resize!(dst.seed, length(src.seed)), src.seed) + copy!(dst.state, src.state) + copy!(dst.vals, src.vals) + dst.idx = src.idx + dst +end + +copy(src::MersenneTwister) = + MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), src.idx) + +==(r1::MersenneTwister, r2::MersenneTwister) = + r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && + r1.idx == r2.idx + +hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) + + +### low level API + +@inline mt_avail(r::MersenneTwister) = MTCacheLength - r.idx +@inline mt_empty(r::MersenneTwister) = r.idx == MTCacheLength +@inline mt_setfull!(r::MersenneTwister) = r.idx = 0 +@inline mt_setempty!(r::MersenneTwister) = r.idx = MTCacheLength +@inline mt_pop!(r::MersenneTwister) = @inbounds return r.vals[r.idx+=1] + +function gen_rand(r::MersenneTwister) + dsfmt_fill_array_close1_open2!(r.state, pointer(r.vals), length(r.vals)) + mt_setfull!(r) +end + +@inline reserve_1(r::MersenneTwister) = (mt_empty(r) && gen_rand(r); nothing) +# `reserve` allows one to call `rand_inbounds` n times +# precondition: n <= MTCacheLength +@inline reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) + + +### 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 + println(STDERR, + "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") + seed = reinterpret(UInt64, time()) + seed = hash(seed, UInt64(getpid())) + try + seed = hash(seed, parse(UInt64, + read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], + 16)) + end + return make_seed(seed) + 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 + +#### srand() + +function srand(r::MersenneTwister, seed::Vector{UInt32}) + copy!(resize!(r.seed, length(seed)), seed) + dsfmt_init_by_array(r.state, r.seed) + mt_setempty!(r) + return r +end + +srand(r::MersenneTwister=GLOBAL_RNG) = srand(r, make_seed()) +srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) +srand(seed::Union{Integer,Vector{UInt32}}) = srand(GLOBAL_RNG, seed) + + +### Global RNG (must be defined after srand) + +const GLOBAL_RNG = MersenneTwister(0) + + +### generation + +#### helper functions + +# precondition: !mt_empty(r) +@inline rand_inbounds(r::MersenneTwister, ::Close1Open2_64) = mt_pop!(r) +@inline rand_inbounds(r::MersenneTwister, ::CloseOpen_64) = + rand_inbounds(r, Close1Open2()) - 1.0 +@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen()) + +@inline rand_ui52_raw_inbounds(r::MersenneTwister) = + reinterpret(UInt64, rand_inbounds(r, Close1Open2())) +@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) + +@inline function rand_ui2x52_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) +end + +@inline function rand_ui104_raw(r::MersenneTwister) + reserve(r, 2) + rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) +end + +rand_ui10_raw(r::MersenneTwister) = rand_ui52_raw(r) +rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) + +#### floats + +@inline rand(r::MersenneTwister, I::FloatInterval_64) = (reserve_1(r); rand_inbounds(r, I)) + +@inline rand(r::MersenneTwister, I::FloatInterval) = rand_generic(r, I) + +#### integers + +@inline rand(r::MersenneTwister, + ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = + rand_ui52_raw(r) % T + +function rand(r::MersenneTwister, ::Type{UInt64}) + reserve(r, 2) + rand_ui52_raw_inbounds(r) << 32 ⊻ rand_ui52_raw_inbounds(r) +end + +function rand(r::MersenneTwister, ::Type{UInt128}) + reserve(r, 3) + xor(rand_ui52_raw_inbounds(r) % UInt128 << 96, + rand_ui52_raw_inbounds(r) % UInt128 << 48, + rand_ui52_raw_inbounds(r)) +end + +rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) +rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) + +#### arrays of floats + +function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, + n=length(A), I::FloatInterval_64=CloseOpen()) + # what follows is equivalent to this simple loop but more efficient: + # for i=1:n + # @inbounds A[i] = rand(r, I) + # end + m = 0 + 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 + +rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) + +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::CloseOpen_64) = + dsfmt_fill_array_close_open!(s, A, n) + +fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Close1Open2_64) = + dsfmt_fill_array_close1_open2!(s, A, n) + +function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), + I::FloatInterval_64=CloseOpen()) + # 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_copy! 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 + if n > length(A) + throw(BoundsError(A,n)) + end + n2 = (n-2) ÷ 2 * 2 + if n2 < dsfmt_get_min_array_size() + rand_AbstractArray_Float64!(r, A, n, I) + else + pA = pointer(A) + align = Csize_t(pA) % 16 + if align > 0 + pA2 = pA + 16 - align + fill_array!(r.state, pA2, n2, I) # generate the data in-place, but shifted + unsafe_copy!(pA, pA2, n2) # move the data to the beginning of the array + else + fill_array!(r.state, pA, n2, I) + end + for i=n2+1:n + @inbounds A[i] = rand(r, I) + end + end + A +end + +@inline mask128(u::UInt128, ::Type{Float16}) = + (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 + +@inline mask128(u::UInt128, ::Type{Float32}) = + (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 + +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, + ::Close1Open2_64) + T = eltype(A) + n = length(A) + n128 = n * sizeof(T) ÷ 16 + rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), + 2*n128, Close1Open2()) + A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) + @inbounds 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 + for i in 16*n128÷sizeof(T)+1:n + @inbounds A[i] = rand(r, T) + oneunit(T) + end + A +end + +function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::CloseOpen_64) + rand!(r, A, Close1Open2()) + 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 + +rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = + rand!(r, A, CloseOpen()) + +#### arrays of integers + +function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) + if n > length(A) + throw(BoundsError(A,n)) + end + Af = unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2n) + i = n + while true + rand!(r, Af, 2i, Close1Open2()) + n < 5 && break + i = 0 + @inbounds 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_ui2x52_raw(r) + for i = 1:n + @inbounds A[i] ⊻= u << (12*i) + end + end + A +end + +# A::Array{UInt128} will match the specialized method above +function rand!(r::MersenneTwister, A::Base.BitIntegerArray) + n = length(A) + T = eltype(A) + n128 = n * sizeof(T) ÷ 16 + rand!(r, unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128)) + for i = 16*n128÷sizeof(T)+1:n + @inbounds A[i] = rand(r, T) + end + A +end + +#### from a range + +@inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer + while true + x = randfun(r) & mask + x <= u && return x + end +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Base.BitInteger64,Bool} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = last(r) % UInt64 - first(r) % UInt64 + bw = (64 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt64 << bw) - (1 % UInt64) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt64), m, mask) + (x + first(r) % UInt64) % T +end + +function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt128} + isempty(r) && throw(ArgumentError("range must be non-empty")) + m = (last(r)-first(r)) % UInt128 + bw = (128 - leading_zeros(m)) % UInt # bit-width + mask = (1 % UInt128 << bw) - (1 % UInt128) + x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : + bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : + rand_lteq(rng, rng->rand(rng, UInt128), m, mask) + x % T + first(r) +end + + +### randjump + +""" + randjump(r::MersenneTwister, jumps::Integer, + [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} + +Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The +first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are +initialized such that a state of the RNG object in the array would be moved forward (without +generating numbers) from a previous RNG object array element on a particular number of steps +encoded by the jump polynomial `jumppoly`. + +Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. +""" +function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) + mts = MersenneTwister[] + push!(mts, mt) + for i in 1:jumps-1 + cmt = mts[end] + push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) + end + return mts +end + +randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) diff --git a/base/random/dSFMT.jl b/base/random/dSFMT.jl new file mode 100644 index 0000000000000..2061cc54f9741 --- /dev/null +++ b/base/random/dSFMT.jl @@ -0,0 +1,179 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module dSFMT + +import Base: copy, copy!, ==, hash + +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!, + win32_SystemFunction036! + +"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 + + +mutable 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) = (copy!(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), + Void, + (Ptr{Void}, UInt32,), + s.val, seed) +end + +function dsfmt_init_by_array(s::DSFMT_state, seed::Vector{UInt32}) + ccall((:dsfmt_init_by_array,:libdSFMT), + Void, + (Ptr{Void}, 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), + Void, + (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), + Void, + (Ptr{Void}, 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), + Void, + (Ptr{Void}, Ptr{Float64}, Int), + s.val, A, n) +end + + +## dSFMT jump + +function dsfmt_jump(s::DSFMT_state, jp::AbstractString) + val = s.val + nval = length(val) + index = val[nval - 1] + work = zeros(UInt64, JN32 >> 1) + dsfmt = Vector{UInt64}(nval >> 1) + ccall(:memcpy, Ptr{Void}, (Ptr{UInt64}, Ptr{Int32}, Csize_t), + dsfmt, val, (nval - 1) * sizeof(Int32)) + dsfmt[end] = UInt64(N*2) + + for c in jp + bits = parse(UInt8,c,16) + for j in 1:4 + (bits & 0x01) != 0x00 && dsfmt_jump_add!(work, dsfmt) + bits = bits >> 0x01 + dsfmt_jump_next_state!(dsfmt) + end + end + + work[end] = index + return DSFMT_state(reinterpret(Int32, work)) +end + +function dsfmt_jump_add!(dest::Vector{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 JPOLY1e21 = "e172e20c5d2de26b567c0cace9e7c6cc4407bd5ffcd22ca59d37b73d54fdbd937cd3abc6f502e8c186dbd4f1a06b9e2b894f31be77424f94dddfd5a45888a84ca66eeeb242eefe6764ed859dafccae7a6a635b3a63fe9dfbbd5f2d3f2610d39388f53060e84edae75be4f4f2272c0f1f26d1231836ad040ab091550f8a3a5423fb3ab83e068fe2684057f15691c4dc757a3aee4bca8595bf1ad03500d9620a5dbe3b2d64380694895d2f379ca928238293ea267ce14236d5be816a61f018fe4f6bc3c9865f5d4d4186e320ab653d1f3c035ae83e2ad725648a67d3480331e763a1dcdfb5711b56796170b124f5febd723a664a2deefbfa9999d922a108b0e683582ae8d3baacb5bb56683405ea9e6e0d71ddb24b2229c72bb9d07061f2d1fa097ade823b607a2029d6e121ae09d93de01a154199e8e6a6e77c970bda72ba8079b2b3a15dd494a3188b1d94a25ae108a8a5bd0b050e6ce64a365a21420e07fdeebecae02eb68a4304b59283055d22c27d680ea35952834d828c9b9b9dd1a886b4f7fe82fe8f2a962e1e5390e563dc281c799aee2a441b7a813facb6ff5e94c059710dcfe7e6b1635e21ae0dc878dd5f7cc0e1101a74452495a67d23a2672c939f32c81d4a2611073990e92a084cc3a62fd42ee566f29d963a9cc5100ccd0a200f49ce0a74fa891efa1b974d342b7fedf9269e40d9b34e3c59c3d37201aecd5a04f4ae3d0c9a68c7ab78c662390e4cf36cb63ea3539c442efd0bf4aace4b8c8bde93c3d84b4d6290adfae1c5e3fcd457b6f3159e501f17b72ff6bc13d6bf61fbdafabefd16ac1dae0bca667e4e16a2b800732f1d0a9274c8a4c6cccd2db62fc275dc308c31c11cd6fda78de2f81f0e542b76b42b2cc09ed8f965d94c714c9918064f53af5379cfbbc31edf9cbce694f63a75f122048de6e57b094908f749661456813a908027f5d8397ab7962bf75ac779a3e1b7ae3fbc93397a67b486bb849befff1de6162ef2819715a88f41881e366ace692a900796a2806393898dd1750ac2b4ca3d34ca48942322fb6375f0c9a00c9701048ee8d7d7a17e11739177a7ad5027556e85835daf8594d84a97fe6621c0fce1495ae6ab8676cdc992d247acf5a4e5ec8c4755fde28117228d2c3ecf89edb91e93d949e2174924572265e36d176d082ed1be884e51d885ba3cda175c51edcee5042eaf519d292aa05aa4185b03858d710a9d0880b3d4e5111f858a52fe352cbe0a24f06a3d977ae2eb85e2a03a68131d0ab91dac4941067cf90ecd0fce156bcd40b8968cd4aa11e0b4353b14508d79d13ac00af4a4d452496b7f2393699889aa1e508427dbf0be3db91d955feb51e559af57640c6b3f9d5f95609852c28f9462a9869dd93acbdb1aafb2381ebb886a0b3fcec278f8bb0f62c23e157e49b89245b0881268ce594acbddd3605b9eaa77c9ff513e0dbad514914136d96fe2843fe2b4e886a0b718a9b8d1132132110618d0d3595da284cd2a4c9d09386199e4f4d7723983d3a374b51cf20dac5cabb4ff7e7197c2ebd9318463409baa583d6a6115c1b768282ff37b0fe152c97671e400d5ccba7d6875df0bf95c5d91257fedb124de393f31908d0e36251326aa29dd5be86291c80b4bf78f419ec151eeaeff643a58b48ab35ad2cd2c0b77b1965966ef3db6b6373cb2c4b590cef2f16f4d6f62f13a6cbf1a481565b5935edd4e76f7b6a8fd0d74bc336b40a803aec38125c006c877dfdcdb9ba2b7aecab5cafe6076e024c73e3567adf97f607a71d180402c22a20a8388f517484cc4198f97c2fe4f3407e0dc577e61f0f71354aa601cf4e3e42e1edd8722d50f5af3441f68caa568cc1c3a19956c1233f265bb47236afab24ee42b27b0042b90693d77c1923147360ae6503f6ba6abbc9dd52a7b4c36a3b6b55f6a80cfa7f101dd9f1bfc7d7eaf09a5d636b510228f245bfb37b4625025d2c911435cdf6f878113753e0804ab8ecab870ad733b9728d7636b17578b41239393e7de47cbce871137d2b61729dda67b2b84cd3363aad64c5dd5bd172f1f091305b1ff78982abe7dab1588036d097cf497e300e6c78a926048febd1b9462c07f5868928357b74297c87f503056b89f786d22a538b6702e290bca04639a0f1d0939b67f409e5e58e472a6a07fa543e2531c2567ec73c41f6769b6ba94c5aa0a030d006f5b6b1c5fb218b86a8f63a48bc867466f20f699859e87956f48a182d26ed451861dd21201ecc7239037ada67319bdf0849c387c73a110af798b4c5f9018bc97993e060ea2a2937fa2eb095d65ec07009fc407a350f1d6fb3c98a0a5f204be985b0cb6962f0eb7844a179c4598a92ea32d2d706c800034d2e960ded5b476d77073316b933fb3e6ba2f4f24a3b73a1e4d8ed1491d757ecf56fd72465dac0000736744d28d29073091587c8bccad302f7054e8a32bb8724974d9f3e449fc70b2a41f0008b548f717ac0a2c3a6580bfb50774933a578ad6acdcb89940bb406ea540893f097d8a88d1609ed605f25499de939083a0c8a7c6db462df5dfa06c298dd233e249433a54267d5cdc22e5524705b7d6b16b96bb2cb83e00cef62e21d91528a74cf95bfd1d391590c93f4058e9bb02656fd087a5b63d738d1c3b5cf533fd59c81cf9136bfcd3e955c19daf9906ef175791fde6a1d98155d7881e241c3522551cf9fcae42e1e46929ea39fd00943446823f9755085ccc8456a3090b73a3031a201d9c704a4ad4868dd9b6d06205560013973f60d637de2f18354bf4523d9d81dc2a7e78cd42c586364bbe0ed86fde0f081f801c1a4abb830839b7796d9a01f141bec8bd93144104c6dc59170162c0a5a639eb63a0a164970de50eb2e04f027394b26ed48d341f7851994df79d7cd663672a556f25e5e16a3adbe1003d631de938fabfed234df12b5ff3027f4a2da823834cb098e0f977a4eb9614579d5c7a1d400a1a933a657aef8ea1a66743d73b0cf37a7d64e9a63e4c7b09945f0db750b311b39783fb5ea216616751967d480a630d3da7c89d1c7beae20369137e96734a4cfedca56a7887f076fe4fe97534ad3e4f74d1a81750581a5ea214b440c7f30331ab86c257534c71175d1e731303a48b01c589fda4fb0d4368b4dd63d91204cb6fc389b2202aa94391907bfb72902a4031f5589ed5f391c2ce92aa998c200ba3c77d8bd747b9d0a29fa85cda3949a6d2bd0c3402e68f98fd451aa27b6c2dfd170e004577cbdb25e3a1b9852e9f66a370789c47bfce722446dade1b32ceae71ee0e1d96edf7ed08a93e3690056f46c3d8e63f88e53673ee71d72cfedbeba493ee91333120e09e9ce9f9c9a7a400f814ea618b1de48f9805e092f4e20f301fbb65caa83735a2a5c89befe4bce4116dca3688e1e14c6f09a945671dedbb5c0ba526842b6cae31d8b5ff978bae928a17a75c134630dd9de988f6ad3d89a071b33775a9660a40b48ec61ad3f93ac81cb1c65d8b0bab5c214786abd13cc10a8ea2e2a370e86e2fa1a372d83c9697b5e37b281e51507685f714fdaebe49ffc93a5582e1936eaee8e4140a4b72" + +end # module diff --git a/base/random/generation.jl b/base/random/generation.jl new file mode 100644 index 0000000000000..45b2329d30176 --- /dev/null +++ b/base/random/generation.jl @@ -0,0 +1,413 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Uniform random generation + +## from types: rand(::Type, [dims...]) + +### GLOBAL_RNG fallback for all types + +@inline rand(T::Type) = rand(GLOBAL_RNG, T) + +### random floats + +# CloseOpen(T) is the fallback for an AbstractFloat T +@inline rand(r::AbstractRNG=GLOBAL_RNG, ::Type{T}=Float64) where {T<:AbstractFloat} = + rand(r, CloseOpen(T)) + +# 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_generic(r::AbstractRNG, ::CloseOpen{Float16}) = + Float16(reinterpret(Float32, + (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) + +rand_generic(r::AbstractRNG, ::CloseOpen{Float32}) = + reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 + +rand_generic(r::AbstractRNG, ::Close1Open2_64) = + reinterpret(Float64, 0x3ff0000000000000 | rand(r, UInt64) & 0x000fffffffffffff) + +rand_generic(r::AbstractRNG, ::CloseOpen_64) = rand(r, Close1Open2()) - 1.0 + +#### BigFloat + +const bits_in_Limb = sizeof(Limb) << 3 +const Limb_high_bit = one(Limb) << (bits_in_Limb-1) + +struct BigFloatRandGenerator + prec::Int + nlimbs::Int + limbs::Vector{Limb} + shift::UInt + + function BigFloatRandGenerator(prec::Int=precision(BigFloat)) + nlimbs = (prec-1) ÷ bits_in_Limb + 1 + limbs = Vector{Limb}(nlimbs) + shift = nlimbs * bits_in_Limb - prec + new(prec, nlimbs, limbs, shift) + end +end + +function _rand(rng::AbstractRNG, gen::BigFloatRandGenerator) + z = BigFloat() + limbs = gen.limbs + rand!(rng, limbs) + @inbounds begin + limbs[1] <<= gen.shift + randbool = iszero(limbs[end] & Limb_high_bit) + limbs[end] |= Limb_high_bit + end + z.sign = 1 + unsafe_copy!(z.d, pointer(limbs), gen.nlimbs) + (z, randbool) +end + +function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::Close1Open2{BigFloat}) + z = _rand(rng, gen)[1] + z.exp = 1 + z +end + +function rand(rng::AbstractRNG, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat}) + z, randbool = _rand(rng, gen) + z.exp = 0 + randbool && + ccall((:mpfr_sub_d, :libmpfr), Int32, + (Ref{BigFloat}, Ref{BigFloat}, Cdouble, Int32), + 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, gen::BigFloatRandGenerator, ::CloseOpen{BigFloat}, ::Void) + z = rand(rng, Close1Open2(BigFloat), gen) + ccall((:mpfr_sub_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, Int32), + z, z, 1, Base.MPFR.ROUNDING_MODE[]) + z +end + +rand_generic(rng::AbstractRNG, I::FloatInterval{BigFloat}) = + rand(rng, BigFloatRandGenerator(), I) + +### random integers + +rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) +rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) + +@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2())) +@inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff + +### random complex numbers + +rand(r::AbstractRNG, ::Type{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, ::Type{Char}) + c = rand(r, 0x00000000:0x0010f7ff) + (c < 0xd800) ? Char(c) : Char(c+0x800) +end + +### arrays of random numbers + +function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} + for i in eachindex(A) + @inbounds A[i] = rand(r, X) + end + A +end + +rand!(A::AbstractArray, ::Type{X}) where {X} = rand!(GLOBAL_RNG, A, X) +# NOTE: if the second parameter above is defaulted to eltype(A) and the +# method below is removed, then some specialized methods (e.g. for +# rand!(::Array{Float64})) will fail to be called +rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) + + +rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) +rand( dims::Dims) = rand(GLOBAL_RNG, dims) +rand(r::AbstractRNG, dims::Integer...) = rand(r, Dims(dims)) +rand( dims::Integer...) = rand(Dims(dims)) + +rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) +rand( T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) +rand(r::AbstractRNG, T::Type, d::Integer, dims::Integer...) = rand(r, T, Dims((d, dims...))) +rand( T::Type, d::Integer, dims::Integer...) = rand(T, 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 + +#### arrays of floats + +rand!(r::AbstractRNG, A::AbstractArray, ::Type{T}) where {T<:AbstractFloat} = + rand!(r, A, CloseOpen{T}()) + +function rand!(r::AbstractRNG, A::AbstractArray, I::FloatInterval) + for i in eachindex(A) + @inbounds A[i] = rand(r, I) + end + A +end + +function rand!(rng::AbstractRNG, A::AbstractArray, I::FloatInterval{BigFloat}) + gen = BigFloatRandGenerator() + for i in eachindex(A) + @inbounds A[i] = rand(rng, gen, I) + end + A +end + +rand!(A::AbstractArray, I::FloatInterval) = rand!(GLOBAL_RNG, A, I) + +## Generate random integer within a range + +abstract type RangeGenerator end + +### RangeGenerator for BitInteger + +# 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 <= 2^bits(T) decremented by one, +# that is 0xFFFF...FFFF if k = typemax(T) - typemin(T) with intentional underflow +# see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow +maxmultiple(k::T) where {T<:Unsigned} = + (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T + +# maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size +maxmultiplemix(k::UInt64) = k >> 32 != 0 ? + maxmultiple(k) : + (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64 + +struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator + a::T # first element of the range + k::U # range length or zero for full range + u::U # rejection threshold +end + +# generators with 32, 128 bits entropy +RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = + RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) + +# mixed 32/64 bits entropy generator +RangeGeneratorInt(a::T, k::UInt64) where {T} = + RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) + +function RangeGenerator(r::UnitRange{T}) where T<:Unsigned + isempty(r) && throw(ArgumentError("range must be non-empty")) + RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) +end + +for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), + (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), + (Int64, UInt64), (Int128, UInt128), (Bool, UInt32)] + + @eval RangeGenerator(r::UnitRange{$T}) = begin + isempty(r) && throw(ArgumentError("range must be non-empty")) + # overflow ok: + RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) + end +end + +### RangeGenerator for BigInt + +struct RangeGeneratorBigInt <: RangeGenerator + 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 + mask::Limb # applied to the highest limb +end + + +function RangeGenerator(r::UnitRange{BigInt}) + m = last(r) - first(r) + m < 0 && throw(ArgumentError("range must be non-empty")) + nd = ndigits(m, 2) + nlimbs, highbits = divrem(nd, 8*sizeof(Limb)) + highbits > 0 && (nlimbs += 1) + mask = highbits == 0 ? ~zero(Limb) : one(Limb)<> 32 == 0 + x = rand(rng, UInt32) + while x > g.u + x = rand(rng, UInt32) + end + else + x = rand(rng, UInt64) + while x > g.u + x = rand(rng, UInt64) + end + end + return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k)) +end + +function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where {T<:Integer,U<:Unsigned} + x = rand(rng, U) + while x > g.u + x = rand(rng, U) + end + (unsigned(g.a) + rem_knuth(x, g.k)) % T +end + +function rand(rng::AbstractRNG, g::RangeGeneratorBigInt) + x = MPZ.realloc2(g.nlimbsmax*8*sizeof(Limb)) + limbs = unsafe_wrap(Array, x.d, g.nlimbs) + while true + rand!(rng, limbs) + @inbounds limbs[end] &= g.mask + MPZ.mpn_cmp(x, g.m, g.nlimbs) <= 0 && break + end + # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6) + x.size = g.nlimbs + while x.size > 0 + @inbounds limbs[x.size] != 0 && break + x.size -= 1 + end + MPZ.add!(x, g.a) +end + +#### arrays + +function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) + for i in eachindex(A) + @inbounds A[i] = rand(rng, g) + end + return A +end + +### random values from UnitRange + +rand(rng::AbstractRNG, r::UnitRange{<:Integer}) = rand(rng, RangeGenerator(r)) + +rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Integer}) = + rand!(rng, A, RangeGenerator(r)) + +## random values from AbstractArray + +rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] +rand( r::AbstractArray) = rand(GLOBAL_RNG, r) + +### arrays + +function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) + g = RangeGenerator(1:(length(r))) + for i in eachindex(A) + @inbounds A[i] = r[rand(rng, g)] + end + return A +end + +rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) + +rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = + rand!(rng, Array{T}(dims), r) +rand( r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) +rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, Dims(dims)) +rand( r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, Dims(dims)) + + +## random values from Dict, Set, IntSet + +function rand(r::AbstractRNG, t::Dict) + isempty(t) && throw(ArgumentError("collection must be non-empty")) + rg = RangeGenerator(1:length(t.slots)) + while true + i = rand(r, rg) + Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) + end +end + +rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first + +function rand(r::AbstractRNG, s::IntSet) + isempty(s) && throw(ArgumentError("collection must be non-empty")) + # s can be empty while s.bits is not, so we cannot rely on the + # length check in RangeGenerator below + rg = RangeGenerator(1:length(s.bits)) + while true + n = rand(r, rg) + @inbounds b = s.bits[n] + b && return n + end +end + +function nth(iter, n::Integer)::eltype(iter) + for (i, x) in enumerate(iter) + i == n && return x + end +end +nth(iter::AbstractArray, n::Integer) = iter[n] + +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) + +rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) + +### arrays + +function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) + for i in eachindex(A) + @inbounds A[i] = rand(r, s) + end + A +end + +# avoid linear complexity for repeated calls with generic containers +rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = + rand!(r, A, collect(s)) + +rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) + +rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = + rand!(r, Array{Pair{K,V}}(dims), s) + +rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) +rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = + rand(r, s, Dims(dims)) +rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, Dims(dims)) +rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) + + +## random characters from a string + +isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i)) +isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) +_endof(s::String) = sizeof(s) +_endof(s::AbstractString) = endof(s) + +function rand(rng::AbstractRNG, s::AbstractString)::Char + g = RangeGenerator(1:_endof(s)) + while true + pos = rand(rng, g) + isvalid_unsafe(s, pos) && return s[pos] + end +end + +rand(s::AbstractString) = rand(GLOBAL_RNG, s) + +### arrays + +# we use collect(str), which is most of the time more efficient than specialized methods +# (except maybe for very small arrays) +rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) +rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str) +rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = + rand!(rng, Array{eltype(str)}(dims), str) + +rand(rng::AbstractRNG, str::AbstractString, d::Integer, dims::Integer...) = + rand(rng, str, Dims((d, dims...))) + +rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims) +rand(str::AbstractString, d::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d, dims...) diff --git a/base/random/misc.jl b/base/random/misc.jl new file mode 100644 index 0000000000000..0d5f1123a41fc --- /dev/null +++ b/base/random/misc.jl @@ -0,0 +1,467 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## rand!(::BitArray) && bitrand + +function rand!(rng::AbstractRNG, B::BitArray) + isempty(B) && return B + Bc = B.chunks + rand!(rng, Bc) + Bc[end] &= Base._msk_end(B) + return B +end + +""" + bitrand([rng=GLOBAL_RNG], [dims...]) + +Generate a `BitArray` of random boolean values. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> bitrand(rng, 10) +10-element BitArray{1}: + true + true + true + false + true + false + false + true + false + true +``` +""" +bitrand(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(dims)) +bitrand(r::AbstractRNG, dims::Integer...) = rand!(r, BitArray(convert(Dims, dims))) + +bitrand(dims::Dims) = rand!(BitArray(dims)) +bitrand(dims::Integer...) = rand!(BitArray(convert(Dims, dims))) + + +## randstring (often useful for temporary filenames/dirnames) + +""" + randstring([rng=GLOBAL_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> srand(0); randstring() +"c03rgKi1" + +julia> randstring(MersenneTwister(0), 'a':'z', 6) +"wijzek" + +julia> randstring("ACGT") +"TATCGGTC" +``` + +!!! 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 + randstring(r::AbstractRNG, chars=b, n::Integer=8) = String(rand(r, chars, n)) + randstring(r::AbstractRNG, n::Integer) = randstring(r, b, n) + randstring(chars=b, n::Integer=8) = randstring(GLOBAL_RNG, chars, n) + randstring(n::Integer) = randstring(GLOBAL_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) + 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) + n = length(A) + p == 1 && return copy!(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!(S, A, p) + +Like [`randsubseq`](@ref), but the results are stored in `S` +(which is resized as needed). +""" +randsubseq!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) + +randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = + randsubseq!(r, T[], A, p) + +""" + randsubseq(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`. +""" +randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) + + +## rand_lt (helper function) + +"Return a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." +@inline function rand_lt(r::AbstractRNG, n::Int, mask::Int=nextpow2(n)-1) + # this duplicates the functionality of RangeGenerator objects, + # to optimize this special case + while true + x = (rand_ui52_raw(r) % Int) & mask + x < n && return x + end +end + + +## shuffle & shuffle! + +""" + shuffle!([rng=GLOBAL_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, collect(1:16)) +16-element Array{Int64,1}: + 2 + 15 + 5 + 14 + 1 + 9 + 10 + 6 + 11 + 3 + 16 + 7 + 4 + 12 + 8 + 13 +``` +""" +function shuffle!(r::AbstractRNG, a::AbstractArray) + n = length(a) + @assert n <= Int64(2)^52 + mask = nextpow2(n) - 1 + for i = n:-1:2 + (mask >> 1) == i && (mask >>= 1) + j = 1 + rand_lt(r, i, mask) + a[i], a[j] = a[j], a[i] + end + return a +end + +shuffle!(a::AbstractArray) = shuffle!(GLOBAL_RNG, a) + +""" + shuffle([rng=GLOBAL_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, collect(1:10)) +10-element Array{Int64,1}: + 6 + 1 + 10 + 2 + 3 + 9 + 5 + 7 + 4 + 8 +``` +""" +shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) +shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) + + +## randperm & randperm! + +""" + randperm([rng=GLOBAL_RNG,] n::Integer) + +Construct a random permutation of length `n`. 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), 4) +4-element Array{Int64,1}: + 2 + 1 + 4 + 3 +``` +""" +randperm(r::AbstractRNG, n::Integer) = randperm!(r, Vector{Int}(n)) +randperm(n::Integer) = randperm(GLOBAL_RNG, n) + +""" + randperm!([rng=GLOBAL_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}(4)) +4-element Array{Int64,1}: + 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_lt(r, i, mask) + if i != j # a[i] is uninitialized (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!(GLOBAL_RNG, a) + + +## randcycle & randcycle! + +""" + randcycle([rng=GLOBAL_RNG,] n::Integer) + +Construct a random cyclic permutation of length `n`. The optional `rng` +argument specifies a random number generator, see [Random Numbers](@ref). + +# Examples +```jldoctest +julia> randcycle(MersenneTwister(1234), 6) +6-element Array{Int64,1}: + 3 + 5 + 4 + 6 + 1 + 2 +``` +""" +randcycle(r::AbstractRNG, n::Integer) = randcycle!(r, Vector{Int}(n)) +randcycle(n::Integer) = randcycle(GLOBAL_RNG, n) + +""" + randcycle!([rng=GLOBAL_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}(6)) +6-element Array{Int64,1}: + 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_lt(r, 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!(GLOBAL_RNG, a) + + +## random UUID generation + +struct UUID + value::UInt128 + + UUID(u::UInt128) = new(u) +end + +""" + uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> 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. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid1(rng) +2cc938da-5937-11e7-196e-0f4ef71aa64b +``` +""" +function uuid1(rng::AbstractRNG=GLOBAL_RNG) + 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=GLOBAL_RNG]) -> UUID + +Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), +as specified by RFC 4122. + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid4(rng) +82015f10-44cc-4827-996e-0f4ef71aa64b +``` +""" +function uuid4(rng::AbstractRNG=GLOBAL_RNG) + u = rand(rng, UInt128) + u &= 0xffffffffffff0fff3fffffffffffffff + u |= 0x00000000000040008000000000000000 + UUID(u) +end + +""" + uuid_version(u::UUID) -> Integer + +Inspects the given UUID and returns its version (see RFC 4122). + +# Examples +```jldoctest +julia> rng = MersenneTwister(1234); + +julia> Base.Random.uuid_version(Base.Random.uuid4(rng)) +4 +``` +""" +uuid_version(u::UUID) = Int((u.value >> 76) & 0xf) + +Base.convert(::Type{UInt128}, u::UUID) = u.value + +function Base.convert(::Type{UUID}, s::AbstractString) + s = lowercase(s) + + if !ismatch(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) + throw(ArgumentError("Malformed UUID string")) + end + + u = UInt128(0) + for i in [1:8; 10:13; 15:18; 20:23; 25:36] + u <<= 4 + d = s[i]-'0' + u |= 0xf & (d-39*(d>9)) + end + return UUID(u) +end + +function Base.repr(u::UUID) + u = u.value + a = Vector{UInt8}(36) + for i = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] + d = u & 0xf + a[i] = '0'+d+39*(d>9) + u >>= 4 + end + a[[24,19,14,9]] = '-' + + return String(a) +end + +Base.show(io::IO, u::UUID) = write(io, Base.repr(u)) diff --git a/base/random.jl b/base/random/normal.jl similarity index 51% rename from base/random.jl rename to base/random/normal.jl index c8ca64d16eacf..c78f6e4976ee7 100644 --- a/base/random.jl +++ b/base/random/normal.jl @@ -1,884 +1,196 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -module Random +# Normally distributed random numbers using Ziggurat algorithm -using Base.dSFMT -using Base.GMP: Limb, MPZ -import Base: copymutable, copy, copy!, ==, hash - -export srand, - rand, rand!, - randn, randn!, - randexp, randexp!, - bitrand, - randstring, - randsubseq, randsubseq!, - shuffle, shuffle!, - randperm, randperm!, - randcycle, randcycle!, - AbstractRNG, MersenneTwister, RandomDevice, - GLOBAL_RNG, randjump - - -abstract type AbstractRNG end - -abstract type FloatInterval end -mutable struct CloseOpen <: FloatInterval end -mutable struct Close1Open2 <: FloatInterval end - - -## RandomDevice - -const BoolBitIntegerType = Union{Type{Bool},Base.BitIntegerType} -const BoolBitIntegerArray = Union{Array{Bool},Base.BitIntegerArray} - -if Sys.iswindows() - struct RandomDevice <: AbstractRNG - buffer::Vector{UInt128} - - RandomDevice() = new(Vector{UInt128}(1)) - end - - function rand(rd::RandomDevice, T::BoolBitIntegerType) - win32_SystemFunction036!(rd.buffer) - @inbounds return rd.buffer[1] % T - end - - rand!(rd::RandomDevice, A::BoolBitIntegerArray) = (win32_SystemFunction036!(A); A) -else # !windows - struct RandomDevice <: AbstractRNG - file::IOStream - unlimited::Bool - - RandomDevice(unlimited::Bool=true) = new(open(unlimited ? "/dev/urandom" : "/dev/random"), unlimited) - end - - rand(rd::RandomDevice, T::BoolBitIntegerType) = read( rd.file, T) - rand!(rd::RandomDevice, A::BoolBitIntegerArray) = read!(rd.file, A) -end # os-test - -""" - RandomDevice() - -Create a `RandomDevice` RNG object. Two such objects will always generate different streams of random numbers. -""" -RandomDevice - - -rand(rng::RandomDevice, ::Type{Close1Open2}) = - reinterpret(Float64, 0x3ff0000000000000 | rand(rng, UInt64) & 0x000fffffffffffff) - -rand(rng::RandomDevice, ::Type{CloseOpen}) = rand(rng, Close1Open2) - 1.0 - - -## MersenneTwister - -const MTCacheLength = dsfmt_get_min_array_size() - -mutable struct MersenneTwister <: AbstractRNG - seed::Vector{UInt32} - state::DSFMT_state - vals::Vector{Float64} - idx::Int +# The Ziggurat Method for generating random variables - Marsaglia and Tsang +# Paper and reference code: http://www.jstatsoft.org/v05/i08/ - function MersenneTwister(seed, state, vals, idx) - if !(length(vals) == MTCacheLength && 0 <= idx <= MTCacheLength) - throw(DomainError(idx, "`length(vals)` and `idx` must be consistent with $MTCacheLength")) - end - new(seed, state, vals, idx) - end -end +# randmtzig (covers also exponential variates) -MersenneTwister(seed::Vector{UInt32}, state::DSFMT_state) = - MersenneTwister(seed, state, zeros(Float64, MTCacheLength), MTCacheLength) +## randn """ - MersenneTwister(seed) + randn([rng=GLOBAL_RNG], [T=Float64], [dims...]) -Create a `MersenneTwister` RNG object. Different RNG objects can have their own seeds, which -may be useful for generating different streams of random numbers. +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. # Examples ```jldoctest julia> rng = MersenneTwister(1234); -julia> x1 = rand(rng, 2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> rng = MersenneTwister(1234); - -julia> x2 = rand(rng, 2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 +julia> randn(rng, Complex128) +0.6133070881429037 - 0.6376291670853887im -julia> x1 == x2 -true +julia> randn(rng, Complex64, (2, 3)) +2×3 Array{Complex{Float32},2}: + -0.349649-0.638457im 0.376756-0.192146im -0.396334-0.0136413im + 0.611224+1.56403im 0.355204-0.365563im 0.0905552+1.31012im ``` """ -MersenneTwister(seed) = srand(MersenneTwister(Vector{UInt32}(), DSFMT_state()), seed) - -function copy!(dst::MersenneTwister, src::MersenneTwister) - copy!(resize!(dst.seed, length(src.seed)), src.seed) - copy!(dst.state, src.state) - copy!(dst.vals, src.vals) - dst.idx = src.idx - dst -end - -copy(src::MersenneTwister) = - MersenneTwister(copy(src.seed), copy(src.state), copy(src.vals), src.idx) - -==(r1::MersenneTwister, r2::MersenneTwister) = - r1.seed == r2.seed && r1.state == r2.state && isequal(r1.vals, r2.vals) && r1.idx == r2.idx - -hash(r::MersenneTwister, h::UInt) = foldr(hash, h, (r.seed, r.state, r.vals, r.idx)) - -## Low level API for MersenneTwister - -@inline mt_avail(r::MersenneTwister) = MTCacheLength - r.idx -@inline mt_empty(r::MersenneTwister) = r.idx == MTCacheLength -@inline mt_setfull!(r::MersenneTwister) = r.idx = 0 -@inline mt_setempty!(r::MersenneTwister) = r.idx = MTCacheLength -@inline mt_pop!(r::MersenneTwister) = @inbounds return r.vals[r.idx+=1] - -function gen_rand(r::MersenneTwister) - dsfmt_fill_array_close1_open2!(r.state, pointer(r.vals), length(r.vals)) - mt_setfull!(r) -end - -@inline reserve_1(r::MersenneTwister) = (mt_empty(r) && gen_rand(r); nothing) -# `reserve` allows one to call `rand_inbounds` n times -# precondition: n <= MTCacheLength -@inline reserve(r::MersenneTwister, n::Int) = (mt_avail(r) < n && gen_rand(r); nothing) - -# precondition: !mt_empty(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{Close1Open2}) = mt_pop!(r) -@inline rand_inbounds(r::MersenneTwister, ::Type{CloseOpen}) = rand_inbounds(r, Close1Open2) - 1.0 -@inline rand_inbounds(r::MersenneTwister) = rand_inbounds(r, CloseOpen) - -# produce Float64 values -@inline rand(r::MersenneTwister, ::Type{I}) where {I<:FloatInterval} = (reserve_1(r); rand_inbounds(r, I)) - -@inline rand_ui52_raw_inbounds(r::MersenneTwister) = reinterpret(UInt64, rand_inbounds(r, Close1Open2)) -@inline rand_ui52_raw(r::MersenneTwister) = (reserve_1(r); rand_ui52_raw_inbounds(r)) - -@inline function rand_ui2x52_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 64 | rand_ui52_raw_inbounds(r) -end - -@inline function rand_ui104_raw(r::MersenneTwister) - reserve(r, 2) - rand_ui52_raw_inbounds(r) % UInt128 << 52 ⊻ rand_ui52_raw_inbounds(r) -end - -function srand(r::MersenneTwister, seed::Vector{UInt32}) - copy!(resize!(r.seed, length(seed)), seed) - dsfmt_init_by_array(r.state, r.seed) - mt_setempty!(r) - return r -end - -# MersenneTwister jump - -""" - randjump(r::MersenneTwister, jumps::Integer, [jumppoly::AbstractString=dSFMT.JPOLY1e21]) -> Vector{MersenneTwister} - -Create an array of the size `jumps` of initialized `MersenneTwister` RNG objects. The -first RNG object given as a parameter and following `MersenneTwister` RNGs in the array are -initialized such that a state of the RNG object in the array would be moved forward (without -generating numbers) from a previous RNG object array element on a particular number of steps -encoded by the jump polynomial `jumppoly`. - -Default jump polynomial moves forward `MersenneTwister` RNG state by `10^20` steps. -""" -function randjump(mt::MersenneTwister, jumps::Integer, jumppoly::AbstractString) - mts = MersenneTwister[] - push!(mts, mt) - for i in 1:jumps-1 - cmt = mts[end] - push!(mts, MersenneTwister(copy(cmt.seed), dSFMT.dsfmt_jump(cmt.state, jumppoly))) +@inline function randn(rng::AbstractRNG=GLOBAL_RNG) + @inbounds begin + r = rand_ui52(rng) + 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 - return mts end -randjump(r::MersenneTwister, jumps::Integer) = randjump(r, jumps, dSFMT.JPOLY1e21) - -## initialization -function __init__() - try - srand() - catch ex - Base.showerror_nostdio(ex, - "WARNING: Error during initialization of module Random") +# this unlikely branch is put in a separate function for better efficiency +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 -## make_seed() -# make_seed methods produce values of type Array{UInt32}, suitable for MersenneTwister seeding +Base.@irrational SQRT_HALF 0.7071067811865475244008 sqrt(big(0.5)) -function make_seed() - try - return rand(RandomDevice(), UInt32, 4) - catch - println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.") - seed = reinterpret(UInt64, time()) - seed = hash(seed, UInt64(getpid())) - try - seed = hash(seed, parse(UInt64, read(pipeline(`ifconfig`, `sha1sum`), String)[1:40], 16)) - end - return make_seed(seed) - end -end +randn(rng::AbstractRNG, ::Type{Complex{T}}) where {T<:AbstractFloat} = + Complex{T}(SQRT_HALF * randn(rng, T), SQRT_HALF * randn(rng, T)) -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 -## srand() +## randexp """ - srand([rng=GLOBAL_RNG], seed) -> rng - srand([rng=GLOBAL_RNG]) -> rng + randexp([rng=GLOBAL_RNG], [T=Float64], [dims...]) -Reseed the random number generator. If a `seed` is provided, the RNG will give a -reproducible sequence of numbers, otherwise Julia will get entropy from the system. For -`MersenneTwister`, the `seed` may be a non-negative integer or a vector of [`UInt32`](@ref) -integers. `RandomDevice` does not support seeding. +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> srand(1234); - -julia> x1 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 - -julia> srand(1234); +julia> rng = MersenneTwister(1234); -julia> x2 = rand(2) -2-element Array{Float64,1}: - 0.590845 - 0.766797 +julia> randexp(rng, Float32) +2.4835055f0 -julia> x1 == x2 -true +julia> randexp(rng, 3, 3) +3×3 Array{Float64,2}: + 1.5167 1.30652 0.344435 + 0.604436 2.78029 0.418516 + 0.695867 0.693292 0.643644 ``` """ -srand(r::MersenneTwister) = srand(r, make_seed()) -srand(r::MersenneTwister, n::Integer) = srand(r, make_seed(n)) - - -function dsfmt_gv_srand() - # Temporary fix for #8874 and #9124: update global RNG for Rmath - dsfmt_gv_init_by_array(GLOBAL_RNG.seed+UInt32(1)) - return GLOBAL_RNG -end - -function srand() - srand(GLOBAL_RNG) - dsfmt_gv_srand() +@inline function randexp(rng::AbstractRNG=GLOBAL_RNG) + @inbounds begin + ri = rand_ui52(rng) + 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 -function srand(seed::Union{Integer,Vector{UInt32}}) - srand(GLOBAL_RNG, seed) - dsfmt_gv_srand() +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 -## Global RNG -const GLOBAL_RNG = MersenneTwister(0) -globalRNG() = GLOBAL_RNG - -# rand: a non-specified RNG defaults to GLOBAL_RNG +## arrays & other scalar methods """ - rand([rng=GLOBAL_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:n` or `['x','y','z']`), -* an `Associative` 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)), and to ``[0, 1)`` for floating - point numbers; + randn!([rng=GLOBAL_RNG], A::AbstractArray) -> A -`S` defaults to [`Float64`](@ref). +Fill the array `A` with normally-distributed (mean 0, standard deviation 1) random numbers. +Also see the [`rand`](@ref) function. # Examples -```julia-repl -julia> rand(Int, 2) -2-element Array{Int64,1}: - 1339893410598768192 - 1575814717733606317 +```jldoctest +julia> rng = MersenneTwister(1234); -julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) -1=>2 +julia> randn!(rng, zeros(5)) +5-element Array{Float64,1}: + 0.867347 + -0.901744 + -0.494479 + -0.902914 + 0.864401 ``` - -!!! note - The complexity of `rand(rng, s::Union{Associative,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 `IntSet`. 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. """ -@inline rand() = rand(GLOBAL_RNG, CloseOpen) -@inline rand(T::Type) = rand(GLOBAL_RNG, T) -rand(dims::Dims) = rand(GLOBAL_RNG, dims) -rand(dims::Integer...) = rand(convert(Dims, dims)) -rand(T::Type, dims::Dims) = rand(GLOBAL_RNG, T, dims) -rand(T::Type, d1::Integer, dims::Integer...) = rand(T, tuple(Int(d1), convert(Dims, dims)...)) -rand!(A::AbstractArray) = rand!(GLOBAL_RNG, A) - -rand(r::AbstractArray) = rand(GLOBAL_RNG, r) +function randn! end """ - rand!([rng=GLOBAL_RNG], A, [S=eltype(A)]) + randexp!([rng=GLOBAL_RNG], A::AbstractArray) -> 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 `copy!(A, rand(rng, S, size(A)))` -but without allocating a new array. +Fill the array `A` with random numbers following the exponential distribution +(with scale 1). # Examples ```jldoctest julia> rng = MersenneTwister(1234); -julia> rand!(rng, zeros(5)) +julia> randexp!(rng, zeros(5)) 5-element Array{Float64,1}: - 0.590845 - 0.766797 - 0.566237 - 0.460085 - 0.794026 + 2.48351 + 1.5167 + 0.604436 + 0.695867 + 1.30652 ``` """ -rand!(A::AbstractArray, r::AbstractArray) = rand!(GLOBAL_RNG, A, r) - -rand(r::AbstractArray, dims::Dims) = rand(GLOBAL_RNG, r, dims) -rand(r::AbstractArray, dims::Integer...) = rand(GLOBAL_RNG, r, convert(Dims, dims)) - -## random floating point values - -@inline rand(r::AbstractRNG) = rand(r, CloseOpen) - -# MersenneTwister & RandomDevice -@inline rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float64}) = rand(r, CloseOpen) - -rand_ui10_raw(r::MersenneTwister) = rand_ui52_raw(r) -rand_ui23_raw(r::MersenneTwister) = rand_ui52_raw(r) -rand_ui10_raw(r::AbstractRNG) = rand(r, UInt16) -rand_ui23_raw(r::AbstractRNG) = rand(r, UInt32) - -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float16}) = - Float16(reinterpret(Float32, (rand_ui10_raw(r) % UInt32 << 13) & 0x007fe000 | 0x3f800000) - 1) - -rand(r::Union{RandomDevice,MersenneTwister}, ::Type{Float32}) = - reinterpret(Float32, rand_ui23_raw(r) % UInt32 & 0x007fffff | 0x3f800000) - 1 - - -## random integers - -@inline rand_ui52_raw(r::AbstractRNG) = reinterpret(UInt64, rand(r, Close1Open2)) -@inline rand_ui52(r::AbstractRNG) = rand_ui52_raw(r) & 0x000fffffffffffff - -# MersenneTwister - -@inline rand(r::MersenneTwister, ::Type{T}) where {T<:Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}} = - rand_ui52_raw(r) % T - -function rand(r::MersenneTwister, ::Type{UInt64}) - reserve(r, 2) - rand_ui52_raw_inbounds(r) << 32 ⊻ rand_ui52_raw_inbounds(r) -end - -function rand(r::MersenneTwister, ::Type{UInt128}) - reserve(r, 3) - xor(rand_ui52_raw_inbounds(r) % UInt128 << 96, - rand_ui52_raw_inbounds(r) % UInt128 << 48, - rand_ui52_raw_inbounds(r)) -end - -rand(r::MersenneTwister, ::Type{Int64}) = reinterpret(Int64, rand(r, UInt64)) -rand(r::MersenneTwister, ::Type{Int128}) = reinterpret(Int128, rand(r, UInt128)) - -## random Complex values - -rand(r::AbstractRNG, ::Type{Complex{T}}) where {T<:Real} = complex(rand(r, T), rand(r, T)) - -# random Char values -# returns a random valid Unicode scalar value (i.e. 0 - 0xd7ff, 0xe000 - # 0x10ffff) -function rand(r::AbstractRNG, ::Type{Char}) - c = rand(r, 0x00000000:0x0010f7ff) - (c < 0xd800) ? Char(c) : Char(c+0x800) -end - -# random values from Dict, Set, IntSet (for efficiency) -function rand(r::AbstractRNG, t::Dict) - isempty(t) && throw(ArgumentError("collection must be non-empty")) - rg = RangeGenerator(1:length(t.slots)) - while true - i = rand(r, rg) - Base.isslotfilled(t, i) && @inbounds return (t.keys[i] => t.vals[i]) - end -end - -rand(r::AbstractRNG, s::Set) = rand(r, s.dict).first - -function rand(r::AbstractRNG, s::IntSet) - isempty(s) && throw(ArgumentError("collection must be non-empty")) - # s can be empty while s.bits is not, so we cannot rely on the - # length check in RangeGenerator below - rg = RangeGenerator(1:length(s.bits)) - while true - n = rand(r, rg) - @inbounds b = s.bits[n] - b && return n - end -end - -function nth(iter, n::Integer)::eltype(iter) - for (i, x) in enumerate(iter) - i == n && return x - end -end -nth(iter::AbstractArray, n::Integer) = iter[n] - -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}) = nth(s, rand(r, 1:length(s))) - -rand(s::Union{Associative,AbstractSet}) = rand(GLOBAL_RNG, s) - -## Arrays of random numbers - -rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims) -rand(r::AbstractRNG, dims::Integer...) = rand(r, convert(Dims, dims)) - -rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array{T}(dims)) -rand(r::AbstractRNG, T::Type, d1::Integer, dims::Integer...) = rand(r, T, tuple(Int(d1), convert(Dims, dims)...)) -# note: the above method would trigger an ambiguity warning if d1 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 - -function rand!(r::AbstractRNG, A::AbstractArray{T}, ::Type{X}=T) where {T,X} - for i in eachindex(A) - @inbounds A[i] = rand(r, X) - end - A -end - -rand!(A::AbstractArray, ::Type{X}) where {X} = rand!(GLOBAL_RNG, A, X) - -function rand!(r::AbstractRNG, A::AbstractArray, s::Union{Dict,Set,IntSet}) - for i in eachindex(A) - @inbounds A[i] = rand(r, s) - end - A -end - -# avoid linear complexity for repeated calls with generic containers -rand!(r::AbstractRNG, A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(r, A, collect(s)) - -rand!(A::AbstractArray, s::Union{Associative,AbstractSet}) = rand!(GLOBAL_RNG, A, s) - -rand(r::AbstractRNG, s::Associative{K,V}, dims::Dims) where {K,V} = rand!(r, Array{Pair{K,V}}(dims), s) -rand(r::AbstractRNG, s::AbstractSet{T}, dims::Dims) where {T} = rand!(r, Array{T}(dims), s) -rand(r::AbstractRNG, s::Union{Associative,AbstractSet}, dims::Integer...) = rand(r, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Integer...) = rand(GLOBAL_RNG, s, convert(Dims, dims)) -rand(s::Union{Associative,AbstractSet}, dims::Dims) = rand(GLOBAL_RNG, s, dims) - -# MersenneTwister - -function rand_AbstractArray_Float64!(r::MersenneTwister, A::AbstractArray{Float64}, - n=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval - # what follows is equivalent to this simple loop but more efficient: - # for i=1:n - # @inbounds A[i] = rand(r, I) - # end - m = 0 - 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 - -rand!(r::MersenneTwister, A::AbstractArray{Float64}) = rand_AbstractArray_Float64!(r, A) +function randexp! end -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{CloseOpen}) = dsfmt_fill_array_close_open!(s, A, n) -fill_array!(s::DSFMT_state, A::Ptr{Float64}, n::Int, ::Type{Close1Open2}) = dsfmt_fill_array_close1_open2!(s, A, n) +for randfun in [:randn, :randexp] + randfun! = Symbol(randfun, :!) + @eval begin + # scalars + $randfun(rng::AbstractRNG, T::BitFloatType) = convert(T, $randfun(rng)) + $randfun(::Type{T}) where {T} = $randfun(GLOBAL_RNG, T) -function rand!(r::MersenneTwister, A::Array{Float64}, n::Int=length(A), ::Type{I}=CloseOpen) where I<:FloatInterval - # 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_copy! 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 - if n > length(A) - throw(BoundsError(A,n)) - end - n2 = (n-2) ÷ 2 * 2 - if n2 < dsfmt_get_min_array_size() - rand_AbstractArray_Float64!(r, A, n, I) - else - pA = pointer(A) - align = Csize_t(pA) % 16 - if align > 0 - pA2 = pA + 16 - align - fill_array!(r.state, pA2, n2, I) # generate the data in-place, but shifted - unsafe_copy!(pA, pA2, n2) # move the data to the beginning of the array - else - fill_array!(r.state, pA, n2, I) - end - for i=n2+1:n - @inbounds A[i] = rand(r, I) + # 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 - end - A -end - -@inline mask128(u::UInt128, ::Type{Float16}) = (u & 0x03ff03ff03ff03ff03ff03ff03ff03ff) | 0x3c003c003c003c003c003c003c003c00 -@inline mask128(u::UInt128, ::Type{Float32}) = (u & 0x007fffff007fffff007fffff007fffff) | 0x3f8000003f8000003f8000003f800000 -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{Close1Open2}) - T = eltype(A) - n = length(A) - n128 = n * sizeof(T) ÷ 16 - rand!(r, unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2*n128), 2*n128, Close1Open2) - A128 = unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128) - @inbounds 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 - for i in 16*n128÷sizeof(T)+1:n - @inbounds A[i] = rand(r, T) + oneunit(T) - end - A -end - -function rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}, ::Type{CloseOpen}) - rand!(r, A, Close1Open2) - 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 - -rand!(r::MersenneTwister, A::Union{Array{Float16},Array{Float32}}) = rand!(r, A, CloseOpen) + $randfun!(A::AbstractArray) = $randfun!(GLOBAL_RNG, A) -function rand!(r::MersenneTwister, A::Array{UInt128}, n::Int=length(A)) - if n > length(A) - throw(BoundsError(A,n)) - end - Af = unsafe_wrap(Array, convert(Ptr{Float64}, pointer(A)), 2n) - i = n - while true - rand!(r, Af, 2i, Close1Open2) - n < 5 && break - i = 0 - @inbounds 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_ui2x52_raw(r) - for i = 1:n - @inbounds A[i] ⊻= u << (12*i) - end + # generating arrays + $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(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}(dim1, dims...)) + $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(GLOBAL_RNG, T, dims) + $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(GLOBAL_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(GLOBAL_RNG, Float64, dims) + $randfun( dims::Integer... ) = $randfun(GLOBAL_RNG, Float64, dims...) end - A end -# A::Array{UInt128} will match the specialized method above -function rand!(r::MersenneTwister, A::Base.BitIntegerArray) - n = length(A) - T = eltype(A) - n128 = n * sizeof(T) ÷ 16 - rand!(r, unsafe_wrap(Array, convert(Ptr{UInt128}, pointer(A)), n128)) - for i = 16*n128÷sizeof(T)+1:n - @inbounds A[i] = rand(r, T) - end - A -end +## Tables for normal variates -## Generate random integer within a range - -# 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 <= 2^bits(T) decremented by one, -# that is 0xFFFF...FFFF if k = typemax(T) - typemin(T) with intentional underflow -# see http://stackoverflow.com/questions/29182036/integer-arithmetic-add-1-to-uint-max-and-divide-by-n-without-overflow -maxmultiple(k::T) where {T<:Unsigned} = (div(typemax(T) - k + oneunit(k), k + (k == 0))*k + k - oneunit(k))::T - -# maximum multiple of k within 1:2^32 or 1:2^64 decremented by one, depending on size -maxmultiplemix(k::UInt64) = if k >> 32 != 0; maxmultiple(k); else (div(0x0000000100000000, k + (k == 0))*k - oneunit(k))::UInt64; end - -abstract type RangeGenerator end - -struct RangeGeneratorInt{T<:Integer,U<:Unsigned} <: RangeGenerator - a::T # first element of the range - k::U # range length or zero for full range - u::U # rejection threshold -end -# generators with 32, 128 bits entropy -RangeGeneratorInt(a::T, k::U) where {T,U<:Union{UInt32,UInt128}} = RangeGeneratorInt{T,U}(a, k, maxmultiple(k)) -# mixed 32/64 bits entropy generator -RangeGeneratorInt(a::T, k::UInt64) where {T} = RangeGeneratorInt{T,UInt64}(a, k, maxmultiplemix(k)) -# generator for ranges -function RangeGenerator(r::UnitRange{T}) where T<:Unsigned - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end - RangeGeneratorInt(first(r), last(r) - first(r) + oneunit(T)) -end - -# specialized versions -for (T, U) in [(UInt8, UInt32), (UInt16, UInt32), - (Int8, UInt32), (Int16, UInt32), (Int32, UInt32), (Int64, UInt64), (Int128, UInt128), - (Bool, UInt32)] - - @eval RangeGenerator(r::UnitRange{$T}) = begin - if isempty(r) - throw(ArgumentError("range must be non-empty")) - end - RangeGeneratorInt(first(r), convert($U, unsigned(last(r) - first(r)) + one($U))) # overflow ok - end -end - -struct RangeGeneratorBigInt <: RangeGenerator - 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 - mask::Limb # applied to the highest limb -end - - -function RangeGenerator(r::UnitRange{BigInt}) - m = last(r) - first(r) - m < 0 && throw(ArgumentError("range must be non-empty")) - nd = ndigits(m, 2) - nlimbs, highbits = divrem(nd, 8*sizeof(Limb)) - highbits > 0 && (nlimbs += 1) - mask = highbits == 0 ? ~zero(Limb) : one(Limb)<> 32 == 0 - x = rand(rng, UInt32) - while x > g.u - x = rand(rng, UInt32) - end - else - x = rand(rng, UInt64) - while x > g.u - x = rand(rng, UInt64) - end - end - return reinterpret(T, reinterpret(UInt64, g.a) + rem_knuth(x, g.k)) -end - -function rand(rng::AbstractRNG, g::RangeGeneratorInt{T,U}) where U<:Unsigned where T<:Integer - x = rand(rng, U) - while x > g.u - x = rand(rng, U) - end - (unsigned(g.a) + rem_knuth(x, g.k)) % T -end - -function rand(rng::AbstractRNG, g::RangeGeneratorBigInt) - x = MPZ.realloc2(g.nlimbsmax*8*sizeof(Limb)) - limbs = unsafe_wrap(Array, x.d, g.nlimbs) - while true - rand!(rng, limbs) - @inbounds limbs[end] &= g.mask - MPZ.mpn_cmp(x, g.m, g.nlimbs) <= 0 && break - end - # adjust x.size (normally done by mpz_limbs_finish, in GMP version >= 6) - x.size = g.nlimbs - while x.size > 0 - @inbounds limbs[x.size] != 0 && break - x.size -= 1 - end - MPZ.add!(x, g.a) -end - -rand(rng::AbstractRNG, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool}}) = rand(rng, RangeGenerator(r)) - -## special case for MersenneTwister -@inline function rand_lteq(r::AbstractRNG, randfun, u::U, mask::U) where U<:Integer - while true - x = randfun(r) & mask - x <= u && return x - end -end - -function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Base.BitInteger64,Bool} - isempty(r) && throw(ArgumentError("range must be non-empty")) - m = last(r) % UInt64 - first(r) % UInt64 - bw = (64 - leading_zeros(m)) % UInt # bit-width - mask = (1 % UInt64 << bw) - (1 % UInt64) - x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m, mask) : - rand_lteq(rng, rng->rand(rng, UInt64), m, mask) - (x + first(r) % UInt64) % T -end - -function rand(rng::MersenneTwister, r::UnitRange{T}) where T<:Union{Int128,UInt128} - isempty(r) && throw(ArgumentError("range must be non-empty")) - m = (last(r)-first(r)) % UInt128 - bw = (128 - leading_zeros(m)) % UInt # bit-width - mask = (1 % UInt128 << bw) - (1 % UInt128) - x = bw <= 52 ? rand_lteq(rng, rand_ui52_raw, m % UInt64, mask % UInt64) % UInt128 : - bw <= 104 ? rand_lteq(rng, rand_ui104_raw, m, mask) : - rand_lteq(rng, rng->rand(rng, UInt128), m, mask) - x % T + first(r) -end - -# Randomly draw a sample from an AbstractArray r -# (e.g. r is a range 0:2:8 or a vector [2, 3, 5, 7]) -rand(rng::AbstractRNG, r::AbstractArray) = @inbounds return r[rand(rng, 1:length(r))] - -function rand!(rng::AbstractRNG, A::AbstractArray, g::RangeGenerator) - for i in eachindex(A) - @inbounds A[i] = rand(rng, g) - end - return A -end - -rand!(rng::AbstractRNG, A::AbstractArray, r::UnitRange{<:Union{Signed,Unsigned,BigInt,Bool,Char}}) = rand!(rng, A, RangeGenerator(r)) - -function rand!(rng::AbstractRNG, A::AbstractArray, r::AbstractArray) - g = RangeGenerator(1:(length(r))) - for i in eachindex(A) - @inbounds A[i] = r[rand(rng, g)] - end - return A -end - -rand(rng::AbstractRNG, r::AbstractArray{T}, dims::Dims) where {T} = rand!(rng, Array{T}(dims), r) -rand(rng::AbstractRNG, r::AbstractArray, dims::Integer...) = rand(rng, r, convert(Dims, dims)) - -# rand from a string - -isvalid_unsafe(s::String, i) = !Base.is_valid_continuation(unsafe_load(pointer(s), i)) -isvalid_unsafe(s::AbstractString, i) = isvalid(s, i) -_endof(s::String) = sizeof(s) -_endof(s::AbstractString) = endof(s) - -function rand(rng::AbstractRNG, s::AbstractString)::Char - g = RangeGenerator(1:_endof(s)) - while true - pos = rand(rng, g) - isvalid_unsafe(s, pos) && return s[pos] - end -end - -rand(s::AbstractString) = rand(GLOBAL_RNG, s) - -## rand from a string for arrays -# we use collect(str), which is most of the time more efficient than specialized methods -# (except maybe for very small arrays) -rand!(rng::AbstractRNG, A::AbstractArray, str::AbstractString) = rand!(rng, A, collect(str)) -rand!(A::AbstractArray, str::AbstractString) = rand!(GLOBAL_RNG, A, str) -rand(rng::AbstractRNG, str::AbstractString, dims::Dims) = rand!(rng, Array{eltype(str)}(dims), str) -rand(rng::AbstractRNG, str::AbstractString, d1::Integer, dims::Integer...) = rand(rng, str, convert(Dims, tuple(d1, dims...))) -rand(str::AbstractString, dims::Dims) = rand(GLOBAL_RNG, str, dims) -rand(str::AbstractString, d1::Integer, dims::Integer...) = rand(GLOBAL_RNG, str, d1, dims...) - -## random BitArrays (AbstractRNG) - -function rand!(rng::AbstractRNG, B::BitArray) - isempty(B) && return B - Bc = B.chunks - rand!(rng, Bc) - Bc[end] &= Base._msk_end(B) - return B -end - -""" - bitrand([rng=GLOBAL_RNG], [dims...]) - -Generate a `BitArray` of random boolean values. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> bitrand(rng, 10) -10-element BitArray{1}: - true - true - true - false - true - false - false - true - false - true -``` -""" -bitrand(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(dims)) -bitrand(r::AbstractRNG, dims::Integer...) = rand!(r, BitArray(convert(Dims, dims))) - -bitrand(dims::Dims) = rand!(BitArray(dims)) -bitrand(dims::Integer...) = rand!(BitArray(convert(Dims, dims))) - -## randn() - 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) -## Tables for normal variates const ki = UInt64[0x0007799ec012f7b2,0x0000000000000000,0x0006045f4c7de363,0x0006d1aa7d5ec0a5, 0x000728fb3f60f777,0x0007592af4e9fbc0,0x000777a5c0bf655d,0x00078ca3857d2256, @@ -1120,6 +432,7 @@ const fi = 1.2602859304985975e-03] ## Tables for exponential variates + const ke = UInt64[0x000e290a13924be3,0x0000000000000000,0x0009beadebce18bf,0x000c377ac71f9e08, 0x000d4ddb99075857,0x000de893fb8ca23e,0x000e4a8e87c4328d,0x000e8dff16ae1cb9, @@ -1361,582 +674,8 @@ const fe = 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 - -""" - randn([rng=GLOBAL_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. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> randn(rng, Complex128) -0.6133070881429037 - 0.6376291670853887im - -julia> randn(rng, Complex64, (2, 3)) -2×3 Array{Complex{Float32},2}: - -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=GLOBAL_RNG) - @inbounds begin - r = rand_ui52(rng) - 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 -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 - -""" - randexp([rng=GLOBAL_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 Array{Float64,2}: - 1.5167 1.30652 0.344435 - 0.604436 2.78029 0.418516 - 0.695867 0.693292 0.643644 -``` -""" -@inline function randexp(rng::AbstractRNG=GLOBAL_RNG) - @inbounds begin - ri = rand_ui52(rng) - 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 - -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 - -""" - randn!([rng=GLOBAL_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 Array{Float64,1}: - 0.867347 - -0.901744 - -0.494479 - -0.902914 - 0.864401 -``` -""" -function randn! end - -""" - randexp!([rng=GLOBAL_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 Array{Float64,1}: - 2.48351 - 1.5167 - 0.604436 - 0.695867 - 1.30652 -``` -""" -function randexp! end - -for randfun in [:randn, :randexp] - randfun! = Symbol(randfun, :!) - @eval begin - # scalars - $randfun(rng::AbstractRNG, T::Union{Type{Float16},Type{Float32},Type{Float64}}) = - convert(T, $randfun(rng)) - $randfun(::Type{T}) where {T} = $randfun(GLOBAL_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 - - $randfun!(A::AbstractArray) = $randfun!(GLOBAL_RNG, A) - - # generating arrays - $randfun(rng::AbstractRNG, ::Type{T}, dims::Dims ) where {T} = $randfun!(rng, Array{T}(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}(dim1, dims...)) - $randfun( ::Type{T}, dims::Dims ) where {T} = $randfun(GLOBAL_RNG, T, dims) - $randfun( ::Type{T}, dims::Integer... ) where {T} = $randfun(GLOBAL_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(GLOBAL_RNG, Float64, dims) - $randfun( dims::Integer... ) = $randfun(GLOBAL_RNG, Float64, dims...) - 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)) - -## random UUID generation - -struct UUID - value::UInt128 - - UUID(u::UInt128) = new(u) -end - -""" - uuid1([rng::AbstractRNG=GLOBAL_RNG]) -> 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. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid1(rng) -2cc938da-5937-11e7-196e-0f4ef71aa64b -``` -""" -function uuid1(rng::AbstractRNG=GLOBAL_RNG) - 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=GLOBAL_RNG]) -> UUID - -Generates a version 4 (random or pseudo-random) universally unique identifier (UUID), -as specified by RFC 4122. - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid4(rng) -82015f10-44cc-4827-996e-0f4ef71aa64b -``` -""" -function uuid4(rng::AbstractRNG=GLOBAL_RNG) - u = rand(rng, UInt128) - u &= 0xffffffffffff0fff3fffffffffffffff - u |= 0x00000000000040008000000000000000 - UUID(u) -end - -""" - uuid_version(u::UUID) -> Integer - -Inspects the given UUID and returns its version (see RFC 4122). - -# Examples -```jldoctest -julia> rng = MersenneTwister(1234); - -julia> Base.Random.uuid_version(Base.Random.uuid4(rng)) -4 -``` -""" -function uuid_version(u::UUID) - Int((u.value >> 76) & 0xf) -end - -Base.convert(::Type{UInt128}, u::UUID) = u.value - -function Base.convert(::Type{UUID}, s::AbstractString) - s = lowercase(s) - - if !ismatch(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) - throw(ArgumentError("Malformed UUID string")) - end - - u = UInt128(0) - for i in [1:8; 10:13; 15:18; 20:23; 25:36] - u <<= 4 - d = s[i]-'0' - u |= 0xf & (d-39*(d>9)) - end - return UUID(u) -end - -function Base.repr(u::UUID) - u = u.value - a = Vector{UInt8}(36) - for i = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] - d = u & 0xf - a[i] = '0'+d+39*(d>9) - u >>= 4 - end - a[[24,19,14,9]] = '-' - - return String(a) -end - -Base.show(io::IO, u::UUID) = write(io, Base.repr(u)) - -# return a random string (often useful for temporary filenames/dirnames) - -""" - randstring([rng=GLOBAL_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> srand(0); randstring() -"c03rgKi1" - -julia> randstring(MersenneTwister(0), 'a':'z', 6) -"wijzek" - -julia> randstring("ACGT") -"TATCGGTC" -``` - -!!! 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 - randstring(r::AbstractRNG, chars=b, n::Integer=8) = String(rand(r, chars, n)) - randstring(r::AbstractRNG, n::Integer) = randstring(r, b, n) - randstring(chars=b, n::Integer=8) = randstring(GLOBAL_RNG, chars, n) - randstring(n::Integer) = randstring(GLOBAL_RNG, b, n) -end - -# 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) - 0 <= p <= 1 || throw(ArgumentError("probability $p not in [0,1]")) - n = length(A) - p == 1 && return copy!(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!(S::AbstractArray, A::AbstractArray, p::Real) = randsubseq!(GLOBAL_RNG, S, A, p) - -randsubseq(r::AbstractRNG, A::AbstractArray{T}, p::Real) where {T} = randsubseq!(r, T[], A, p) - -""" - randsubseq(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`. -""" -randsubseq(A::AbstractArray, p::Real) = randsubseq(GLOBAL_RNG, A, p) - -"Return a random `Int` (masked with `mask`) in ``[0, n)``, when `n <= 2^52`." -@inline function rand_lt(r::AbstractRNG, n::Int, mask::Int=nextpow2(n)-1) - # this duplicates the functionality of RangeGenerator objects, - # to optimize this special case - while true - x = (rand_ui52_raw(r) % Int) & mask - x < n && return x - end -end - -""" - shuffle!([rng=GLOBAL_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, collect(1:16)) -16-element Array{Int64,1}: - 2 - 15 - 5 - 14 - 1 - 9 - 10 - 6 - 11 - 3 - 16 - 7 - 4 - 12 - 8 - 13 -``` -""" -function shuffle!(r::AbstractRNG, a::AbstractArray) - n = length(a) - @assert n <= Int64(2)^52 - mask = nextpow2(n) - 1 - for i = n:-1:2 - (mask >> 1) == i && (mask >>= 1) - j = 1 + rand_lt(r, i, mask) - a[i], a[j] = a[j], a[i] - end - return a -end - -shuffle!(a::AbstractArray) = shuffle!(GLOBAL_RNG, a) - -""" - shuffle([rng=GLOBAL_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, collect(1:10)) -10-element Array{Int64,1}: - 6 - 1 - 10 - 2 - 3 - 9 - 5 - 7 - 4 - 8 -``` -""" -shuffle(r::AbstractRNG, a::AbstractArray) = shuffle!(r, copymutable(a)) -shuffle(a::AbstractArray) = shuffle(GLOBAL_RNG, a) - -""" - randperm([rng=GLOBAL_RNG,] n::Integer) - -Construct a random permutation of length `n`. 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), 4) -4-element Array{Int64,1}: - 2 - 1 - 4 - 3 -``` -""" -randperm(r::AbstractRNG, n::Integer) = randperm!(r, Vector{Int}(n)) -randperm(n::Integer) = randperm(GLOBAL_RNG, n) - -""" - randperm!([rng=GLOBAL_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}(4)) -4-element Array{Int64,1}: - 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_lt(r, i, mask) - if i != j # a[i] is uninitialized (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!(GLOBAL_RNG, a) - - -""" - randcycle([rng=GLOBAL_RNG,] n::Integer) - -Construct a random cyclic permutation of length `n`. The optional `rng` -argument specifies a random number generator, see [Random Numbers](@ref). - -# Examples -```jldoctest -julia> randcycle(MersenneTwister(1234), 6) -6-element Array{Int64,1}: - 3 - 5 - 4 - 6 - 1 - 2 -``` -""" -randcycle(r::AbstractRNG, n::Integer) = randcycle!(r, Vector{Int}(n)) -randcycle(n::Integer) = randcycle(GLOBAL_RNG, n) - -""" - randcycle!([rng=GLOBAL_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}(6)) -6-element Array{Int64,1}: - 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_lt(r, 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!(GLOBAL_RNG, a) - -end # module diff --git a/base/random/random.jl b/base/random/random.jl new file mode 100644 index 0000000000000..9d3cc99bc69fe --- /dev/null +++ b/base/random/random.jl @@ -0,0 +1,163 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Random + +using Base.dSFMT +using Base.GMP: Limb, MPZ +import Base: copymutable, copy, copy!, ==, hash + +export srand, + rand, rand!, + randn, randn!, + randexp, randexp!, + bitrand, + randstring, + randsubseq, randsubseq!, + shuffle, shuffle!, + randperm, randperm!, + randcycle, randcycle!, + AbstractRNG, MersenneTwister, RandomDevice, + GLOBAL_RNG, randjump + + +abstract type AbstractRNG end + +abstract type FloatInterval{T<:AbstractFloat} end + +struct CloseOpen{ T<:AbstractFloat} <: FloatInterval{T} end # interval [0,1) +struct Close1Open2{T<:AbstractFloat} <: FloatInterval{T} end # interval [1,2) + +const FloatInterval_64 = FloatInterval{Float64} +const CloseOpen_64 = CloseOpen{Float64} +const Close1Open2_64 = Close1Open2{Float64} + +CloseOpen( ::Type{T}=Float64) where {T<:AbstractFloat} = CloseOpen{T}() +Close1Open2(::Type{T}=Float64) where {T<:AbstractFloat} = Close1Open2{T}() + +const BitFloatType = Union{Type{Float16},Type{Float32},Type{Float64}} + +function __init__() + try + srand() + catch ex + Base.showerror_nostdio(ex, + "WARNING: Error during initialization of module Random") + end +end + +include("RNGs.jl") +include("generation.jl") +include("normal.jl") +include("misc.jl") + + +## rand & rand! & srand docstrings + +""" + rand([rng=GLOBAL_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:n` or `['x','y','z']`), +* an `Associative` 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)), and to ``[0, 1)`` for floating + point numbers; + +`S` defaults to [`Float64`](@ref). + +# Examples +```julia-repl +julia> rand(Int, 2) +2-element Array{Int64,1}: + 1339893410598768192 + 1575814717733606317 + +julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) +1=>2 +``` + +!!! note + The complexity of `rand(rng, s::Union{Associative,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 `IntSet`. 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=GLOBAL_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 `copy!(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 Array{Float64,1}: + 0.590845 + 0.766797 + 0.566237 + 0.460085 + 0.794026 +``` +""" +rand! + +""" + srand([rng=GLOBAL_RNG], seed) -> rng + srand([rng=GLOBAL_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 `srand`, `rng` is equivalent to a newly created +object initialized with the same seed. + +# Examples +```julia-repl +julia> srand(1234); + +julia> x1 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> srand(1234); + +julia> x2 = rand(2) +2-element Array{Float64,1}: + 0.590845 + 0.766797 + +julia> x1 == x2 +true + +julia> rng = MersenneTwister(1234); rand(rng, 2) == x1 +true + +julia> MersenneTwister(1) == srand(rng, 1) +true + +julia> rand(srand(rng), Bool) # not reproducible +true + +julia> rand(srand(rng), Bool) +false + +julia> rand(MersenneTwister(), Bool) # not reproducible either +true +``` +""" +srand(rng::AbstractRNG, ::Void) = srand(rng) + +end # module diff --git a/base/range.jl b/base/range.jl index 381efd9ea5f2f..ba90a98cbbb26 100644 --- a/base/range.jl +++ b/base/range.jl @@ -205,6 +205,8 @@ end StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = StepRangeLen{typeof(ref+0*step),R,S}(ref, step, len, offset) +StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = + StepRangeLen{T,R,S}(ref, step, len, offset) ## linspace and logspace @@ -239,7 +241,7 @@ julia> linspace(1.3,2.9,9) 1.3:0.2:2.9 ``` """ -linspace(start, stop, len::Real=50) = linspace(promote_noncircular(start, stop)..., Int(len)) +linspace(start, stop, len::Real=50) = linspace(promote(start, stop)..., Int(len)) linspace(start::T, stop::T, len::Real=50) where {T} = linspace(start, stop, Int(len)) linspace(start::Real, stop::Real, len::Integer) = linspace(promote(start, stop)..., len) @@ -370,9 +372,12 @@ julia> step(linspace(2.5,10.9,85)) """ step(r::StepRange) = r.step step(r::AbstractUnitRange) = 1 -step(r::StepRangeLen) = r.step +step(r::StepRangeLen{T}) where {T} = T(r.step) step(r::LinSpace) = (last(r)-first(r))/r.lendiv +step_hp(r::StepRangeLen) = r.step +step_hp(r::Range) = step(r) + unsafe_length(r::Range) = length(r) # generic fallback function unsafe_length(r::StepRange) @@ -455,10 +460,9 @@ done(r::StepRange, i) = isempty(r) | (i < min(r.start, r.stop)) | (i > max(r.sta done(r::StepRange, i::Integer) = isempty(r) | (i == oftype(i, r.stop) + r.step) -# see also twiceprecision.jl -start(r::StepRangeLen) = (unsafe_getindex(r, 1), 1) -next(r::StepRangeLen{T}, s) where {T} = s[1], (T(s[1]+r.step), s[2]+1) -done(r::StepRangeLen, s) = s[2] > length(r) +start(r::StepRangeLen) = 1 +next(r::StepRangeLen{T}, i) where {T} = unsafe_getindex(r, i), i+1 +done(r::StepRangeLen, i) = i > length(r) start(r::UnitRange{T}) where {T} = oftype(r.start + oneunit(T), r.start) next(r::AbstractUnitRange{T}, i) where {T} = (convert(T, i), i + oneunit(T)) @@ -496,7 +500,7 @@ end function getindex(v::Range{T}, i::Integer) where T @_inline_meta - ret = convert(T, first(v) + (i - 1)*step(v)) + ret = convert(T, first(v) + (i - 1)*step_hp(v)) ok = ifelse(step(v) > zero(step(v)), (ret <= v.stop) & (ret >= v.start), (ret <= v.start) & (ret >= v.stop)) @@ -516,6 +520,11 @@ function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T T(r.ref + u*r.step) end +function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T + u = i - r.offset + r.ref + u*r.step +end + function unsafe_getindex(r::LinSpace, i::Integer) lerpi.(i-1, r.lendiv, r.start, r.stop) end @@ -556,11 +565,14 @@ function getindex(r::StepRange, s::Range{<:Integer}) range(st, step(r)*step(s), length(s)) end -function getindex(r::StepRangeLen, s::OrdinalRange{<:Integer}) +function getindex(r::StepRangeLen{T}, s::OrdinalRange{<:Integer}) where {T} @_inline_meta @boundscheck checkbounds(r, s) - vfirst = unsafe_getindex(r, first(s)) - return StepRangeLen(vfirst, r.step*step(s), length(s)) + # Find closest approach to offset by s + ind = linearindices(s) + offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) + ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) + return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) end function getindex(r::LinSpace, s::OrdinalRange{<:Integer}) @@ -725,16 +737,17 @@ end ## linear operations on ranges ## -(r::OrdinalRange) = range(-first(r), -step(r), length(r)) --(r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset) +-(r::StepRangeLen{T,R,S}) where {T,R,S} = + StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset) -(r::LinSpace) = LinSpace(-r.start, -r.stop, length(r)) +(x::Real, r::AbstractUnitRange) = range(x + first(r), length(r)) # For #18336 we need to prevent promotion of the step type: +(x::Number, r::AbstractUnitRange) = range(x + first(r), step(r), length(r)) +(x::Number, r::Range) = (x+first(r)):step(r):(x+last(r)) -function +(x::Number, r::StepRangeLen) +function +(x::Number, r::StepRangeLen{T}) where T newref = x + r.ref - StepRangeLen{eltype(newref),typeof(newref),typeof(r.step)}(newref, r.step, length(r), r.offset) + StepRangeLen{typeof(T(r.ref) + x)}(newref, r.step, length(r), r.offset) end function +(x::Number, r::LinSpace) LinSpace(x + r.start, x + r.stop, r.len) @@ -750,15 +763,18 @@ end -(r::Range, x::Number) = +(-x, r) *(x::Number, r::Range) = range(x*first(r), x*step(r), length(r)) -*(x::Number, r::StepRangeLen) = StepRangeLen(x*r.ref, x*r.step, length(r), r.offset) +*(x::Number, r::StepRangeLen{T}) where {T} = + StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) *(x::Number, r::LinSpace) = LinSpace(x * r.start, x * r.stop, r.len) # separate in case of noncommutative multiplication *(r::Range, x::Number) = range(first(r)*x, step(r)*x, length(r)) -*(r::StepRangeLen, x::Number) = StepRangeLen(r.ref*x, r.step*x, length(r), r.offset) +*(r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) *(r::LinSpace, x::Number) = LinSpace(r.start * x, r.stop * x, r.len) /(r::Range, x::Number) = range(first(r)/x, step(r)/x, length(r)) -/(r::StepRangeLen, x::Number) = StepRangeLen(r.ref/x, r.step/x, length(r), r.offset) +/(r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) /(r::LinSpace, x::Number) = LinSpace(r.start / x, r.stop / x, r.len) /(x::Number, r::Range) = [ x/y for y=r ] @@ -931,7 +947,7 @@ function _define_range_op(@nospecialize f) $f(r1::Union{StepRangeLen, OrdinalRange, LinSpace}, r2::Union{StepRangeLen, OrdinalRange, LinSpace}) = - $f(promote_noncircular(r1, r2)...) + $f(promote(r1, r2)...) end end _define_range_op(:+) diff --git a/base/rational.jl b/base/rational.jl index 87c0c2cb0cdd9..a23c38dc2a59c 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -114,7 +114,6 @@ widen(::Type{Rational{T}}) where {T} = Rational{widen(T)} 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`. -If `T` is not provided, it defaults to `Int`. ```jldoctest julia> rationalize(5.6) @@ -232,7 +231,7 @@ typemax(::Type{Rational{T}}) where {T<:Integer} = one(T)//zero(T) isinteger(x::Rational) = x.den == 1 -(x::Rational) = (-x.num) // x.den -function -(x::Rational{T}) where T<:Signed +function -(x::Rational{T}) where T<:BitSigned x.num == typemin(T) && throw(OverflowError("rational numerator is typemin(T)")) (-x.num) // x.den end diff --git a/base/reduce.jl b/base/reduce.jl index 3ca6351ebfb99..626809a73721a 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -359,7 +359,7 @@ julia> sum(1:20) ``` """ sum(a) = mapreduce(identity, +, a) -sum(a::AbstractArray{Bool}) = countnz(a) +sum(a::AbstractArray{Bool}) = count(a) # Kahan (compensated) summation: O(1) error growth, at the expense @@ -670,7 +670,7 @@ function contains(eq::Function, itr, x) end -## countnz & count +## count """ count(p, itr) -> Integer @@ -703,22 +703,3 @@ function count(pred, a::AbstractArray) return n end count(itr) = count(identity, itr) - -""" - countnz(A) -> Integer - -Counts the number of nonzero values in array `A` (dense or sparse). Note that this is not a constant-time operation. -For sparse matrices, one should usually use [`nnz`](@ref), which returns the number of stored values. - -```jldoctest -julia> A = [1 2 4; 0 0 1; 1 1 0] -3×3 Array{Int64,2}: - 1 2 4 - 0 0 1 - 1 1 0 - -julia> countnz(A) -6 -``` -""" -countnz(a) = count(x -> x != 0, a) diff --git a/base/reducedim.jl b/base/reducedim.jl index 84a168e1245df..b2bee574032e7 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -23,7 +23,8 @@ reduced_indices(inds::Indices, d::Int) = reduced_indices(inds, d, OneTo(1)) function reduced_indices0(inds::Indices{N}, d::Int) where N d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) if d <= N - return reduced_indices(inds, d, (inds[d] == OneTo(0) ? OneTo(0) : OneTo(1))) + ind = inds[d] + return reduced_indices(inds, d, (isempty(ind) ? ind : OneTo(1))) else return inds end @@ -51,30 +52,50 @@ function reduced_indices0(inds::Indices{N}, region) where N if d < 1 throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) elseif d <= N - rinds[d] = oftype(rinds[d], (rinds[d] == OneTo(0) ? OneTo(0) : OneTo(1))) + rind = rinds[d] + rinds[d] = oftype(rind, (isempty(rind) ? rind : OneTo(1))) end end tuple(rinds...)::typeof(inds) end - ###### Generic reduction functions ##### ## initialization -for (Op, initfun) in ((:(typeof(+)), :zero), (:(typeof(*)), :one), (:(typeof(scalarmax)), :typemin), (:(typeof(scalarmin)), :typemax), (:(typeof(max)), :typemin), (:(typeof(min)), :typemax)) - @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool) where {T} = (init && fill!(a, $(initfun)(T)); a) +for (Op, initfun) in ((:(typeof(+)), :zero), (:(typeof(*)), :one)) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a) +end + +for Op in (:(typeof(scalarmax)), :(typeof(scalarmin)), :(typeof(max)), :(typeof(min))) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && copyfirst!(a, src); a) end for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false)) - @eval initarray!(a::AbstractArray, ::$(Op), init::Bool) = (init && fill!(a, $initval); a) + @eval initarray!(a::AbstractArray, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a) end reducedim_initarray(A::AbstractArray, region, v0, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), v0) reducedim_initarray(A::AbstractArray, region, v0::T) where {T} = reducedim_initarray(A, region, v0, T) -reducedim_initarray0(A::AbstractArray, region, v0, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices0(A,region)), v0) -reducedim_initarray0(A::AbstractArray, region, v0::T) where {T} = reducedim_initarray0(A, region, v0, T) +function reducedim_initarray0(A::AbstractArray{T}, region, f, ops) where T + ri = reduced_indices0(A, region) + if isempty(A) + if prod(map(length, reduced_indices(A, region))) != 0 + reducedim_initarray0_empty(A, region, f, ops) # ops over empty slice of A + else + R = f == identity ? T : Core.Inference.return_type(f, (T,)) + similar(A, R, ri) + end + else + R = f == identity ? T : typeof(f(first(A))) + si = similar(A, R, ri) + mapfirst!(f, si, A) + end +end + +reducedim_initarray0_empty(A::AbstractArray, region, f, ops) = mapslices(x->ops(f.(x)), A, region) +reducedim_initarray0_empty(A::AbstractArray, region,::typeof(identity), ops) = mapslices(ops, A, region) # TODO: better way to handle reducedim initialization # @@ -91,7 +112,7 @@ function reducedim_init(f, op::typeof(*), A::AbstractArray, region) end function _reducedim_init(f, op, fv, fop, A, region) T = promote_union(eltype(A)) - if method_exists(zero, Tuple{Type{T}}) + if applicable(zero, T) x = f(zero(T)) z = op(fv(x), fv(x)) Tr = typeof(z) == typeof(x) && !isbits(T) ? T : typeof(z) @@ -106,8 +127,8 @@ reducedim_init(f, op::typeof(max), A::AbstractArray, region) = reducedim_init(f, reducedim_init(f, op::typeof(min), A::AbstractArray, region) = reducedim_init(f, scalarmin, A, region) reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(max), A::AbstractArray, region) = reducedim_init(f, scalarmax, A, region) -reducedim_init(f, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, typemin(f(zero(T)))) -reducedim_init(f, op::typeof(scalarmin), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, typemax(f(zero(T)))) +reducedim_init(f, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, maximum) +reducedim_init(f, op::typeof(scalarmin), A::AbstractArray{T}, region) where {T} = reducedim_initarray0(A, region, f, minimum) reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(scalarmax), A::AbstractArray{T}, region) where {T} = reducedim_initarray(A, region, zero(f(zero(T)))) @@ -132,7 +153,6 @@ end reducedim_init(f::Union{typeof(identity),typeof(abs),typeof(abs2)}, op::typeof(+), A::AbstractArray{Bool}, region) = reducedim_initarray(A, region, 0) - ## generic (map)reduction has_fast_linear_indexing(a::AbstractArray) = false @@ -169,6 +189,23 @@ function check_reducedims(R, A) 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, R::AbstractArray, A::AbstractArray) + lsiz = check_reducedims(R, A) + iA = indices(A) + iR = indices(R) + t = [] + for i in 1:length(iR) + iAi = iA[i] + push!(t, iAi == iR[i] ? iAi : first(iAi)) + end + map!(f, R, view(A, t...)) +end + function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) lsiz = check_reducedims(R,A) isempty(A) && return R @@ -211,7 +248,7 @@ mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) = (_mapreducedim!(f, op, R, A); R) reducedim!(op, R::AbstractArray{RT}, A::AbstractArray) where {RT} = - mapreducedim!(identity, op, R, A, zero(RT)) + mapreducedim!(identity, op, R, A) """ mapreducedim(f, op, A, region[, v0]) @@ -277,7 +314,6 @@ julia> reducedim(max, a, 1) reducedim(op, A::AbstractArray, region, v0) = mapreducedim(identity, op, A, region, v0) reducedim(op, A::AbstractArray, region) = mapreducedim(identity, op, A, region) - ##### Specific reduction functions ##### """ sum(A, dims) @@ -577,7 +613,7 @@ for (fname, op) in [(:sum, :+), (:prod, :*), fname! = Symbol(fname, '!') @eval begin $(fname!)(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = - mapreducedim!(f, $(op), initarray!(r, $(op), init), A) + mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A) $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) $(fname)(f::Function, A::AbstractArray, region) = @@ -586,9 +622,9 @@ for (fname, op) in [(:sum, :+), (:prod, :*), end end - ##### findmin & findmax ##### - +# The initial values of Rval are not used if the correponding indices in Rind are 0. +# function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} (isempty(Rval) || isempty(A)) && return Rval, Rind lsiz = check_reducedims(Rval, A) @@ -599,7 +635,9 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. indsAt, indsRt = safe_tail(indices(A)), safe_tail(indices(Rval)) keep, Idefault = Broadcast.shapeindexer(indsAt, indsRt) - k = 0 + ks = keys(A) + k, kss = next(ks, start(ks)) + zi = zero(eltype(ks)) if reducedim1(Rval, A) i1 = first(indices1(Rval)) @inbounds for IA in CartesianRange(indsAt) @@ -607,12 +645,12 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} tmpRv = Rval[i1,IR] tmpRi = Rind[i1,IR] for i in indices(A,1) - k += 1 tmpAv = A[i,IA] - if f(tmpAv, tmpRv) + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) tmpRv = tmpAv tmpRi = k end + k, kss = next(ks, kss) end Rval[i1,IR] = tmpRv Rind[i1,IR] = tmpRi @@ -621,95 +659,110 @@ function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} @inbounds for IA in CartesianRange(indsAt) IR = Broadcast.newindex(IA, keep, Idefault) for i in indices(A, 1) - k += 1 tmpAv = A[i,IA] - if f(tmpAv, Rval[i,IR]) + tmpRv = Rval[i,IR] + tmpRi = Rind[i,IR] + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) Rval[i,IR] = tmpAv Rind[i,IR] = k end + k, kss = next(ks, kss) end end end Rval, Rind end - """ findmin!(rval, rind, A, [init=true]) -> (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. """ function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(<, initarray!(rval, scalarmin, init), rind, A) + findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ findmin(A, region) -> (minval, index) For an array input, returns the value and index of the minimum over the given region. +`NaN` is treated as less than all other values. # Examples ```jldoctest -julia> A = [1 2; 3 4] +julia> A = [1.0 2; 3 4] 2×2 Array{Int64,2}: - 1 2 - 3 4 + 1.0 2.0 + 3.0 4.0 julia> findmin(A, 1) -([1 2], [1 3]) +([1.0 2.0], [1 3]) julia> findmin(A, 2) -([1; 3], [1; 2]) +([1.0; 3.0], [1; 2]) ``` """ function findmin(A::AbstractArray{T}, region) where T + ri = reduced_indices0(A, region) if isempty(A) - return (similar(A, reduced_indices0(A, region)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region))) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + (similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri)) + else + findminmax!(isless, fill!(similar(A, ri), first(A)), + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end - return findminmax!(<, reducedim_initarray0(A, region, typemax(T)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region)), A) end +isgreater(a, b) = isless(b,a) + """ findmax!(rval, rind, A, [init=true]) -> (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. """ function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; init::Bool=true) - findminmax!(>, initarray!(rval, scalarmax, init), rind, A) + findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) end """ findmax(A, region) -> (maxval, index) For an array input, returns the value and index of the maximum over the given region. +`NaN` is treated as greater than all other values. # Examples ```jldoctest -julia> A = [1 2; 3 4] +julia> A = [1.0 2; 3 4] 2×2 Array{Int64,2}: - 1 2 - 3 4 + 1.0 2.0 + 3.0 4.0 julia> findmax(A,1) -([3 4], [2 4]) +([3.0 4.0], [2 4]) julia> findmax(A,2) -([2; 4], [3; 4]) +([2.0; 4.0], [3; 4]) ``` """ function findmax(A::AbstractArray{T}, region) where T + ri = reduced_indices0(A, region) if isempty(A) - return (similar(A, reduced_indices0(A,region)), - similar(dims->zeros(Int, dims), reduced_indices0(A,region))) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + similar(A, ri), similar(dims->zeros(eltype(keys(A)), dims), ri) + else + findminmax!(isgreater, fill!(similar(A, ri), first(A)), + similar(dims->zeros(eltype(keys(A)), dims), ri), A) end - return findminmax!(>, reducedim_initarray0(A, region, typemin(T)), - similar(dims->zeros(Int, dims), reduced_indices0(A, region)), A) end reducedim1(R, A) = length(indices1(R)) == 1 diff --git a/base/reflection.jl b/base/reflection.jl index 032c20c4b4009..41b67dff32964 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -167,7 +167,7 @@ datatype_name(t::UnionAll) = datatype_name(unwrap_unionall(t)) """ Base.datatype_module(t::DataType) -> Module -Determine the module containing the definition of a `DataType`. +Determine the module containing the definition of a (potentially UnionAll-wrapped) `DataType`. # Examples ```jldoctest @@ -184,6 +184,7 @@ Foo ``` """ datatype_module(t::DataType) = t.name.module +datatype_module(t::UnionAll) = datatype_module(unwrap_unionall(t)) """ isconst(m::Module, s::Symbol) -> Bool @@ -216,7 +217,11 @@ macro isdefined(s::Symbol) return Expr(:isdefined, esc(s)) end -# return an integer such that object_id(x)==object_id(y) if x===y +""" + object_id(x) + +Get a hash value for `x` based on object identity. `object_id(x)==object_id(y)` if `x === y`. +""" object_id(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) struct DataTypeLayout @@ -554,20 +559,43 @@ function to_tuple_type(@nospecialize(t)) t end -tt_cons(@nospecialize(t), @nospecialize(tup)) = (@_pure_meta; Tuple{t, (isa(tup, Type) ? tup.parameters : tup)...}) +function signature_type(@nospecialize(f), @nospecialize(args)) + f_type = isa(f, Type) ? Type{f} : typeof(f) + arg_types = isa(args, Type) ? args.parameters : args + return Tuple{f_type, arg_types...} +end """ - code_lowered(f, types) + code_lowered(f, types, expand_generated = true) -Returns an array of lowered ASTs for the methods matching the given generic function and type signature. +Return an array of lowered ASTs for the methods matching the given generic function and type signature. + +If `expand_generated` is `false`, then the `CodeInfo` instances returned for `@generated` +methods will correspond to the generators' lowered ASTs. If `expand_generated` is `true`, +these `CodeInfo` instances will correspond to the lowered ASTs of the method bodies yielded +by expanding the generators. + +Note that an error will be thrown if `types` are not leaf types when `expand_generated` is +`true` and the corresponding method is a `@generated` method. """ -function code_lowered(@nospecialize(f), @nospecialize t = Tuple) - asts = map(methods(f, t)) do m - return uncompressed_ast(m::Method) +function code_lowered(@nospecialize(f), @nospecialize(t = Tuple), expand_generated::Bool = true) + return map(method_instances(f, t)) do m + if expand_generated && isgenerated(m) + if isa(m, Core.MethodInstance) + return Core.Inference.get_staged(m) + else # isa(m, Method) + error("Could not expand generator for `@generated` method ", m, ". ", + "This can happen if the provided argument types (", t, ") are ", + "not leaf types, but the `expand_generated` argument is `true`.") + end + end + return uncompressed_ast(m) end - return asts end +isgenerated(m::Method) = isdefined(m, :generator) +isgenerated(m::Core.MethodInstance) = isgenerated(m.def) + # low-level method lookup functions used by the compiler unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) @@ -578,8 +606,7 @@ _uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} + tt = signature_type(f, t) return _methods_by_ftype(tt, lim, world) end @@ -631,8 +658,7 @@ end methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt) function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = isa(t,Type) ? Tuple{ft, t.parameters...} : Tuple{ft, t...} + tt = signature_type(f, t) world = typemax(UInt) min = UInt[typemin(UInt)] max = UInt[typemax(UInt)] @@ -682,45 +708,43 @@ function length(mt::MethodTable) end isempty(mt::MethodTable) = (mt.defs === nothing) -uncompressed_ast(m::Method) = uncompressed_ast(m, isdefined(m,:source) ? m.source : m.generator.inferred) +uncompressed_ast(m::Method) = uncompressed_ast(m, isdefined(m, :source) ? m.source : m.generator.inferred) uncompressed_ast(m::Method, s::CodeInfo) = s uncompressed_ast(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Any), m, s)::CodeInfo - -# this type mirrors jl_cghooks_t (documented in julia.h) -struct CodegenHooks - module_setup::Ptr{Void} - module_activation::Ptr{Void} - raise_exception::Ptr{Void} - - CodegenHooks(;module_setup=nothing, module_activation=nothing, raise_exception=nothing) = - new(pointer_from_objref(module_setup), - pointer_from_objref(module_activation), - pointer_from_objref(raise_exception)) +uncompressed_ast(m::Core.MethodInstance) = uncompressed_ast(m.def) + +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt = typemax(UInt)) + tt = signature_type(f, t) + results = Vector{Union{Method,Core.MethodInstance}}() + for method_data in _methods_by_ftype(tt, -1, world) + mtypes, msp, m = method_data + instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) + push!(results, ifelse(isa(instance, Core.MethodInstance), instance, m)) + end + return results end # this type mirrors jl_cgparams_t (documented in julia.h) struct CodegenParams cached::Cint - runtime::Cint - exceptions::Cint track_allocations::Cint code_coverage::Cint static_alloc::Cint - dynamic_alloc::Cint + prefer_specsig::Cint - hooks::CodegenHooks + module_setup::Any + module_activation::Any + raise_exception::Any CodegenParams(;cached::Bool=true, - runtime::Bool=true, exceptions::Bool=true, track_allocations::Bool=true, code_coverage::Bool=true, - static_alloc::Bool=true, dynamic_alloc::Bool=true, - hooks::CodegenHooks=CodegenHooks()) = + static_alloc::Bool=true, prefer_specsig::Bool=false, + module_setup=nothing, module_activation=nothing, raise_exception=nothing) = new(Cint(cached), - Cint(runtime), Cint(exceptions), Cint(track_allocations), Cint(code_coverage), - Cint(static_alloc), Cint(dynamic_alloc), - hooks) + Cint(static_alloc), Cint(prefer_specsig), + module_setup, module_activation, raise_exception) end # Printing code representations in IR and assembly @@ -735,8 +759,7 @@ function _dump_function(@nospecialize(f), @nospecialize(t), native::Bool, wrappe world = typemax(UInt) meth = which(f, t) t = to_tuple_type(t) - ft = isa(f, Type) ? Type{f} : typeof(f) - tt = Tuple{ft, t.parameters...} + tt = signature_type(f, t) (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), tt, meth.sig)::SimpleVector meth = func_for_method_checked(meth, ti) linfo = ccall(:jl_specializations_get_linfo, Ref{Core.MethodInstance}, (Any, Any, Any, UInt), meth, ti, env, world) @@ -773,10 +796,10 @@ function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::B end """ - code_llvm([io], f, types) + code_llvm([io=STDOUT,], f, types) Prints the LLVM bitcodes generated for running the method matching the given generic -function and type signature to `io` which defaults to `STDOUT`. +function and type signature to `io`. All metadata and dbg.* calls are removed from the printed bitcode. Use code_llvm_raw for the full IR. """ @@ -786,11 +809,11 @@ code_llvm(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, t code_llvm_raw(@nospecialize(f), @nospecialize(types=Tuple)) = code_llvm(STDOUT, f, types, false) """ - code_native([io], f, types, [syntax]) + code_native([io=STDOUT,], f, types, syntax=:att) Prints the native assembly instructions generated for running the method matching the given -generic function and type signature to `io` which defaults to `STDOUT`. -Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. Output is AT&T syntax by default. +generic function and type signature to `io`. +Switch assembly syntax using `syntax` symbol parameter set to `:att` for AT&T syntax or `:intel` for Intel syntax. """ code_native(io::IO, @nospecialize(f), @nospecialize(types=Tuple), syntax::Symbol=:att) = print(io, _dump_function(f, types, true, false, false, false, syntax)) @@ -861,24 +884,12 @@ function which(@nospecialize(f), @nospecialize(t)) throw(ArgumentError("argument is not a generic function")) end t = to_tuple_type(t) - if isleaftype(t) - ms = methods(f, t) - isempty(ms) && error("no method found for the specified argument types") - length(ms)!=1 && error("no unique matching method for the specified argument types") - return first(ms) - else - ft = isa(f,Type) ? Type{f} : typeof(f) - tt = Tuple{ft, t.parameters...} - m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) - if m === nothing - error("no method found for the specified argument types") - end - meth = m.func::Method - if ccall(:jl_has_call_ambiguities, Cint, (Any, Any), tt, meth) != 0 - error("method match is ambiguous for the specified argument types") - end - return meth + tt = signature_type(f, t) + m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) + if m === nothing + error("no unique matching method found for the specified argument types") end + return m.func::Method end """ @@ -975,7 +986,7 @@ true """ function method_exists(@nospecialize(f), @nospecialize(t), world=typemax(UInt)) t = to_tuple_type(t) - t = Tuple{isa(f,Type) ? Type{f} : typeof(f), t.parameters...} + t = signature_type(f, t) return ccall(:jl_method_exists, Cint, (Any, Any, UInt), typeof(f).name.mt, t, world) != 0 end diff --git a/base/refpointer.jl b/base/refpointer.jl index 44a625c76b6cf..9ad3be1d89dbf 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -53,7 +53,7 @@ Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x) convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x) function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T - if isbits(T) + if isbits(T) || isbitsunion(T) return convert(P, pointer_from_objref(b)) elseif isleaftype(T) return convert(P, pointer_from_objref(b.x)) diff --git a/base/regex.jl b/base/regex.jl index 75e8dcc70e821..ffea88ec79eb8 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -141,6 +141,11 @@ function getindex(m::RegexMatch, name::Symbol) end getindex(m::RegexMatch, name::AbstractString) = m[Symbol(name)] +""" + ismatch(r::Regex, s::AbstractString) -> Bool + +Test whether a string contains a match of the given regular expression. +""" function ismatch(r::Regex, s::AbstractString, offset::Integer=0) compile(r) return PCRE.exec(r.regex, String(s), offset, r.match_options, @@ -155,6 +160,16 @@ end (r::Regex)(s) = ismatch(r, s) +""" + match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) + +Search for the first match of the regular expression `r` in `s` and return a `RegexMatch` +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. +""" +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 @@ -175,6 +190,11 @@ match(r::Regex, s::AbstractString, i::Integer) = throw(ArgumentError( "regex matching is only available for the String type; use String(s) to convert" )) +""" + matchall(r::Regex, s::AbstractString[, overlap::Bool=false]) -> Vector{AbstractString} + +Return a vector of the matching substrings from [`eachmatch`](@ref). +""" function matchall(re::Regex, str::String, overlap::Bool=false) regex = compile(re).regex n = sizeof(str) @@ -251,10 +271,10 @@ function _write_capture(io, re, group) end function _replace(io, repl_s::SubstitutionString, str, r, re) - const SUB_CHAR = '\\' - const GROUP_CHAR = 'g' - const LBRACKET = '<' - const RBRACKET = '>' + SUB_CHAR = '\\' + GROUP_CHAR = 'g' + LBRACKET = '<' + RBRACKET = '>' repl = repl_s.string i = start(repl) e = endof(repl) @@ -362,6 +382,13 @@ function eachmatch(re::Regex, str::AbstractString, ovr::Bool) RegexMatchIterator(re,str,ovr) end +""" + eachmatch(r::Regex, s::AbstractString[, overlap::Bool=false]) + +Search for all matches of a the regular expression `r` in `s` and return a 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. +""" eachmatch(re::Regex, str::AbstractString) = RegexMatchIterator(re,str) ## comparison ## diff --git a/base/repl/LineEdit.jl b/base/repl/LineEdit.jl index 8c894bf32ea3f..ebf27b7a7bad9 100644 --- a/base/repl/LineEdit.jl +++ b/base/repl/LineEdit.jl @@ -7,7 +7,7 @@ using ..Terminals import ..Terminals: raw!, width, height, cmove, getX, getY, clear_line, beep -import Base: ensureroom, peek, show, AnyDict +import Base: ensureroom, peek, show, AnyDict, position abstract type TextInterface end abstract type ModeState end @@ -37,19 +37,25 @@ end show(io::IO, x::Prompt) = show(io, string("Prompt(\"", prompt_string(x.prompt), "\",...)")) +"Maximum number of entries in the kill ring queue. +Beyond this number, oldest entries are discarded first." +const KILL_RING_MAX = Ref(100) + mutable struct MIState interface::ModalInterface current_mode::TextInterface aborted::Bool mode_state::Dict - kill_buffer::String - previous_key::Array{Char,1} + kill_ring::Vector{String} + kill_idx::Int + previous_key::Vector{Char} key_repeats::Int + last_action::Symbol end -MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0) +MIState(i, c, a, m) = MIState(i, c, a, m, String[], 0, Char[], 0, :begin) function show(io::IO, s::MIState) - print(io, "MI State (", s.current_mode, " active)") + print(io, "MI State (", mode(s), " active)") end struct InputAreaState @@ -61,19 +67,37 @@ mutable struct PromptState <: ModeState terminal::AbstractTerminal p::Prompt input_buffer::IOBuffer + 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 end +setmark(s) = mark(buffer(s)) + +# the default mark is 0 +getmark(s) = max(0, buffer(s).mark) + +const Region = Pair{<:Integer,<:Integer} + +_region(s) = getmark(s) => position(s) +region(s) = Pair(extrema(_region(s))...) + +indexes(reg::Region) = first(reg)+1:last(reg) + +content(s, reg::Region = 0=>buffer(s).size) = String(buffer(s).data[indexes(reg)]) + +const REGION_ANIMATION_DURATION = Ref(0.2) + 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.input_buffer)):end] + rest = str[nextind(str, position(s)):end] return count(c->(c == '\n'), rest) end @@ -90,12 +114,21 @@ complete_line(c::EmptyCompletionProvider, s) = [], true, true terminal(s::IO) = s terminal(s::PromptState) = s.terminal -for f in [:terminal, :edit_insert, :on_enter, :add_history, :buffer, :edit_backspace, :(Base.isempty), - :replace_line, :refresh_multi_line, :input_string, :edit_move_left, :edit_move_right, - :edit_move_word_left, :edit_move_word_right, :update_display_buffer] - @eval ($f)(s::MIState, args...) = $(f)(s.mode_state[s.current_mode], args...) +for f in [:terminal, :on_enter, :add_history, :buffer, :(Base.isempty), + :replace_line, :refresh_multi_line, :input_string, :update_display_buffer, + :empty_undo, :push_undo, :pop_undo] + @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...) + $(f)(state(s), args...) + return $(Expr(:quote, f)) + end end + function common_prefix(completions) ret = "" c1 = completions[1] @@ -139,7 +172,12 @@ function show_completions(s::PromptState, completions) end # Prompt Completions -complete_line(s::MIState) = complete_line(s.mode_state[s.current_mode], s.key_repeats) +function complete_line(s::MIState) + complete_line(state(s), s.key_repeats) + refresh_line(s) + :complete_line +end + function complete_line(s::PromptState, repeats) completions, partial, should_complete = complete_line(s.p.complete, s) if isempty(completions) @@ -150,17 +188,17 @@ function complete_line(s::PromptState, repeats) show_completions(s, completions) elseif length(completions) == 1 # Replace word by completion - prev_pos = position(s.input_buffer) - seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, completions[1]) + 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.input_buffer) - seek(s.input_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.input_buffer), prev_pos, p) + 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 @@ -286,6 +324,17 @@ function reset_key_repeats(f::Function, s::MIState) end end +edit_exchange_point_and_mark(s::MIState) = + edit_exchange_point_and_mark(buffer(s)) && (refresh_line(s); true) + +function edit_exchange_point_and_mark(buf::IOBuffer) + m = getmark(buf) + m == position(buf) && return false + mark(buf) + seek(buf, m) + true +end + char_move_left(s::PromptState) = char_move_left(s.input_buffer) function char_move_left(buf::IOBuffer) while position(buf) > 0 @@ -315,7 +364,7 @@ end edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) && refresh_line(s) function edit_move_word_left(s) - if position(s.input_buffer) > 0 + if position(s) > 0 char_move_word_left(s.input_buffer) refresh_line(s) end @@ -386,7 +435,7 @@ function edit_move_up(buf::IOBuffer) npos = rsearch(buf.data, '\n', position(buf)) npos == 0 && return false # we're in the first line # We're interested in character count, not byte count - offset = length(String(buf.data[(npos+1):(position(buf))])) + offset = length(content(buf, npos => position(buf))) npos2 = rsearch(buf.data, '\n', npos-1) seek(buf, npos2) for _ = 1:offset @@ -428,38 +477,42 @@ function edit_move_down(s) changed end -# splice! for IOBuffer: convert from 0-indexed positions, update the size, -# and keep the cursor position stable with the text -function splice_buffer!(buf::IOBuffer, r::UnitRange{<:Integer}, ins::AbstractString = "") +# 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, r::Region=region(s), ins::AbstractString = "") + A, B = first(r), last(r) + A >= B && isempty(ins) && return String(ins) + buf = buffer(s) pos = position(buf) - if !isempty(r) && pos in r - seek(buf, first(r)) - elseif pos > last(r) - seek(buf, pos - length(r)) - end - splice!(buf.data, r + 1, Vector{UInt8}(ins)) # position(), etc, are 0-indexed - buf.size = buf.size + sizeof(ins) - length(r) + if A <= pos < B + seek(buf, A) + elseif B <= pos + seek(buf, pos - B + A) + end + if A < buf.mark < B + buf.mark = A + elseif A < B <= buf.mark + buf.mark += sizeof(ins) - B + A + end + ret = splice!(buf.data, A+1:B, Vector{UInt8}(ins)) # position(), etc, are 0-indexed + buf.size = buf.size + sizeof(ins) - B + A seek(buf, position(buf) + sizeof(ins)) + String(ret) end -function edit_replace(s, from, to, str) - splice_buffer!(buffer(s), from:to-1, str) -end +edit_splice!(s, ins::AbstractString) = edit_splice!(s, region(s), ins) function edit_insert(s::PromptState, c) + push_undo(s) buf = s.input_buffer - function line_size() - p = position(buf) - seek(buf, rsearch(buf.data, '\n', p)) - ls = p - position(buf) - seek(buf, p) - return ls - end str = string(c) edit_insert(buf, str) - offset = s.ias.curs_row == 1 ? sizeof(prompt_string(s.p.prompt)) : s.indent + offset = s.ias.curs_row == 1 || s.indent < 0 ? + sizeof(prompt_string(s.p.prompt)) : s.indent if !('\n' in str) && eof(buf) && - ((line_size() + offset + sizeof(str) - 1) < width(terminal(s))) + ((position(buf) - beginofline(buf) + # size of current line + offset + sizeof(str) - 1) < width(terminal(s))) # Avoid full update when appending characters to the end # and an update of curs_row isn't necessary (conservatively estimated) write(terminal(s), str) @@ -473,35 +526,94 @@ function edit_insert(buf::IOBuffer, c) return write(buf, c) else s = string(c) - splice_buffer!(buf, position(buf):position(buf)-1, s) + edit_splice!(buf, position(buf) => position(buf), s) return sizeof(s) end end -function edit_backspace(s::PromptState) - if edit_backspace(s.input_buffer) +# align: number of ' ' to insert after '\n' +# if align < 0: align like line above +function edit_insert_newline(s::PromptState, align=-1) + push_undo(s) + buf = buffer(s) + if align < 0 + beg = beginofline(buf) + align = min(findnext(_notspace, buf.data[beg+1:buf.size], 1) - 1, + position(buf) - beg) # indentation must not increase + align < 0 && (align = buf.size-beg) + end + edit_insert(buf, '\n' * ' '^align) + refresh_line(s) +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=false, adjust=align) + push_undo(s) + if edit_backspace(buffer(s), align) refresh_line(s) else + pop_undo(s) beep(terminal(s)) end end -function edit_backspace(buf::IOBuffer) - if position(buf) > 0 - oldpos = position(buf) - char_move_left(buf) - splice_buffer!(buf, position(buf):oldpos-1) - return true + +const _newline = UInt8('\n') +const _space = UInt8(' ') + +_notspace(c) = c != _space + +beginofline(buf, pos=position(buf)) = findprev(buf.data, _newline, pos) + +function endofline(buf, pos=position(buf)) + eol = findnext(buf.data[pos+1:buf.size], _newline, 1) + eol == 0 ? buf.size : pos + eol - 1 +end + +function edit_backspace(buf::IOBuffer, align::Bool=false, adjust::Bool=align) + !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 = strwidth(String(buf.data[1+beg:newpos])) % 4 + nonspace = findprev(_notspace, buf.data, newpos) + if newpos - align >= nonspace + newpos -= align + seek(buf, newpos) + if adjust + spaces = findnext(_notspace, buf.data[newpos+2:buf.size], 1) + 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) + push_undo(s) + if edit_delete(buffer(s)) + refresh_line(s) else - return false + pop_undo(s) + beep(terminal(s)) end + :edit_delete end -edit_delete(s) = edit_delete(buffer(s)) ? refresh_line(s) : beep(terminal(s)) function edit_delete(buf::IOBuffer) eof(buf) && return false oldpos = position(buf) char_move_right(buf) - splice_buffer!(buf, oldpos:position(buf)-1) + edit_splice!(buf, oldpos => position(buf)) true end @@ -509,44 +621,97 @@ function edit_werase(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf, isspace) pos0 = position(buf) - pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) - true + edit_splice!(buf, pos0 => pos1) end -function edit_werase(s) - edit_werase(buffer(s)) && refresh_line(s) + +function edit_werase(s::MIState) + push_undo(s) + if push_kill!(s, edit_werase(buffer(s)), rev=true) + refresh_line(s) + :edit_werase + else + pop_undo(s) + :ignore + end end function edit_delete_prev_word(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf) pos0 = position(buf) - pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) - true + edit_splice!(buf, pos0 => pos1) end -function edit_delete_prev_word(s) - edit_delete_prev_word(buffer(s)) && refresh_line(s) + +function edit_delete_prev_word(s::MIState) + push_undo(s) + if push_kill!(s, edit_delete_prev_word(buffer(s)), rev=true) + refresh_line(s) + :edit_delete_prev_word + else + pop_undo(s) + :ignore + end end function edit_delete_next_word(buf::IOBuffer) pos0 = position(buf) char_move_word_right(buf) pos1 = position(buf) - pos0 < pos1 || return false - splice_buffer!(buf, pos0:pos1-1) - true + edit_splice!(buf, pos0 => pos1) end + function edit_delete_next_word(s) - edit_delete_next_word(buffer(s)) && refresh_line(s) + push_undo(s) + if push_kill!(s, edit_delete_next_word(buffer(s))) + refresh_line(s) + :edit_delete_next_word + else + pop_undo(s) + :ignore + end end function edit_yank(s::MIState) - edit_insert(buffer(s), s.kill_buffer) + if isempty(s.kill_ring) + beep(terminal(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)]) refresh_line(s) + :edit_yank +end + +function edit_yank_pop(s::MIState, require_previous_yank=true) + if require_previous_yank && !(s.last_action in [:edit_yank, :edit_yank_pop]) || + isempty(s.kill_ring) + beep(terminal(s)) + :ignore + else + push_undo(s) + edit_splice!(s, s.kill_ring[mod1(s.kill_idx-=1, end)]) + refresh_line(s) + :edit_yank_pop + end +end + +function push_kill!(s::MIState, killed::String, concat = s.key_repeats > 0; rev=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) > KILL_RING_MAX[] && shift!(s.kill_ring) + end + s.kill_idx = endof(s.kill_ring) + true end function edit_kill_line(s::MIState) + push_undo(s) buf = buffer(s) pos = position(buf) killbuf = readline(buf, chomp=false) @@ -554,14 +719,41 @@ function edit_kill_line(s::MIState) killbuf = killbuf[1:end-1] char_move_left(buf) end - s.kill_buffer = s.key_repeats > 0 ? s.kill_buffer * killbuf : killbuf - - splice_buffer!(buf, pos:position(buf)-1) + push_kill!(s, killbuf) || return :ignore + edit_splice!(buf, pos => position(buf)) refresh_line(s) + :edit_kill_line +end + +function edit_copy_region(s::MIState) + buf = buffer(s) + push_kill!(s, content(buf, region(buf)), false) || return :ignore + if REGION_ANIMATION_DURATION[] > 0.0 + edit_exchange_point_and_mark(s) + sleep(REGION_ANIMATION_DURATION[]) + edit_exchange_point_and_mark(s) + end + :edit_copy_region +end + +function edit_kill_region(s::MIState) + push_undo(s) + if push_kill!(s, edit_splice!(s), false) + refresh_line(s) + :edit_kill_region + else + pop_undo(s) + :ignore + end +end + +function edit_transpose_chars(s::MIState) + push_undo(s) + edit_transpose_chars(buffer(s)) ? refresh_line(s) : pop_undo(s) + :edit_transpose end -edit_transpose(s) = edit_transpose(buffer(s)) && refresh_line(s) -function edit_transpose(buf::IOBuffer) +function edit_transpose_chars(buf::IOBuffer) position(buf) == 0 && return false eof(buf) && char_move_left(buf) char_move_left(buf) @@ -572,18 +764,73 @@ function edit_transpose(buf::IOBuffer) return true end +function edit_transpose_words(s) + push_undo(s) + edit_transpose_words(buffer(s)) ? refresh_line(s) : pop_undo(s) + :edit_transpose_words +end + +function edit_transpose_words(buf::IOBuffer, mode=: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) + true +end + + +edit_upper_case(s) = (edit_replace_word_right(s, uppercase); :edit_upper_case) +edit_lower_case(s) = (edit_replace_word_right(s, lowercase); :edit_lower_case) +edit_title_case(s) = (edit_replace_word_right(s, ucfirst); :edit_title_case) + +function edit_replace_word_right(s, replace::Function) + push_undo(s) + 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(buf, is_non_word_char) + 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))) + true +end + edit_clear(buf::IOBuffer) = truncate(buf, 0) function edit_clear(s::MIState) + push_undo(s) + push_kill!(s, content(s), false) || return :ignore edit_clear(buffer(s)) refresh_line(s) + :edit_clear end function replace_line(s::PromptState, l::IOBuffer) + empty_undo(s) s.input_buffer = copy(l) end -function replace_line(s::PromptState, l) +function replace_line(s::PromptState, l, keep_undo=false) + keep_undo || empty_undo(s) s.input_buffer.ptr = 1 s.input_buffer.size = 0 write(s.input_buffer, l) @@ -648,16 +895,18 @@ end ### Keymap Support +const wildcard = Char(0x0010f7ff) # "Private Use" Char + normalize_key(key::Char) = string(key) normalize_key(key::Integer) = normalize_key(Char(key)) function normalize_key(key::AbstractString) - '\0' in key && error("Matching \\0 not currently supported.") + wildcard in key && error("Matching Char(0x0010f7ff) not supported.") buf = IOBuffer() i = start(key) while !done(key, i) c, i = next(key, i) if c == '*' - write(buf, '\0') + write(buf, wildcard) elseif c == '^' c, i = next(key, i) write(buf, uppercase(c)-64) @@ -699,20 +948,14 @@ function add_nested_key!(keymap::Dict, key, value; override = false) i = start(key) while !done(key, i) c, i = next(key, i) - if c in keys(keymap) - if done(key, i) && override - # isa(keymap[c], Dict) - In this case we're overriding a prefix of an existing command - keymap[c] = value - break - else - if !isa(keymap[c], Dict) - error("Conflicting definitions for keyseq " * escape_string(key) * " within one keymap") - end - end - elseif done(key, i) + if !override && c in keys(keymap) && (done(key, i) || !isa(keymap[c], Dict)) + error("Conflicting definitions for keyseq " * escape_string(key) * + " within one keymap") + end + if done(key, i) keymap[c] = value break - else + elseif !(c in keys(keymap) && isa(keymap[c], Dict)) keymap[c] = Dict{Char,Any}() end keymap = keymap[c] @@ -727,19 +970,25 @@ struct KeyAlias KeyAlias(seq) = new(normalize_key(seq)) end -match_input(k::Function, s, term, cs, keymap) = (update_key_repeats(s, cs); return keymap_fcn(k, String(cs))) +function match_input(k::Function, s, term, cs, keymap) + update_key_repeats(s, cs) + return keymap_fcn(k, String(cs)) +end + match_input(k::Void, s, term, cs, keymap) = (s,p) -> return :ok -match_input(k::KeyAlias, s, term, cs, keymap) = match_input(keymap, s, IOBuffer(k.seq), Char[], keymap) +match_input(k::KeyAlias, s, term, cs, keymap) = + match_input(keymap, s, IOBuffer(k.seq), Char[], keymap) + function match_input(k::Dict, s, term=terminal(s), cs=Char[], keymap = k) # if we run out of characters to match before resolving an action, # return an empty keymap function eof(term) && return keymap_fcn(nothing, "") c = read(term, Char) - # Ignore any '\0' (eg, CTRL-space in xterm), as this is used as a + # Ignore any `wildcard` as this is used as a # placeholder for the wildcard (see normalize_key("*")) - c != '\0' || return keymap_fcn(nothing, "") + c == wildcard && return keymap_fcn(nothing, "") push!(cs, c) - key = haskey(k, c) ? c : '\0' + 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 @@ -830,12 +1079,12 @@ function fixup_keymaps!(dict::Dict, level, s, subkeymap) end function add_specialisations(dict, subdict, level) - default_branch = subdict['\0'] + default_branch = subdict[wildcard] if isa(default_branch, Dict) # Go through all the keymaps in the default branch # and copy them over to dict for s in keys(default_branch) - s == '\0' && add_specialisations(dict, default_branch, level+1) + s == wildcard && add_specialisations(dict, default_branch, level+1) fixup_keymaps!(dict, level, s, default_branch[s]) end end @@ -844,11 +1093,11 @@ end postprocess!(others) = nothing function postprocess!(dict::Dict) # needs to be done first for every branch - if haskey(dict, '\0') + if haskey(dict, wildcard) add_specialisations(dict, dict, 1) end for (k,v) in dict - k == '\0' && continue + k == wildcard && continue postprocess!(v) end end @@ -868,7 +1117,7 @@ end # source is the keymap specified by the user (with normalized keys) function keymap_merge(target,source) ret = copy(target) - direct_keys = filter((k,v) -> isa(v, Union{Function, KeyAlias, Void}), source) + direct_keys = filter(p -> isa(p.second, Union{Function, KeyAlias, Void}), source) # first direct entries for key in keys(direct_keys) add_nested_key!(ret, key, source[key]; override = true) @@ -932,7 +1181,7 @@ function keymap(keymaps::Array{<:Dict}) end const escape_defaults = merge!( - AnyDict(Char(i) => nothing for i=vcat(1:26, 28:31)), # Ignore control characters by default + 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, @@ -1120,8 +1369,8 @@ function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") end -state(s::MIState, p) = s.mode_state[p] -state(s::PromptState, p) = (@assert s.p == p; s) +state(s::MIState, p=mode(s)) = s.mode_state[p] +state(s::PromptState, p=mode(s)) = (@assert s.p == p; s) mode(s::MIState) = s.current_mode mode(s::PromptState) = s.p mode(s::SearchState) = @assert false @@ -1132,9 +1381,9 @@ function complete_line(s::SearchState, repeats) completions, partial, should_complete = complete_line(s.histprompt.complete, s) # For now only allow exact completions in search mode if length(completions) == 1 - prev_pos = position(s.query_buffer) - seek(s.query_buffer, prev_pos-sizeof(partial)) - edit_replace(s, position(s.query_buffer), prev_pos, completions[1]) + prev_pos = position(s) + push_undo(s) + edit_splice!(s, prev_pos-sizeof(partial) => prev_pos, completions[1]) end end @@ -1293,11 +1542,13 @@ function move_line_start(s::MIState) else seek(buf, rsearch(buf.data, '\n', curpos)) end + :move_line_start end function move_line_end(s::MIState) s.key_repeats > 0 ? move_input_end(s) : move_line_end(buffer(s)) + :move_line_end end function move_line_end(buf::IOBuffer) eof(buf) && return @@ -1337,42 +1588,68 @@ function bracketed_paste(s) return replace(input, '\t', " "^tabwidth) end +function tab_should_complete(s) + # Yes, we are ignoring the possiblity + # 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] + c != _newline && c != UInt8('\t') && + # hack to allow path completion in cmds + # after a space, e.g., `cd `, 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=false, delete_trailing=jump_spaces) + if tab_should_complete(s) + complete_line(s) + else + push_undo(s) + edit_insert_tab(buffer(s), jump_spaces, delete_trailing) + refresh_line(s) + :edit_insert_tab + end +end + +function edit_insert_tab(buf::IOBuffer, jump_spaces=false, delete_trailing=jump_spaces) + i = position(buf) + if jump_spaces && i < buf.size && buf.data[i+1] == _space + spaces = findnext(_notspace, buf.data[i+1:buf.size], 1) + 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 + return seek(buf, jump) + end + end + # align to multiples of 4: + align = 4 - strwidth(String(buf.data[1+beginofline(buf, i):i])) % 4 + return edit_insert(buf, ' '^align) +end + + const default_keymap = AnyDict( # Tab - '\t' => (s,o...)->begin - buf = buffer(s) - # Yes, we are ignoring the possiblity - # 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 - i = position(buf) - if i != 0 - c = buf.data[i] - if c == UInt8('\n') || c == UInt8('\t') || - # hack to allow path completion in cmds - # after a space, e.g., `cd `, while still - # allowing multiple indent levels - (c == UInt8(' ') && i > 3 && buf.data[i-1] == UInt8(' ')) - edit_insert(s, " "^4) - return - end - end - complete_line(s) - refresh_line(s) - end, + '\t' => (s,o...)->edit_tab(s, true), # Enter '\r' => (s,o...)->begin if on_enter(s) || (eof(buffer(s)) && s.key_repeats > 1) commit_line(s) return :done else - edit_insert(s, '\n') + edit_insert_newline(s) end end, '\n' => KeyAlias('\r'), # Backspace/^H - '\b' => (s,o...)->edit_backspace(s), + '\b' => (s,o...)->edit_backspace(s, true), 127 => KeyAlias('\b'), # Meta Backspace "\e\b" => (s,o...)->edit_delete_prev_word(s), @@ -1386,8 +1663,13 @@ AnyDict( return :abort end end, + # Ctrl-Space + "\0" => (s,o...)->setmark(s), + "^X^X" => (s,o...)->edit_exchange_point_and_mark(s), "^B" => (s,o...)->edit_move_left(s), "^F" => (s,o...)->edit_move_right(s), + "^P" => (s,o...)->edit_move_up(s), + "^N" => (s,o...)->edit_move_down(s), # Meta B "\eb" => (s,o...)->edit_move_word_left(s), # Meta F @@ -1401,13 +1683,18 @@ AnyDict( # Ctrl-Right Arrow on rxvt "\eOc" => "\ef", # Meta Enter - "\e\r" => (s,o...)->(edit_insert(s, '\n')), + "\e\r" => (s,o...)->edit_insert_newline(s), "\e\n" => "\e\r", + "^_" => (s,o...)->edit_undo!(s), + "\e_" => (s,o...)->edit_redo!(s), # Simply insert it into the buffer by default "*" => (s,data,c)->(edit_insert(s, c)), "^U" => (s,o...)->edit_clear(s), "^K" => (s,o...)->edit_kill_line(s), "^Y" => (s,o...)->edit_yank(s), + "\ey" => (s,o...)->edit_yank_pop(s), + "\ew" => (s,o...)->edit_copy_region(s), + "\eW" => (s,o...)->edit_kill_region(s), "^A" => (s,o...)->(move_line_start(s); refresh_line(s)), "^E" => (s,o...)->(move_line_end(s); refresh_line(s)), # Try to catch all Home/End keys @@ -1443,12 +1730,18 @@ AnyDict( input = bracketed_paste(s) edit_insert(s, input) end, - "^T" => (s,o...)->edit_transpose(s) + "^T" => (s,o...)->edit_transpose_chars(s), + "\et" => (s,o...)->edit_transpose_words(s), + "\eu" => (s,o...)->edit_upper_case(s), + "\el" => (s,o...)->edit_lower_case(s), + "\ec" => (s,o...)->edit_title_case(s), ) const history_keymap = AnyDict( - "^P" => (s,o...)->(history_prev(s, mode(s).hist)), - "^N" => (s,o...)->(history_next(s, mode(s).hist)), + "^P" => (s,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), + "^N" => (s,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), + "\ep" => (s,o...)->(history_prev(s, mode(s).hist)), + "\en" => (s,o...)->(history_next(s, mode(s).hist)), # Up Arrow "\e[A" => (s,o...)->(edit_move_up(s) || history_prev(s, mode(s).hist)), # Down Arrow @@ -1463,6 +1756,8 @@ const history_keymap = AnyDict( const prefix_history_keymap = merge!( AnyDict( + "^P" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), + "^N" => (s,data,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), # Up Arrow "\e[A" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), # Down Arrow @@ -1493,6 +1788,8 @@ function setup_prefix_keymap(hp, parent_prompt) p = PrefixHistoryPrompt(hp, parent_prompt) p.keymap_dict = keymap([prefix_history_keymap]) pkeymap = AnyDict( + "^P" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), + "^N" => (s,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), # Up Arrow "\e[A" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), # Down Arrow @@ -1512,31 +1809,32 @@ function activate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) end function activate(p::TextInterface, s::MIState, termbuf, term::TextTerminal) - @assert p == s.current_mode - activate(p, s.mode_state[s.current_mode], termbuf, term) + @assert p == mode(s) + activate(p, state(s), termbuf, term) end activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) = - activate(s.current_mode, s, termbuf, term) + activate(mode(s), s, termbuf, term) commit_changes(t::UnixTerminal, termbuf) = write(t, take!(termbuf.out_stream)) -function transition(f::Function, s::MIState, mode) - if mode === :abort + +function transition(f::Function, s::MIState, newmode) + if newmode === :abort s.aborted = true return end - if mode === :reset + if newmode === :reset reset_state(s) return end - if !haskey(s.mode_state,mode) - s.mode_state[mode] = init_state(terminal(s), mode) + 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[s.current_mode] = deactivate(s.current_mode, s.mode_state[s.current_mode], termbuf, t) - s.current_mode = mode + s.mode_state[mode(s)] = deactivate(mode(s), state(s), termbuf, t) + s.current_mode = newmode f() - activate(mode, s.mode_state[mode], termbuf, t) + activate(newmode, state(s, newmode), termbuf, t) commit_changes(t, termbuf) end transition(s::MIState, mode) = transition((args...)->nothing, s, mode) @@ -1546,11 +1844,12 @@ function reset_state(s::PromptState) s.input_buffer.size = 0 s.input_buffer.ptr = 1 end + empty_undo(s) s.ias = InputAreaState(0, 0) end function reset_state(s::MIState) - for (mode,state) in s.mode_state + for (mode, state) in s.mode_state reset_state(state) end end @@ -1575,8 +1874,8 @@ end run_interface(::Prompt) = nothing init_state(terminal, prompt::Prompt) = - PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), - #=indent(spaces)=# -1) + PromptState(terminal, prompt, IOBuffer(), IOBuffer[], 1, InputAreaState(1, 1), + #=indent(spaces)=# -1) function init_state(terminal, m::ModalInterface) s = MIState(m, m.modes[1], false, Dict{Any,Any}()) @@ -1598,7 +1897,7 @@ function run_interface(terminal, m::ModalInterface) Expr(:body, Expr(:return, Expr(:call, - QuoteNode(mode(state(s, s.current_mode)).on_done), + QuoteNode(mode(state(s)).on_done), QuoteNode(s), QuoteNode(buf), QuoteNode(ok))))) @@ -1608,11 +1907,70 @@ end 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 +end + +empty_undo(s) = nothing + +function push_undo(s::PromptState, advance=true) + resize!(s.undo_buffers, s.undo_idx) + s.undo_buffers[end] = copy(s.input_buffer) + advance && (s.undo_idx += 1) +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 +end + +function edit_undo!(s::MIState) + s.last_action ∉ (:edit_redo!, :edit_undo!) && push_undo(s, false) + if edit_undo!(state(s)) + :edit_undo! + else + beep(terminal(s)) + :ignore + end +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) + true +end +edit_undo!(s) = nothing + +function edit_redo!(s::MIState) + if s.last_action ∈ (:edit_redo!, :edit_undo!) && edit_redo!(state(s)) + :edit_redo! + else + beep(terminal(s)) + :ignore + end +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) + true +end +edit_redo!(s) = nothing keymap(s::PromptState, prompt::Prompt) = prompt.keymap_dict keymap_data(s::PromptState, prompt::Prompt) = prompt.keymap_func_data -keymap(ms::MIState, m::ModalInterface) = keymap(ms.mode_state[ms.current_mode], ms.current_mode) -keymap_data(ms::MIState, m::ModalInterface) = keymap_data(ms.mode_state[ms.current_mode], ms.current_mode) +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, prompt, s = init_state(term, prompt)) Base.reseteof(term) @@ -1625,29 +1983,28 @@ function prompt!(term, prompt, s = init_state(term, prompt)) kmap = keymap(s, prompt) fcn = match_input(kmap, s) kdata = keymap_data(s, prompt) + local action # errors in keymaps shouldn't cause the REPL to fail, so wrap in a # try/catch block - local state try - state = fcn(s, kdata) + action = fcn(s, kdata) catch e bt = catch_backtrace() warn(e, bt = bt, prefix = "ERROR (in the keymap): ") # try to cleanup and get `s` back to its original state before returning transition(s, :reset) transition(s, old_state) - state = :done + action = :done end - if state === :abort + action != :ignore && (s.last_action = action) + if action === :abort return buffer(s), false, false - elseif state === :done + elseif action === :done return buffer(s), true, false - elseif state === :suspend + elseif action === :suspend if Sys.isunix() return buffer(s), true, true end - else - @assert state === :ok end end finally @@ -1655,4 +2012,5 @@ function prompt!(term, prompt, s = init_state(term, prompt)) end end + end # module diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index 4309a83efb7e1..c8fc5a3fb42ba 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -507,7 +507,8 @@ function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, end history_first(s::LineEdit.MIState, hist::REPLHistoryProvider) = - history_prev(s, hist, hist.cur_idx - 1) + 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) @@ -807,7 +808,7 @@ function setup_interface( extra_repl_keymap = [extra_repl_keymap] end - const repl_keymap = AnyDict( + repl_keymap = AnyDict( ';' => function (s,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 buf = copy(LineEdit.buffer(s)) @@ -848,6 +849,7 @@ function setup_interface( edit_insert(s, input) return end + LineEdit.push_undo(s) edit_insert(sbuffer, input) input = String(take!(sbuffer)) oldpos = start(input) @@ -888,13 +890,19 @@ function setup_interface( # (avoids modifying the user's current leading wip line) tail = lstrip(tail) end - LineEdit.replace_line(s, tail) + if isprompt_paste # remove indentation spaces corresponding to the prompt + tail = replace(tail, r"^ {7}"m, "") # 7: jl_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"^ {7}"m, "") # 7: jl_prompt_len + end # put the line on the screen and history LineEdit.replace_line(s, line) LineEdit.commit_line(s) @@ -903,6 +911,7 @@ function setup_interface( 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 diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index bdb84d972b933..402b86279e2ec 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -10,11 +10,19 @@ function completes_global(x, name) return startswith(x, name) && !('#' in x) end +function appendmacro!(syms, macros, needle, endchar) + append!(syms, s[2:end-sizeof(needle)]*endchar for s in filter(x -> endswith(x, needle), macros)) +end + function filtered_mod_names(ffunc::Function, mod::Module, name::AbstractString, all::Bool=false, imported::Bool=false) ssyms = names(mod, all, imported) filter!(ffunc, ssyms) syms = String[string(s) for s in ssyms] + macros = filter(x -> startswith(x, "@" * name), syms) + appendmacro!(syms, macros, "_str", "\"") + appendmacro!(syms, macros, "_cmd", "`") filter!(x->completes_global(x, name), syms) + return syms end # REPL Symbol Completions @@ -71,25 +79,28 @@ function complete_symbol(sym, ffunc) end else # Looking for a member of a type - fields = fieldnames(t) - for field in fields - s = string(field) - if startswith(s, name) - push!(suggestions, s) + if t isa DataType && t != Any + fields = fieldnames(t) + for field in fields + s = string(field) + if startswith(s, name) + push!(suggestions, s) + end end end end suggestions end +const sorted_keywords = [ + "abstract type", "baremodule", "begin", "break", "catch", "ccall", + "const", "continue", "do", "else", "elseif", "end", "export", "false", + "finally", "for", "function", "global", "if", "import", + "importall", "let", "local", "macro", "module", "mutable struct", + "primitive type", "quote", "return", "struct", + "true", "try", "using", "while"] + function complete_keyword(s::String) - const sorted_keywords = [ - "abstract type", "baremodule", "begin", "break", "catch", "ccall", - "const", "continue", "do", "else", "elseif", "end", "export", "false", - "finally", "for", "function", "global", "if", "import", - "importall", "let", "local", "macro", "module", "mutable struct", - "primitive type", "quote", "return", "struct", - "true", "try", "using", "while"] r = searchsorted(sorted_keywords, s) i = first(r) n = length(sorted_keywords) @@ -183,6 +194,11 @@ function complete_path(path::AbstractString, pos; use_envpath=false) return matchList, startpos:pos, !isempty(matchList) end +function complete_expanduser(path::AbstractString, r) + expanded = expanduser(path) + return String[expanded], r, path != expanded +end + # Determines whether method_complete should be tried. It should only be done if # the string endswiths ',' or '(' when disregarding whitespace_chars function should_method_complete(s::AbstractString) @@ -368,7 +384,7 @@ function afterusing(string::String, startpos::Int) r = search(rstr, r"\s(gnisu|tropmi)\b") isempty(r) && return false fr = reverseind(str, last(r)) - return ismatch(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*\w*$", str[fr:end]) + return ismatch(r"^\b(using|import)\s*((\w+[.])*\w+\s*,\s*)*$", str[fr:end]) end function bslash_completions(string, pos) @@ -459,13 +475,19 @@ function completions(string, pos) m = match(r"[\t\n\r\"><=*?|]| (?!\\)", reverse(partial)) startpos = nextind(partial, reverseind(partial, m.offset)) r = startpos:pos + + expanded = complete_expanduser(replace(string[r], r"\\ ", " "), r) + expanded[3] && return expanded # If user expansion available, return it + paths, r, success = complete_path(replace(string[r], r"\\ ", " "), pos) + if inc_tag == :string && length(paths) == 1 && # Only close if there's a single choice, !isdir(expanduser(replace(string[startpos:start(r)-1] * paths[1], r"\\ ", " "))) && # except if it's a directory (length(string) <= pos || string[pos+1] != '"') # or there's already a " at the cursor. paths[1] *= "\"" end + #Latex symbols can be completed for strings (success || inc_tag==:cmd) && return sort!(paths), r, success end diff --git a/base/repl/latex_symbols.jl b/base/repl/latex_symbols.jl index 2a546b01259eb..e268b389a644b 100644 --- a/base/repl/latex_symbols.jl +++ b/base/repl/latex_symbols.jl @@ -88,6 +88,7 @@ const latex_symbols = Dict( "\\implies" => "⟹", "\\impliedby" => "⟸", "\\to" => "→", + "\\euler" => "ℯ", # Superscripts "\\^0" => "⁰", diff --git a/base/replutil.jl b/base/replutil.jl index d5329602aacc0..2e2031b506e3e 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -109,7 +109,8 @@ function show(io::IO, ::MIME"text/plain", f::Function) ft == typeof(getfield(ft.name.module, name)) n = length(methods(f)) m = n==1 ? "method" : "methods" - ns = isself ? string(name) : string("(::", name, ")") + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", ft, ")") what = startswith(ns, '@') ? "macro" : "generic function" print(io, ns, " (", what, " with $n $m)") end @@ -242,7 +243,8 @@ function showerror(io::IO, ex::DomainError, bt; backtrace=true) if isa(ex.val, AbstractArray) compact = get(io, :compact, true) limit = get(io, :limit, true) - print(IOContext(io, compact=compact, limit=limit), "DomainError with ", ex.val) + print(IOContext(io, :compact => compact, :limit => limit), + "DomainError with ", ex.val) else print(io, "DomainError with ", ex.val) end @@ -360,7 +362,7 @@ function showerror(io::IO, ex::MethodError) print(io, "; ") for (i, (k, v)) in enumerate(kwargs) print(io, k, "=") - show(IOContext(io, :limit=>true), v) + show(IOContext(io, :limit => true), v) i == length(kwargs) || print(io, ", ") end end diff --git a/base/serialize.jl b/base/serialize.jl index 6a6847a850e90..b78b658430fe5 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -637,10 +637,28 @@ function serialize_any(s::AbstractSerializer, @nospecialize(x)) end end +""" + serialize(stream, 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. In +general, this process will not work if the reading and writing are done by different +versions of Julia, or an instance of Julia with a different system image. `Ptr` values are +serialized as all-zero bit patterns (`NULL`). +""" serialize(s::IO, x) = serialize(SerializationState(s), x) ## 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). +It has been designed with simplicity and performance as a goal and does not validate +the data read. Malformed data can result in process termination. The caller has to ensure +the integrity and correctness of data read from `stream`. +""" deserialize(s::IO) = deserialize(SerializationState(s)) function deserialize(s::AbstractSerializer) diff --git a/base/set.jl b/base/set.jl index 51603130e01b7..86f1e93b889b4 100644 --- a/base/set.jl +++ b/base/set.jl @@ -65,6 +65,36 @@ done(s::Set, state) = done(s.dict, state) # NOTE: manually optimized to take advantage of Dict representation next(s::Set, i) = (s.dict.keys[i], skip_deleted(s.dict, i+1)) +""" + union(s1,s2...) + ∪(s1,s2...) + +Construct the union of two or more sets. Maintains order with arrays. + +# Examples +```jldoctest +julia> union([1, 2], [3, 4]) +4-element Array{Int64,1}: + 1 + 2 + 3 + 4 + +julia> union([1, 2], [2, 4]) +3-element Array{Int64,1}: + 1 + 2 + 4 + +julia> union([4, 2], [1, 2]) +3-element Array{Int64,1}: + 4 + 2 + 1 +``` +""" +function union end + union(s::Set) = copy(s) function union(s::Set, sets...) u = Set{join_eltype(s, sets...)}() @@ -103,6 +133,28 @@ end join_eltype() = Bottom join_eltype(v1, vs...) = typejoin(eltype(v1), join_eltype(vs...)) +""" + intersect(s1,s2...) + ∩(s1,s2) + +Construct the intersection of two or more sets. +Maintains order and multiplicity of the first argument for arrays and ranges. + +# Examples +```jldoctest +julia> intersect([1, 2, 3], [3, 4, 5]) +1-element Array{Int64,1}: + 3 + +julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) +3-element Array{Int64,1}: + 4 + 4 + 6 +``` +""" +function intersect end + intersect(s::Set) = copy(s) function intersect(s::Set, sets...) i = similar(s) @@ -188,7 +240,8 @@ const ⊆ = issubset 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. +set of equivalent elements originally appears. The element type of the +input is preserved. # Examples ```jldoctest @@ -197,6 +250,11 @@ julia> unique([1, 2, 6, 2]) 1 2 6 + +julia> unique(Real[1, 1.0, 2]) +2-element Array{Real,1}: + 1 + 2 ``` """ function unique(itr) @@ -208,7 +266,7 @@ function unique(itr) return out end x, i = next(itr, i) - if !isleaftype(T) + if !isleaftype(T) && iteratoreltype(itr) == EltypeUnknown() S = typeof(x) return _unique_from(itr, S[x], Set{S}((x,)), i) end diff --git a/base/sharedarray.jl b/base/sharedarray.jl index 479eee1bcc898..3cdeb3bcd88a1 100644 --- a/base/sharedarray.jl +++ b/base/sharedarray.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license import .Serializer: serialize_cycle_header, serialize_type, writetag, UNDEFREF_TAG -import .Distributed: RRID +import .Distributed: RRID, procs mutable struct SharedArray{T,N} <: DenseArray{T,N} id::RRID @@ -499,7 +499,7 @@ function rand!(S::SharedArray{T}) where T return S end -function randn!(S::SharedArray) +function Random.randn!(S::SharedArray) f = S->map!(x -> randn(), S.loc_subarr_1d, S.loc_subarr_1d) @sync for p in procs(S) @async remotecall_wait(f, p, S) diff --git a/base/show.jl b/base/show.jl index 303055c80b633..801d6d8d1bc6f 100644 --- a/base/show.jl +++ b/base/show.jl @@ -20,41 +20,32 @@ struct IOContext{IO_t <: IO} <: AbstractPipe end end -""" - IOContext(io::IO; properties...) +unwrapcontext(io::IO) = io, ImmutableDict{Symbol,Any}() +unwrapcontext(io::IOContext) = io.io, io.dict -The same as `IOContext(io::IO, KV::Pair)`, but accepting properties as keyword arguments. -""" -IOContext(io::IO; kws...) = IOContext(convert(IOContext, io); kws...) -function IOContext(io::IOContext; kws...) - for (k, v) in kws - io = IOContext(io, k, v) - end - return io +function IOContext(io::IO, dict::ImmutableDict) + io0 = unwrapcontext(io)[1] + IOContext{typeof(io0)}(io0, dict) end -convert(::Type{IOContext}, io::IOContext) = io -convert(::Type{IOContext}, io::IO) = IOContext(io, ImmutableDict{Symbol, Any}()) +convert(::Type{IOContext}, io::IO) = IOContext(unwrapcontext(io)...) -IOContext(io::IOContext, dict::ImmutableDict) = typeof(io)(io.io, dict) -IOContext(io::IO, dict::ImmutableDict) = IOContext{typeof(io)}(io, dict) - -IOContext(io::IOContext, key, value) = IOContext(io.io, ImmutableDict{Symbol, Any}(io.dict, key, value)) -IOContext(io::IO, key, value) = IOContext(io, ImmutableDict{Symbol, Any}(key, value)) - -IOContext(io::IO, context::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::IOContext) = IOContext(io, context.dict) +IOContext(io::IO, context::IO) = IOContext(unwrapcontext(io)[1], unwrapcontext(context)[2]) """ - IOContext(io::IO, KV::Pair) + IOContext(io::IO, KV::Pair...) -Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pair to +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 dict` to see if this particular combination is in the properties set @@ -87,7 +78,7 @@ julia> f(IOContext(STDOUT, :short => true)) short ``` """ -IOContext(io::IO, KV::Pair) = IOContext(io, KV[1], KV[2]) +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, ")")) @@ -122,7 +113,17 @@ function show_circular(io::IOContext, @nospecialize(x)) return false end +""" + show(x) + +Write an informative text representation of a value to the current output stream. New types +should overload `show(io, x)` where the first argument is a stream. The representation used +by `show` generally includes Julia-specific formatting and type information. +""" +show(x) = show(STDOUT::IO, x) + show(io::IO, @nospecialize(x)) = show_default(io, x) + function show_default(io::IO, @nospecialize(x)) t = typeof(x)::DataType show(io, t) @@ -171,10 +172,15 @@ end function show(io::IO, f::Function) ft = typeof(f) mt = ft.name.mt - if !isdefined(mt, :module) || is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main - print(io, mt.name) + if isdefined(mt, :module) && isdefined(mt.module, mt.name) && + getfield(mt.module, mt.name) === f + if is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main + print(io, mt.name) + else + print(io, mt.module, ".", mt.name) + end else - print(io, mt.module, ".", mt.name) + show_default(io, f) end end @@ -198,10 +204,36 @@ function print_without_params(@nospecialize(x)) return false end +has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v)!=0 + +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 + function show(io::IO, x::UnionAll) if print_without_params(x) return show(io, unwrap_unionall(x).name) end + + if x.var.name == :_ || io_has_tvar_name(io, x.var.name, x) + counter = 1 + while true + newname = Symbol(x.var.name, counter) + if !io_has_tvar_name(io, newname, x) + newtv = TypeVar(newname, x.var.lb, x.var.ub) + x = UnionAll(newtv, x{newtv}) + break + end + counter += 1 + end + end + show(IOContext(io, :unionall_env => x.var), x.body) print(io, " where ") show(io, x.var) @@ -209,6 +241,57 @@ end show(io::IO, x::DataType) = show_datatype(io, x) +function show_type_name(io::IO, tn::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 = false + if globname !== nothing + globname_str = string(globname) + if ('#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) && + isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && + isa(getfield(tn.module, globname), tn.wrapper) && isleaftype(tn.wrapper)) + globfunc = true + end + end + sym = globfunc ? globname : tn.name + sym_str = string(sym) + hidden = !globfunc && '#' ∈ sym_str + quo = false + if hidden + print(io, "getfield(") + elseif globfunc + print(io, "typeof(") + end + if isdefined(tn, :module) && !(is_exported_from_stdlib(sym, tn.module) || (tn.module === Main && !hidden)) + show(io, tn.module) + if !hidden + print(io, ".") + if globfunc && !is_id_start_char(first(sym_str)) + print(io, ":") + if sym == :(==) + print(io, "(") + quo = true + end + end + end + end + if hidden + print(io, ", Symbol(\"", sym_str, "\"))") + else + print(io, sym_str) + if globfunc + print(io, ")") + if quo + print(io, ")") + end + end + end +end + function show_datatype(io::IO, x::DataType) istuple = x.name === Tuple.name if (!isempty(x.parameters) || istuple) && x !== Tuple @@ -218,7 +301,7 @@ function show_datatype(io::IO, x::DataType) if istuple && n > 3 && all(i -> (x.parameters[1] === i), x.parameters) print(io, "NTuple{", n, ',', x.parameters[1], "}") else - show(io, x.name) + show_type_name(io, x.name) # Do not print the type parameters for the primary type if we are # printing a method signature or type parameter. # Always print the type parameter if we are printing the type directly @@ -231,7 +314,7 @@ function show_datatype(io::IO, x::DataType) print(io, '}') end else - show(io, x.name) + show_type_name(io, x.name) end end @@ -262,11 +345,7 @@ macro show(exs...) end function show(io::IO, tn::TypeName) - if is_exported_from_stdlib(tn.name, tn.module) || tn.module === Main - print(io, tn.name) - else - print(io, tn.module, '.', tn.name) - end + show_type_name(io, tn) end show(io::IO, ::Void) = print(io, "nothing") @@ -461,6 +540,7 @@ show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) 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}([ :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=), @@ -702,23 +782,19 @@ function show_generator(io, ex, indent) fg = ex ranges = Any[] while isa(fg, Expr) && fg.head === :flatten - push!(ranges, fg.args[1].args[2]) + push!(ranges, fg.args[1].args[2:end]) fg = fg.args[1].args[1] end - push!(ranges, fg.args[2]) + push!(ranges, fg.args[2:end]) show_unquoted(io, fg.args[1], indent) for r in ranges print(io, " for ") - show_unquoted(io, r, indent) + show_list(io, r, ", ", indent) end else show_unquoted(io, ex.args[1], indent) print(io, " for ") - show_unquoted(io, ex.args[2], indent) - for i = 3:length(ex.args) - print(io, ", ") - show_unquoted(io, ex.args[i], indent) - end + show_list(io, ex.args[2:end], ", ", indent) end end @@ -883,7 +959,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, "function ", args[1], " end") # block with argument - elseif head in (:for,:while,:function,:if,:elseif) && nargs==2 + elseif head in (:for,:while,:function,:if,:elseif,:let) && nargs==2 show_block(io, head, args[1], args[2], indent) print(io, "end") @@ -920,8 +996,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, head) # type annotation (i.e. "::Int") - elseif head === Symbol("::") && nargs == 1 - print(io, "::") + elseif head in uni_syms && nargs == 1 + print(io, head) show_unquoted(io, args[1], indent) # var-arg declaration or expansion @@ -967,9 +1043,6 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end print(io, "end") - elseif head === :let && nargs >= 1 - show_block(io, "let", args[2:end], args[1], indent); print(io, "end") - elseif head === :block || head === :body show_block(io, "begin", ex, indent); print(io, "end") @@ -1325,7 +1398,7 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) tp = t while true show(tvar_io, tp.var) - tvar_io = IOContext(tvar_io, :unionall_env, tp.var) + tvar_io = IOContext(tvar_io, :unionall_env => tp.var) tp = tp.body if isa(tp, UnionAll) print(io, ", ") @@ -1362,6 +1435,12 @@ end dump(io::IO, x::DataType; maxdepth=8) = ((x.abstract ? dumptype : dump)(io, x, maxdepth, ""); println(io)) dump(io::IO, arg; maxdepth=8) = (dump(io, arg, maxdepth, ""); println(io)) + +""" + dump(x) + +Show every part of the representation of a value. +""" dump(arg; maxdepth=8) = dump(IOContext(STDOUT::IO, :limit => true), arg; maxdepth=maxdepth) diff --git a/base/socket.jl b/base/socket.jl index fd1298fdb5f9f..997e6d2a5cb04 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -333,6 +333,13 @@ _jl_sockaddr_from_addrinfo(addrinfo::Ptr{Void}) = _jl_sockaddr_set_port(ptr::Ptr{Void}, port::UInt16) = ccall(:jl_sockaddr_set_port, Void, (Ptr{Void}, UInt16), ptr, port) +""" + 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()) # Libuv will internally reset the readable and writable flags on @@ -544,8 +551,8 @@ function _send(sock::UDPSocket, ipaddr::IPv4, port::UInt16, buf) end function _send(sock::UDPSocket, ipaddr::IPv6, port::UInt16, buf) - ccall(:jl_udp_send6, Cint, (Ptr{Void}, UInt16, Ptr{UInt128}, Ptr{UInt8}, Csize_t, Ptr{Void}), - sock.handle, hton(port), &hton(ipaddr.host), buf, sizeof(buf), uv_jl_sendcb::Ptr{Void}) + ccall(:jl_udp_send6, Cint, (Ptr{Void}, UInt16, Ref{UInt128}, Ptr{UInt8}, Csize_t, Ptr{Void}), + sock.handle, hton(port), hton(ipaddr.host), buf, sizeof(buf), uv_jl_sendcb::Ptr{Void}) end """ @@ -719,8 +726,8 @@ function connect!(sock::TCPSocket, host::IPv6, port::Integer) if !(0 <= port <= typemax(UInt16)) throw(ArgumentError("port out of range, must be 0 ≤ port ≤ 65535, got $port")) end - uv_error("connect", ccall(:jl_tcp6_connect, Int32, (Ptr{Void}, Ptr{UInt128}, UInt16, Ptr{Void}), - sock.handle, &hton(host.host), hton(UInt16(port)), uv_jl_connectcb::Ptr{Void})) + uv_error("connect", ccall(:jl_tcp6_connect, Int32, (Ptr{Void}, Ref{UInt128}, UInt16, Ptr{Void}), + sock.handle, hton(host.host), hton(UInt16(port)), uv_jl_connectcb::Ptr{Void})) sock.status = StatusConnecting nothing end diff --git a/base/sort.jl b/base/sort.jl index 717de598a23aa..cb28e79d7de52 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -15,18 +15,18 @@ import export # also exported by Base # order-only: issorted, - select, - select!, searchsorted, searchsortedfirst, searchsortedlast, # order & algorithm: sort, sort!, - selectperm, - selectperm!, sortperm, sortperm!, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, sortrows, sortcols, # algorithms: @@ -82,16 +82,80 @@ issorted(itr; lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = issorted(itr, ord(lt,by,rev,order)) -function select!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) +function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) inds = indices(v, 1) sort!(v, first(inds), last(inds), PartialQuickSort(k), o) - v[k] + + if k isa Integer + return v[k] + else + return view(v, k) + end end -select!(v::AbstractVector, k::Union{Int,OrdinalRange}; - lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = - select!(v, k, ord(lt,by,rev,order)) -select(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = select!(copymutable(v), k; kws...) +""" + partialsort!(v, k, [by=,] [lt=,] [rev=false]) + +Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and +`rev` 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 via a non-stable +algorithm. 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!` does not fully sort the input +array. + +# Examples +```jldoctest +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> partialsort!(a, 4) +4 + +julia> a +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 4 + +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> partialsort!(a, 4, rev=true) +2 + +julia> a +5-element Array{Int64,1}: + 4 + 4 + 3 + 2 + 1 +``` +""" +partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; + lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = + partialsort!(v, k, ord(lt,by,rev,order)) + +""" + partialsort(v, k, [by=,] [lt=,] [rev=false]) + +Variant of [`partialsort!`](@ref) which copies `v` before partially sorting it, thereby returning the +same thing as `partialsort!` but leaving `v` unmodified. +""" +partialsort(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = + partialsort!(copymutable(v), k; kws...) # reference on sorted binary search: @@ -609,18 +673,36 @@ julia> v """ sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) -## selectperm: the permutation to sort the first k elements of an array ## +## partialsortperm: the permutation to sort the first k elements of an array ## + +""" + partialsortperm(v, k, [alg=,] [by=,] [lt=,] [rev=false]) + +Return a partial permutation of the vector `v`, according to the order specified by +`by`, `lt` and `rev`, so that `v[output]` returns the first `k` (or range of adjacent values +if `k` is a range) values of a fully sorted version of `v`. If `k` is a single index, +the index in `v` of the value which would be sorted at position `k` is returned; +if `k` is a range, an array with the indices in `v` of the values which would be sorted in +these positions is returned. + +Note that this is equivalent to, but more efficient than, calling `sortperm(...)[k]`. +""" +partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = + partialsortperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) -selectperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = - selectperm!(similar(Vector{eltype(k)}, indices(v,1)), v, k; kwargs..., initialized=false) +""" + partialsortperm!(ix, v, k, [alg=,] [by=,] [lt=,] [rev=false,] [initialized=false]) -function selectperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, - k::Union{Int, OrdinalRange}; - lt::Function=isless, - by::Function=identity, - rev::Bool=false, - order::Ordering=Forward, - initialized::Bool=false) +Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` +(the default), `ix` is initialized to contain the values `1:length(ix)`. +""" +function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, + k::Union{Int, OrdinalRange}; + lt::Function=isless, + by::Function=identity, + rev::Bool=false, + order::Ordering=Forward, + initialized::Bool=false) if !initialized @inbounds for i = indices(ix,1) ix[i] = i @@ -629,7 +711,12 @@ function selectperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, # do partial quicksort sort!(ix, PartialQuickSort(k), Perm(ord(lt, by, rev, order), v)) - return ix[k] + + if k isa Integer + return ix[k] + else + return view(ix, k) + end end ## sortperm: the permutation to sort an array ## diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 5351a0717c041..4d5e7188eb5ae 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -3,20 +3,21 @@ module CHOLMOD import Base: (*), convert, copy, eltype, getindex, show, size, - IndexStyle, IndexLinear, IndexCartesian, ctranspose + IndexStyle, IndexLinear, IndexCartesian, adjoint import Base.LinAlg: (\), A_mul_Bc, A_mul_Bt, Ac_ldiv_B, Ac_mul_B, At_ldiv_B, At_mul_B, cholfact, cholfact!, det, diag, ishermitian, isposdef, issuccess, issymmetric, ldltfact, ldltfact!, logdet -importall ..SparseArrays +using ..SparseArrays export Dense, Factor, Sparse -import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype +import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype, sparse, speye, + spzeros, nnz ######### # Setup # @@ -26,6 +27,16 @@ include("cholmod_h.jl") const CHOLMOD_MIN_VERSION = v"2.1.1" +const common_struct = Vector{UInt8}() + +const common_supernodal = Ref{Ptr{Cint}}() +const common_final_ll = Ref{Ptr{Cint}}() +const common_print = Ref{Ptr{Cint}}() +const common_itype = Ref{Ptr{Cint}}() +const common_dtype = Ref{Ptr{Cint}}() +const common_nmethods = Ref{Ptr{Cint}}() +const common_postorder = Ref{Ptr{Cint}}() + ### These offsets are defined in SuiteSparse_wrapper.c const common_size = ccall((:jl_cholmod_common_size,:libsuitesparse_wrapper),Int,()) @@ -56,8 +67,6 @@ function defaults(a::Vector{UInt8}) return a end -common() = commonStruct - const build_version_array = Vector{Cint}(3) ccall((:jl_cholmod_version, :libsuitesparse_wrapper), Cint, (Ptr{Cint},), build_version_array) const build_version = VersionNumber(build_version_array...) @@ -129,27 +138,21 @@ function __init__() end ### Initiate CHOLMOD - ### The common struct. Controls the type of factorization and keeps pointers + ### common_struct controls the type of factorization and keeps pointers ### to temporary memory. - global const commonStruct = fill(0xff, common_size) - - global const common_supernodal = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[4] + 1)) - global const common_final_ll = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[7] + 1)) - global const common_print = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[13] + 1)) - global const common_itype = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[18] + 1)) - global const common_dtype = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[19] + 1)) - global const common_nmethods = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[15] + 1)) - global const common_postorder = - convert(Ptr{Cint}, pointer(commonStruct, cholmod_com_offsets[17] + 1)) - - start(commonStruct) # initializes CHOLMOD - set_print_level(commonStruct, 0) # no printing from CHOLMOD by default + resize!(common_struct, common_size) + fill!(common_struct, 0xff) + + common_supernodal[] = pointer(common_struct, cholmod_com_offsets[4] + 1) + common_final_ll[] = pointer(common_struct, cholmod_com_offsets[7] + 1) + common_print[] = pointer(common_struct, cholmod_com_offsets[13] + 1) + common_itype[] = pointer(common_struct, cholmod_com_offsets[18] + 1) + common_dtype[] = pointer(common_struct, cholmod_com_offsets[19] + 1) + common_nmethods[] = pointer(common_struct, cholmod_com_offsets[15] + 1) + common_postorder[] = pointer(common_struct, cholmod_com_offsets[17] + 1) + + start(common_struct) # initializes CHOLMOD + set_print_level(common_struct, 0) # no printing from CHOLMOD by default # Register gc tracked allocator if CHOLMOD is new enough if current_version >= v"3.0.0" @@ -166,9 +169,8 @@ function __init__() end end -function set_print_level(cm::Array{UInt8}, lev::Integer) - global common_print - unsafe_store!(common_print, lev) +function set_print_level(cm::Vector{UInt8}, lev::Integer) + unsafe_store!(common_print[], lev) end #################### @@ -397,35 +399,35 @@ Factor(FC::FactorComponent) = Factor(FC.F) function allocate_dense(nrow::Integer, ncol::Integer, d::Integer, ::Type{Float64}) Dense(ccall((:cholmod_l_allocate_dense, :libcholmod), Ptr{C_Dense{Float64}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{Void}), - nrow, ncol, d, REAL, common())) + nrow, ncol, d, REAL, common_struct)) end function allocate_dense(nrow::Integer, ncol::Integer, d::Integer, ::Type{Complex{Float64}}) Dense(ccall((:cholmod_l_allocate_dense, :libcholmod), Ptr{C_Dense{Complex{Float64}}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{Void}), - nrow, ncol, d, COMPLEX, common())) + nrow, ncol, d, COMPLEX, common_struct)) end free_dense!(p::Ptr{C_Dense{T}}) where {T} = ccall((:cholmod_l_free_dense, :libcholmod), - Cint, (Ref{Ptr{C_Dense{T}}}, Ptr{Void}), p, common()) + Cint, (Ref{Ptr{C_Dense{T}}}, Ptr{Void}), p, common_struct) function zeros(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_zeros, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end zeros(m::Integer, n::Integer) = zeros(m, n, Float64) function ones(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_ones, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end ones(m::Integer, n::Integer) = ones(m, n, Float64) function eye(m::Integer, n::Integer, ::Type{T}) where T<:VTypes Dense(ccall((:cholmod_l_eye, :libcholmod), Ptr{C_Dense{T}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(T), common())) + m, n, xtyp(T), common_struct)) end eye(m::Integer, n::Integer) = eye(m, n, Float64) eye(n::Integer) = eye(n, n, Float64) @@ -433,13 +435,13 @@ eye(n::Integer) = eye(n, n, Float64) function copy_dense(A::Dense{Tv}) where Tv<:VTypes Dense(ccall((:cholmod_l_copy_dense, :libcholmod), Ptr{C_Dense{Tv}}, (Ptr{C_Dense{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function sort!(S::Sparse{Tv}) where Tv<:VTypes @isok ccall((:cholmod_l_sort, :libcholmod), SuiteSparse_long, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - S, common()) + S, common_struct) return S end @@ -455,14 +457,14 @@ function norm_dense(D::Dense{Tv}, p::Integer) where Tv<:VTypes end ccall((:cholmod_l_norm_dense, :libcholmod), Cdouble, (Ptr{C_Dense{Tv}}, Cint, Ptr{UInt8}), - D, p, common()) + D, p, common_struct) end ### cholmod_check.h ### function check_dense(A::Dense{T}) where T<:VTypes ccall((:cholmod_l_check_dense, :libcholmod), Cint, (Ptr{C_Dense{T}}, Ptr{UInt8}), - A.p, common()) != 0 + A.p, common_struct) != 0 end # Non-Dense wrappers @@ -474,7 +476,7 @@ function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), nrow, ncol, nzmax, sorted, - packed, stype, REAL, common())) + packed, stype, REAL, common_struct)) end function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, sorted::Bool, packed::Bool, stype::Integer, ::Type{Complex{Float64}}) @@ -483,45 +485,45 @@ function allocate_sparse(nrow::Integer, ncol::Integer, nzmax::Integer, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), nrow, ncol, nzmax, sorted, - packed, stype, COMPLEX, common())) + packed, stype, COMPLEX, common_struct)) end function free_sparse!(ptr::Ptr{C_Sparse{Tv}}) where Tv<:VTypes @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_Sparse{Tv}}}, Ptr{UInt8}), - &ptr, common()) + (Ref{Ptr{C_Sparse{Tv}}}, Ptr{UInt8}), + ptr, common_struct) end function free_sparse!(ptr::Ptr{C_SparseVoid}) @isok ccall((@cholmod_name("free_sparse", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_SparseVoid}}, Ptr{UInt8}), - &ptr, common()) + (Ref{Ptr{C_SparseVoid}}, Ptr{UInt8}), + ptr, common_struct) end function free_factor!(ptr::Ptr{C_Factor{Tv}}) where Tv<:VTypes # Warning! Important that finalizer doesn't modify the global Common struct. @isok ccall((@cholmod_name("free_factor", SuiteSparse_long), :libcholmod), Cint, - (Ptr{Ptr{C_Factor{Tv}}}, Ptr{Void}), - &ptr, common()) + (Ref{Ptr{C_Factor{Tv}}}, Ptr{Void}), + ptr, common_struct) end function aat(A::Sparse{Tv}, fset::Vector{SuiteSparse_long}, mode::Integer) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("aat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{SuiteSparse_long}, Csize_t, Cint, Ptr{UInt8}), - A, fset, length(fset), mode, common())) + A, fset, length(fset), mode, common_struct)) end function sparse_to_dense(A::Sparse{Tv}) where Tv<:VTypes Dense(ccall((@cholmod_name("sparse_to_dense", SuiteSparse_long),:libcholmod), Ptr{C_Dense{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function dense_to_sparse(D::Dense{Tv}, ::Type{SuiteSparse_long}) where Tv<:VTypes Sparse(ccall((@cholmod_name("dense_to_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Dense{Tv}}, Cint, Ptr{UInt8}), - D, true, common())) + D, true, common_struct)) end function factor_to_sparse!(F::Factor{Tv}) where Tv<:VTypes @@ -530,14 +532,14 @@ function factor_to_sparse!(F::Factor{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("factor_to_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common())) + F, common_struct)) end function change_factor!(::Type{Float64}, to_ll::Bool, to_super::Bool, to_packed::Bool, to_monotonic::Bool, F::Factor{Tv}) where Tv<:VTypes @isok ccall((@cholmod_name("change_factor", SuiteSparse_long),:libcholmod), Cint, (Cint, Cint, Cint, Cint, Cint, Ptr{C_Factor{Tv}}, Ptr{UInt8}), - REAL, to_ll, to_super, to_packed, to_monotonic, F, common()) + REAL, to_ll, to_super, to_packed, to_monotonic, F, common_struct) # don't register finalizer since we reuse object Factor{Float64}(pointer(F), false) end @@ -546,7 +548,7 @@ function change_factor!(::Type{Complex{Float64}}, to_ll::Bool, to_super::Bool, to_packed::Bool, to_monotonic::Bool, F::Factor{Tv}) where Tv<:VTypes @isok ccall((@cholmod_name("change_factor", SuiteSparse_long),:libcholmod), Cint, (Cint, Cint, Cint, Cint, Cint, Ptr{C_Factor{Tv}}, Ptr{UInt8}), - COMPLEX, to_ll, to_super, to_packed, to_monotonic, F, common()) + COMPLEX, to_ll, to_super, to_packed, to_monotonic, F, common_struct) # don't register finalizer since we reuse object Factor{Complex{Float64}}(pointer(F), false) end @@ -554,77 +556,75 @@ end function check_sparse(A::Sparse{Tv}) where Tv<:VTypes ccall((@cholmod_name("check_sparse", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common()) != 0 + A, common_struct) != 0 end function check_factor(F::Factor{Tv}) where Tv<:VTypes ccall((@cholmod_name("check_factor", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common()) != 0 + F, common_struct) != 0 end function nnz(A::Sparse{Tv}) where Tv<:VTypes ccall((@cholmod_name("nnz", SuiteSparse_long),:libcholmod), Int, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common()) + A, common_struct) end function speye(m::Integer, n::Integer, ::Type{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("speye", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, xtyp(Tv), common())) + m, n, xtyp(Tv), common_struct)) end function spzeros(m::Integer, n::Integer, nzmax::Integer, ::Type{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("spzeros", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Csize_t, Csize_t, Csize_t, Cint, Ptr{UInt8}), - m, n, nzmax, xtyp(Tv), common())) + m, n, nzmax, xtyp(Tv), common_struct)) end function transpose_(A::Sparse{Tv}, values::Integer) where Tv<:VTypes Sparse(ccall((@cholmod_name("transpose", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, values, common())) + A, values, common_struct)) end function copy_factor(F::Factor{Tv}) where Tv<:VTypes Factor(ccall((@cholmod_name("copy_factor", SuiteSparse_long),:libcholmod), Ptr{C_Factor{Tv}}, (Ptr{C_Factor{Tv}}, Ptr{UInt8}), - F, common())) + F, common_struct)) end function copy_sparse(A::Sparse{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("copy_sparse", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - A, common())) + A, common_struct)) end function copy(A::Sparse{Tv}, stype::Integer, mode::Integer) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("copy", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Cint, Cint, Ptr{UInt8}), - A, stype, mode, common())) + A, stype, mode, common_struct)) end ### cholmod_check.h ### function print_sparse(A::Sparse{Tv}, name::String) where Tv<:VTypes isascii(name) || error("non-ASCII name: $name") - cm = common() - set_print_level(cm, 3) + set_print_level(common_struct, 3) @isok ccall((@cholmod_name("print_sparse", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Sparse{Tv}}, Ptr{UInt8}, Ptr{UInt8}), - A, name, cm) + A, name, common_struct) nothing end function print_factor(F::Factor{Tv}, name::String) where Tv<:VTypes - cm = common() - set_print_level(cm, 3) + set_print_level(common_struct, 3) @isok ccall((@cholmod_name("print_factor", SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Factor{Tv}}, Ptr{UInt8}, Ptr{UInt8}), - F, name, cm) + F, name, common_struct) nothing end @@ -641,7 +641,7 @@ function ssmult(A::Sparse{Tv}, B::Sparse{Tv}, stype::Integer, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Cint, Cint, Ptr{UInt8}), A, B, stype, values, - sorted, common())) + sorted, common_struct)) end function norm_sparse(A::Sparse{Tv}, norm::Integer) where Tv<:VTypes @@ -650,14 +650,14 @@ function norm_sparse(A::Sparse{Tv}, norm::Integer) where Tv<:VTypes end ccall((@cholmod_name("norm_sparse", SuiteSparse_long), :libcholmod), Cdouble, (Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, norm, common()) + A, norm, common_struct) end function horzcat(A::Sparse{Tv}, B::Sparse{Tv}, values::Bool) where Tv<:VRealTypes Sparse(ccall((@cholmod_name("horzcat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, B, values, common())) + A, B, values, common_struct)) end function scale!(S::Dense{Tv}, scale::Integer, A::Sparse{Tv}) where Tv<:VRealTypes @@ -686,7 +686,7 @@ function scale!(S::Dense{Tv}, scale::Integer, A::Sparse{Tv}) where Tv<:VRealType sA = unsafe_load(pointer(A)) @isok ccall((@cholmod_name("scale",SuiteSparse_long),:libcholmod), Cint, (Ptr{C_Dense{Tv}}, Cint, Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - S, scale, A, common()) + S, scale, A, common_struct) A end @@ -702,7 +702,7 @@ function sdmult!(A::Sparse{Tv}, transpose::Bool, (Ptr{C_Sparse{Tv}}, Cint, Ref{Complex128}, Ref{Complex128}, Ptr{C_Dense{Tv}}, Ptr{C_Dense{Tv}}, Ptr{UInt8}), - A, transpose, α, β, X, Y, common()) + A, transpose, α, β, X, Y, common_struct) Y end @@ -710,7 +710,7 @@ function vertcat(A::Sparse{Tv}, B::Sparse{Tv}, values::Bool) where Tv<:VRealType Sparse(ccall((@cholmod_name("vertcat", SuiteSparse_long), :libcholmod), Ptr{C_Sparse{Tv}}, (Ptr{C_Sparse{Tv}}, Ptr{C_Sparse{Tv}}, Cint, Ptr{UInt8}), - A, B, values, common())) + A, B, values, common_struct)) end function symmetry(A::Sparse{Tv}, option::Integer) where Tv<:VTypes @@ -722,7 +722,7 @@ function symmetry(A::Sparse{Tv}, option::Integer) where Tv<:VTypes (Ptr{C_Sparse{Tv}}, Cint, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{SuiteSparse_long}, Ptr{UInt8}), A, option, xmatched, pmatched, - nzoffdiag, nzdiag, common()) + nzoffdiag, nzdiag, common_struct) rv, xmatched[], pmatched[], nzoffdiag[], nzdiag[] end @@ -775,7 +775,7 @@ function solve(sys::Integer, F::Factor{Tv}, B::Dense{Tv}) where Tv<:VTypes end Dense(ccall((@cholmod_name("solve", SuiteSparse_long),:libcholmod), Ptr{C_Dense{Tv}}, (Cint, Ptr{C_Factor{Tv}}, Ptr{C_Dense{Tv}}, Ptr{UInt8}), - sys, F, B, common())) + sys, F, B, common_struct)) end function spsolve(sys::Integer, F::Factor{Tv}, B::Sparse{Tv}) where Tv<:VTypes @@ -786,7 +786,7 @@ function spsolve(sys::Integer, F::Factor{Tv}, B::Sparse{Tv}) where Tv<:VTypes Sparse(ccall((@cholmod_name("spsolve", SuiteSparse_long),:libcholmod), Ptr{C_Sparse{Tv}}, (Cint, Ptr{C_Factor{Tv}}, Ptr{C_Sparse{Tv}}, Ptr{UInt8}), - sys, F, B, common())) + sys, F, B, common_struct)) end # Autodetects the types @@ -794,7 +794,7 @@ function read_sparse(file::Libc.FILE, ::Type{SuiteSparse_long}) ptr = ccall((@cholmod_name("read_sparse", SuiteSparse_long), :libcholmod), Ptr{C_SparseVoid}, (Ptr{Void}, Ptr{UInt8}), - file.ptr, common()) + file.ptr, common_struct) if ptr == C_NULL throw(ArgumentError("sparse matrix construction failed. Check that input file is valid.")) end @@ -993,7 +993,7 @@ end convert(::Type{Sparse}, A::Dense) = dense_to_sparse(A, SuiteSparse_long) convert(::Type{Sparse}, L::Factor) = factor_to_sparse!(copy(L)) -function (::Type{Sparse})(filename::String) +function Sparse(filename::String) open(filename) do f return read_sparse(f, SuiteSparse_long) end @@ -1209,15 +1209,15 @@ IndexStyle(::Dense) = IndexLinear() size(FC::FactorComponent, i::Integer) = size(FC.F, i) size(FC::FactorComponent) = size(FC.F) -ctranspose(FC::FactorComponent{Tv,:L}) where {Tv} = FactorComponent{Tv,:U}(FC.F) -ctranspose(FC::FactorComponent{Tv,:U}) where {Tv} = FactorComponent{Tv,:L}(FC.F) -ctranspose(FC::FactorComponent{Tv,:PtL}) where {Tv} = FactorComponent{Tv,:UP}(FC.F) -ctranspose(FC::FactorComponent{Tv,:UP}) where {Tv} = FactorComponent{Tv,:PtL}(FC.F) -ctranspose(FC::FactorComponent{Tv,:D}) where {Tv} = FC -ctranspose(FC::FactorComponent{Tv,:LD}) where {Tv} = FactorComponent{Tv,:DU}(FC.F) -ctranspose(FC::FactorComponent{Tv,:DU}) where {Tv} = FactorComponent{Tv,:LD}(FC.F) -ctranspose(FC::FactorComponent{Tv,:PtLD}) where {Tv} = FactorComponent{Tv,:DUP}(FC.F) -ctranspose(FC::FactorComponent{Tv,:DUP}) where {Tv} = FactorComponent{Tv,:PtLD}(FC.F) +adjoint(FC::FactorComponent{Tv,:L}) where {Tv} = FactorComponent{Tv,:U}(FC.F) +adjoint(FC::FactorComponent{Tv,:U}) where {Tv} = FactorComponent{Tv,:L}(FC.F) +adjoint(FC::FactorComponent{Tv,:PtL}) where {Tv} = FactorComponent{Tv,:UP}(FC.F) +adjoint(FC::FactorComponent{Tv,:UP}) where {Tv} = FactorComponent{Tv,:PtL}(FC.F) +adjoint(FC::FactorComponent{Tv,:D}) where {Tv} = FC +adjoint(FC::FactorComponent{Tv,:LD}) where {Tv} = FactorComponent{Tv,:DU}(FC.F) +adjoint(FC::FactorComponent{Tv,:DU}) where {Tv} = FactorComponent{Tv,:LD}(FC.F) +adjoint(FC::FactorComponent{Tv,:PtLD}) where {Tv} = FactorComponent{Tv,:DUP}(FC.F) +adjoint(FC::FactorComponent{Tv,:DUP}) where {Tv} = FactorComponent{Tv,:PtLD}(FC.F) function getindex(A::Dense, i::Integer) s = unsafe_load(pointer(A)) @@ -1267,8 +1267,6 @@ end (*)(A::Sparse, B::VecOrMat) = (*)(A, Dense(B)) function A_mul_Bc(A::Sparse{Tv}, B::Sparse{Tv}) where Tv<:VRealTypes - cm = common() - if A !== B aa1 = transpose_(B, 2) ## result of ssmult will have stype==0, contain numerical values and be sorted @@ -1311,14 +1309,14 @@ function fact_(A::Sparse{<:VTypes}, cm::Array{UInt8}; sA.stype == 0 && throw(ArgumentError("sparse matrix is not symmetric/Hermitian")) if !postorder - unsafe_store!(common_postorder, 0) + unsafe_store!(common_postorder[], 0) end if isempty(perm) F = analyze(A, cm) else # user permutation provided if userperm_only # use perm even if it is worse than AMD - unsafe_store!(common_nmethods, 1) + unsafe_store!(common_nmethods[], 1) end F = analyze_p(A, SuiteSparse_long[p-1 for p in perm], cm) end @@ -1327,13 +1325,11 @@ function fact_(A::Sparse{<:VTypes}, cm::Array{UInt8}; end function cholfact!(F::Factor{Tv}, A::Sparse{Tv}; shift::Real=0.0) where Tv - cm = common() - # Makes it an LLt - unsafe_store!(common_final_ll, 1) + unsafe_store!(common_final_ll[], 1) # Compute the numerical factorization - factorize_p!(A, shift, F, cm) + factorize_p!(A, shift, F, common_struct) return F end @@ -1365,7 +1361,7 @@ cholfact!(F::Factor, A::Union{SparseMatrixCSC{T}, function cholfact(A::Sparse; shift::Real=0.0, perm::AbstractVector{SuiteSparse_long}=SuiteSparse_long[]) - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Compute the symbolic factorization @@ -1418,13 +1414,13 @@ cholfact(A::Union{SparseMatrixCSC{T}, SparseMatrixCSC{Complex{T}}, function ldltfact!(F::Factor{Tv}, A::Sparse{Tv}; shift::Real=0.0) where Tv - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Makes it an LDLt - unsafe_store!(common_final_ll, 0) + unsafe_store!(common_final_ll[], 0) # Really make sure it's an LDLt by avoiding supernodal factorization - unsafe_store!(common_supernodal, 0) + unsafe_store!(common_supernodal[], 0) # Compute the numerical factorization factorize_p!(A, shift, F, cm) @@ -1459,13 +1455,13 @@ ldltfact!(F::Factor, A::Union{SparseMatrixCSC{T}, function ldltfact(A::Sparse; shift::Real=0.0, perm::AbstractVector{SuiteSparse_long}=SuiteSparse_long[]) - cm = defaults(common()) + cm = defaults(common_struct) set_print_level(cm, 0) # Makes it an LDLt - unsafe_store!(common_final_ll, 0) + unsafe_store!(common_final_ll[], 0) # Really make sure it's an LDLt by avoiding supernodal factorization - unsafe_store!(common_supernodal, 0) + unsafe_store!(common_supernodal[], 0) # Compute the symbolic factorization F = fact_(A, cm; perm = perm) @@ -1536,7 +1532,7 @@ function lowrankupdowndate!(F::Factor{Tv}, C::Sparse{Tv}, update::Cint) where Tv end @isok ccall((:cholmod_l_updown, :libcholmod), Cint, (Cint, Ptr{C_Sparse{Tv}}, Ptr{C_Factor{Tv}}, Ptr{Void}), - update, C, F, common()) + update, C, F, common_struct) F end @@ -1651,8 +1647,8 @@ function (\)(L::FactorComponent, B::SparseVecOrMat) sparse(L\Sparse(B,0)) end -Ac_ldiv_B(L::FactorComponent, B) = ctranspose(L)\B -Ac_ldiv_B(L::FactorComponent, B::RowVector) = ctranspose(L)\B # ambiguity +Ac_ldiv_B(L::FactorComponent, B) = adjoint(L)\B +Ac_ldiv_B(L::FactorComponent, B::RowVector) = adjoint(L)\B # ambiguity (\)(L::Factor{T}, B::Dense{T}) where {T<:VTypes} = solve(CHOLMOD_A, L, B) # Explicit typevars are necessary to avoid ambiguities with defs in linalg/factorizations.jl @@ -1725,7 +1721,7 @@ function logdet(F::Factor{Tv}) where Tv<:VTypes f = unsafe_load(pointer(F)) res = zero(Tv) for d in diag(F); res += log(abs(d)) end - f.is_ll!=0 ? 2res : res + f.is_ll != 0 ? 2res : res end det(L::Factor) = exp(logdet(L)) diff --git a/base/sparse/linalg.jl b/base/sparse/linalg.jl index e1403d2967cb8..b3b78ecd50f13 100644 --- a/base/sparse/linalg.jl +++ b/base/sparse/linalg.jl @@ -44,7 +44,7 @@ end # In matrix-vector multiplication, the correct orientation of the vector is assumed. for (f, op, transp) in ((:A_mul_B, :identity, false), - (:Ac_mul_B, :ctranspose, true), + (:Ac_mul_B, :adjoint, true), (:At_mul_B, :transpose, true)) @eval begin function $(Symbol(f,:!))(α::Number, A::SparseMatrixCSC, B::StridedVecOrMat, β::Number, C::StridedVecOrMat) @@ -124,11 +124,11 @@ end (*)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = spmatmul(A,B) for (f, opA, opB) in ((:A_mul_Bt, :identity, :transpose), - (:A_mul_Bc, :identity, :ctranspose), + (:A_mul_Bc, :identity, :adjoint), (:At_mul_B, :transpose, :identity), - (:Ac_mul_B, :ctranspose, :identity), + (:Ac_mul_B, :adjoint, :identity), (:At_mul_Bt, :transpose, :transpose), - (:Ac_mul_Bc, :ctranspose, :ctranspose)) + (:Ac_mul_Bc, :adjoint, :adjoint)) @eval begin function ($f)(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} spmatmul(($opA)(A), ($opB)(B)) @@ -301,8 +301,8 @@ function A_rdiv_B!(A::SparseMatrixCSC{T}, D::Diagonal{T}) where T if iszero(ddj) throw(LinAlg.SingularException(j)) end - for k in nzrange(A, j) - nonz[k] /= ddj + for i in nzrange(A, j) + nonz[i] /= ddj end end A @@ -879,7 +879,7 @@ for f in (:\, :Ac_ldiv_B, :At_ldiv_B) if m == n if istril(A) if istriu(A) - return ($f)(Diagonal(A), B) + return ($f)(Diagonal(Vector(diag(A))), B) else return ($f)(LowerTriangular(A), B) end diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 28126d551c10b..abe6289b18070 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -14,12 +14,12 @@ import Base.LinAlg: At_ldiv_B!, Ac_ldiv_B!, A_rdiv_B!, A_rdiv_Bc! import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, atan, atand, atanh, broadcast!, chol, conj!, cos, cosc, cosd, cosh, cospi, cot, - cotd, coth, countnz, csc, cscd, csch, ctranspose!, diag, diff, done, dot, eig, + cotd, coth, count, csc, cscd, csch, adjoint!, diag, diff, done, dot, eig, exp10, exp2, eye, findn, floor, hash, indmin, inv, issymmetric, istril, istriu, log10, log2, lu, next, sec, secd, sech, show, sin, sinc, sind, sinh, sinpi, squeeze, start, sum, summary, tan, tand, tanh, trace, transpose!, tril!, triu!, trunc, vecnorm, abs, abs2, - broadcast, ceil, complex, cond, conj, convert, copy, copy!, ctranspose, diagm, + broadcast, ceil, complex, cond, conj, convert, copy, copy!, adjoint, diagm, exp, expm1, factorize, find, findmax, findmin, findnz, float, full, getindex, vcat, hcat, hvcat, cat, imag, indmax, ishermitian, kron, length, log, log1p, max, min, maximum, minimum, norm, one, promote_eltype, real, reinterpret, reshape, rot180, diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 11a0a434edc7e..5d27cc316c31e 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -52,7 +52,6 @@ getrowval(S::SparseMatrixCSCView) = S.parent.rowval getnzval( S::SparseMatrixCSC) = S.nzval getnzval( S::SparseMatrixCSCView) = S.parent.nzval - """ nnz(A) @@ -70,9 +69,9 @@ julia> nnz(A) 3 ``` """ -nnz(S::SparseMatrixCSC) = Int(S.colptr[S.n + 1]-1) -countnz(S::SparseMatrixCSC) = countnz(S.nzval) -count(S::SparseMatrixCSC) = count(S.nzval) +nnz(S::SparseMatrixCSC) = Int(S.colptr[S.n + 1] - 1) +count(S::SparseMatrixCSC) = count(S.nzval) +count(pred, S::SparseMatrixCSC) = count(pred, S.nzval) + pred(zero(eltype(S)))*(prod(size(S)) - nnz(S)) """ nonzeros(A) @@ -764,6 +763,11 @@ function sparse(B::Bidiagonal) return sparse([1:m;1:m-1],[1:m;2:m],[B.dv;B.ev], Int(m), Int(m)) # upper bidiagonal end +function sparse(D::Diagonal{T}) where T + m = length(D.diag) + return SparseMatrixCSC(m, m, collect(1:(m+1)), collect(1:m), Vector{T}(D.diag)) +end + ## Transposition and permutation methods """ @@ -853,14 +857,14 @@ function ftranspose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}, f::Fu halfperm!(X, A, 1:A.n, f) end transpose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, identity) -ctranspose!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, conj) +adjoint!(X::SparseMatrixCSC{Tv,Ti}, A::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} = ftranspose!(X, A, conj) function ftranspose(A::SparseMatrixCSC{Tv,Ti}, f::Function) where {Tv,Ti} X = SparseMatrixCSC(A.n, A.m, Vector{Ti}(A.m+1), Vector{Ti}(nnz(A)), Vector{Tv}(nnz(A))) halfperm!(X, A, 1:A.n, f) end transpose(A::SparseMatrixCSC) = ftranspose(A, identity) -ctranspose(A::SparseMatrixCSC) = ftranspose(A, conj) +adjoint(A::SparseMatrixCSC) = ftranspose(A, conj) """ unchecked_noalias_permute!(X::SparseMatrixCSC{Tv,Ti}, @@ -1274,7 +1278,6 @@ julia> dropzeros(A) """ dropzeros(A::SparseMatrixCSC, trim::Bool = true) = dropzeros!(copy(A), trim) - ## Find methods function find(S::SparseMatrixCSC) @@ -1332,7 +1335,6 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end - import Base.Random.GLOBAL_RNG function sprand_IJ(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) ((m < 0) || (n < 0)) && throw(ArgumentError("invalid Array dimensions")) @@ -1424,7 +1426,6 @@ sprand(r::AbstractRNG, ::Type{T}, m::Integer, n::Integer, density::AbstractFloat sprand(r::AbstractRNG, ::Type{Bool}, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density, truebools, Bool) sprand(::Type{T}, m::Integer, n::Integer, density::AbstractFloat) where {T} = sprand(GLOBAL_RNG, T, m, n, density) - """ sprandn([rng], m[,n],p::AbstractFloat) @@ -1447,7 +1448,6 @@ julia> sprandn(rng, 2, 2, 0.75) sprandn(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) = sprand(r,m,n,density,randn,Float64) sprandn(m::Integer, n::Integer, density::AbstractFloat) = sprandn(GLOBAL_RNG,m,n,density) - """ spones(S) @@ -1570,7 +1570,6 @@ end sparse(S::UniformScaling, m::Integer, n::Integer=m) = speye_scaled(S.λ, m, n) - # TODO: More appropriate location? conj!(A::SparseMatrixCSC) = (@inbounds broadcast!(conj, A.nzval, A.nzval); A) (-)(A::SparseMatrixCSC) = SparseMatrixCSC(A.m, A.n, copy(A.colptr), copy(A.rowval), map(-, A.nzval)) @@ -1813,109 +1812,114 @@ function _mapreducecols!(f, op::typeof(+), R::AbstractArray, A::SparseMatrixCSC{ end # findmax/min and indmax/min methods +# find first zero value in sparse matrix - return linear index in full matrix +# non-structural zeros are identified by x == 0 in line with the sparse constructors. function _findz(A::SparseMatrixCSC{Tv,Ti}, rows=1:A.m, cols=1:A.n) where {Tv,Ti} colptr = A.colptr; rowval = A.rowval; nzval = A.nzval - zval = zero(Tv) - col = cols[1]; row = 0 + zval = 0 + row = 0 rowmin = rows[1]; rowmax = rows[end] allrows = (rows == 1:A.m) - @inbounds while col <= cols[end] + @inbounds for col in cols r1::Int = colptr[col] r2::Int = colptr[col+1] - 1 if !allrows && (r1 <= r2) r1 = searchsortedfirst(rowval, rowmin, r1, r2, Forward) - (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward) + 1) + (r1 <= r2 ) && (r2 = searchsortedlast(rowval, rowmax, r1, r2, Forward)) end row = rowmin - (r1 > r2) && (return sub2ind(size(A),row,col)) while (r1 <= r2) && (row == rowval[r1]) && (nzval[r1] != zval) r1 += 1 row += 1 end - (row <= rowmax) && (return sub2ind(size(A),row,col)) - col += 1 + (row <= rowmax) && (return CartesianIndex(row, col)) end - return 0 + return CartesianIndex(0, 0) end -macro _findr(op, A, region, Tv, Ti) - esc(quote - N = nnz($A) - L = length($A) - (L == 0) && error("array must be non-empty") +function _findr(op, A, region, Tv) + Ti = eltype(keys(A)) + i1 = first(keys(A)) + N = nnz(A) + L = length(A) + if L == 0 + if prod(map(length, Base.reduced_indices(A, region))) != 0 + throw(ArgumentError("array slices must be non-empty")) + else + ri = Base.reduced_indices0(A, region) + return (similar(A, ri), similar(dims->zeros(Ti, dims), ri)) + end + end - colptr = $A.colptr; rowval = $A.rowval; nzval = $A.nzval; m = $A.m; n = $A.n - zval = zero($Tv) - szA = size($A) + colptr = A.colptr; rowval = A.rowval; nzval = A.nzval; m = A.m; n = A.n + zval = zero(Tv) + szA = size(A) - if $region == 1 || $region == (1,) - (N == 0) && (return (fill(zval,1,n), fill(convert($Ti,1),1,n))) - S = Vector{$Tv}(n); I = Vector{$Ti}(n) + if region == 1 || region == (1,) + (N == 0) && (return (fill(zval,1,n), fill(i1,1,n))) + S = Vector{Tv}(n); I = Vector{Ti}(n) @inbounds for i = 1 : n - Sc = zval; Ic = _findz($A, 1:m, i:i) - if Ic == 0 + Sc = zval; Ic = _findz(A, 1:m, i:i) + if Ic == CartesianIndex(0, 0) j = colptr[i] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) Sc = nzval[j] end for j = colptr[i] : colptr[i+1]-1 - if ($op)(nzval[j], Sc) + if op(nzval[j], Sc) Sc = nzval[j] - Ic = sub2ind(szA, rowval[j], i) + Ic = CartesianIndex(rowval[j], i) end end S[i] = Sc; I[i] = Ic end return(reshape(S,1,n), reshape(I,1,n)) - elseif $region == 2 || $region == (2,) - (N == 0) && (return (fill(zval,m,1), fill(convert($Ti,1),m,1))) - S = Vector{$Tv}(m); I = Vector{$Ti}(m) + elseif region == 2 || region == (2,) + (N == 0) && (return (fill(zval,m,1), fill(i1,m,1))) + S = Vector{Tv}(m); I = Vector{Ti}(m) @inbounds for row in 1:m - S[row] = zval; I[row] = _findz($A, row:row, 1:n) - if I[row] == 0 - I[row] = sub2ind(szA, row, 1) + S[row] = zval; I[row] = _findz(A, row:row, 1:n) + if I[row] == CartesianIndex(0, 0) + I[row] = CartesianIndex(row, 1) S[row] = A[row,1] end end @inbounds for i = 1 : n, j = colptr[i] : colptr[i+1]-1 row = rowval[j] - if ($op)(nzval[j], S[row]) + if op(nzval[j], S[row]) S[row] = nzval[j] - I[row] = sub2ind(szA, row, i) + I[row] = CartesianIndex(row, i) end end return (reshape(S,m,1), reshape(I,m,1)) - elseif $region == (1,2) - (N == 0) && (return (fill(zval,1,1), fill(convert($Ti,1),1,1))) - hasz = nnz($A) != length($A) + elseif region == (1,2) + (N == 0) && (return (fill(zval,1,1), fill(i1,1,1))) + hasz = nnz(A) != length(A) Sv = hasz ? zval : nzval[1] - Iv::($Ti) = hasz ? _findz($A) : 1 - @inbounds for i = 1 : $A.n, j = colptr[i] : (colptr[i+1]-1) - if ($op)(nzval[j], Sv) + Iv::(Ti) = hasz ? _findz(A) : i1 + @inbounds for i = 1 : A.n, j = colptr[i] : (colptr[i+1]-1) + if op(nzval[j], Sv) Sv = nzval[j] - Iv = sub2ind(szA, rowval[j], i) + Iv = CartesianIndex(rowval[j], i) end end return (fill(Sv,1,1), fill(Iv,1,1)) else throw(ArgumentError("invalid value for region; must be 1, 2, or (1,2)")) end - end) #quote end -findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(<, A, region, Tv, Ti) -findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = @_findr(>, A, region, Tv, Ti) +_isless_fm(a, b) = b == b && ( a != a || isless(a, b) ) +_isgreater_fm(a, b) = b == b && ( a != a || isless(b, a) ) + +findmin(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isless_fm, A, region, Tv) +findmax(A::SparseMatrixCSC{Tv,Ti}, region) where {Tv,Ti} = _findr(_isgreater_fm, A, region, Tv) findmin(A::SparseMatrixCSC) = (r=findmin(A,(1,2)); (r[1][1], r[2][1])) findmax(A::SparseMatrixCSC) = (r=findmax(A,(1,2)); (r[1][1], r[2][1])) indmin(A::SparseMatrixCSC) = findmin(A)[2] indmax(A::SparseMatrixCSC) = findmax(A)[2] -#all(A::SparseMatrixCSC{Bool}, region) = reducedim(all,A,region,true) -#any(A::SparseMatrixCSC{Bool}, region) = reducedim(any,A,region,false) -#sum(A::SparseMatrixCSC{Bool}, region) = reducedim(+,A,region,0,Int) -#sum(A::SparseMatrixCSC{Bool}) = countnz(A) - ## getindex function rangesearch(haystack::Range, needle) (i,rem) = divrem(needle - first(haystack), step(haystack)) @@ -2042,8 +2046,8 @@ function getindex_I_sorted(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::Abst end function getindex_I_sorted_bsearch_A(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) @@ -2101,8 +2105,8 @@ function getindex_I_sorted_bsearch_A(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVecto end function getindex_I_sorted_linear(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) @@ -2160,8 +2164,8 @@ function getindex_I_sorted_linear(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, end function getindex_I_sorted_bsearch_I(A::SparseMatrixCSC{Tv,Ti}, I::AbstractVector, J::AbstractVector) where {Tv,Ti} - const nI = length(I) - const nJ = length(J) + nI = length(I) + nJ = length(J) colptrA = A.colptr; rowvalA = A.rowval; nzvalA = A.nzval colptrS = Vector{Ti}(nJ+1) @@ -2553,7 +2557,6 @@ setindex!(A::SparseMatrixCSC, v::AbstractVector, i::Integer, J::AbstractVector{< setindex!(A::SparseMatrixCSC, v::AbstractVector, I::AbstractVector{T}, J::AbstractVector{T}) where {T<:Integer} = setindex!(A, reshape(v, length(I), length(J)), I, J) - # A[I,J] = B function setindex!(A::SparseMatrixCSC{Tv,Ti}, B::SparseMatrixCSC{Tv,Ti}, I::AbstractVector{T}, J::AbstractVector{T}) where {Tv,Ti,T<:Integer} if size(B,1) != length(I) || size(B,2) != length(J) @@ -2795,7 +2798,6 @@ function setindex!(A::SparseMatrixCSC, x, I::AbstractMatrix{Bool}) A end - function setindex!(A::SparseMatrixCSC, x, I::AbstractVector{<:Real}) n = length(I) (n == 0) && (return A) @@ -3033,7 +3035,6 @@ dropstored!(A::SparseMatrixCSC, ::Colon) = dropstored!(A, :, :) # TODO: Implement linear indexing methods for dropstored! ? # TODO: Implement logical indexing methods for dropstored! ? - # Sparse concatenation function vcat(X::SparseMatrixCSC...) @@ -3091,7 +3092,6 @@ end end end - function hcat(X::SparseMatrixCSC...) num = length(X) mX = Int[ size(x, 1) for x in X ] @@ -3380,83 +3380,39 @@ function expandptr(V::Vector{<:Integer}) res end -## diag and related using an iterator - -mutable struct SpDiagIterator{Tv,Ti} - A::SparseMatrixCSC{Tv,Ti} - n::Int -end -SpDiagIterator(A::SparseMatrixCSC) = SpDiagIterator(A,minimum(size(A))) - -length(d::SpDiagIterator) = d.n -start(d::SpDiagIterator) = 1 -done(d::SpDiagIterator, j) = j > d.n -function next(d::SpDiagIterator{Tv}, j) where Tv - A = d.A - r1 = Int(A.colptr[j]) - r2 = Int(A.colptr[j+1]-1) - (r1 > r2) && (return (zero(Tv), j+1)) - r1 = searchsortedfirst(A.rowval, j, r1, r2, Forward) - (((r1 > r2) || (A.rowval[r1] != j)) ? zero(Tv) : A.nzval[r1], j+1) +function diag(A::SparseMatrixCSC{Tv,Ti}, d::Integer=0) where {Tv,Ti} + m, n = size(A) + k = Int(d) + if !(-m <= k <= n) + throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($m, $n)")) + end + l = k < 0 ? min(m+k,n) : min(n-k,m) + r, c = k <= 0 ? (-k, 0) : (0, k) # start row/col -1 + ind = Vector{Ti}() + val = Vector{Tv}() + for i in 1:l + r += 1; c += 1 + r1 = Int(A.colptr[c]) + r2 = Int(A.colptr[c+1]-1) + r1 > r2 && continue + r1 = searchsortedfirst(A.rowval, r, r1, r2, Forward) + ((r1 > r2) || (A.rowval[r1] != r)) && continue + push!(ind, i) + push!(val, A.nzval[r1]) + end + return SparseVector{Tv,Ti}(l, ind, val) end function trace(A::SparseMatrixCSC{Tv}) where Tv - if size(A,1) != size(A,2) - throw(DimensionMismatch("expected square matrix")) - end + n = checksquare(A) s = zero(Tv) - for d in SpDiagIterator(A) - s += d + for i in 1:n + s += A[i,i] end - s + return s end -diag(A::SparseMatrixCSC{Tv}) where {Tv} = Tv[d for d in SpDiagIterator(A)] - -function diagm(v::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} - if size(v,1) != 1 && size(v,2) != 1 - throw(DimensionMismatch("input should be nx1 or 1xn")) - end - - n = length(v) - numnz = nnz(v) - colptr = Vector{Ti}(n+1) - rowval = Vector{Ti}(numnz) - nzval = Vector{Tv}(numnz) - - if size(v,1) == 1 - copy!(colptr, 1, v.colptr, 1, n+1) - ptr = 1 - for col = 1:n - if colptr[col] != colptr[col+1] - rowval[ptr] = col - nzval[ptr] = v.nzval[ptr] - ptr += 1 - end - end - else - copy!(rowval, 1, v.rowval, 1, numnz) - copy!(nzval, 1, v.nzval, 1, numnz) - colptr[1] = 1 - ptr = 1 - col = 1 - while col <= n && ptr <= numnz - while rowval[ptr] > col - colptr[col+1] = colptr[col] - col += 1 - end - colptr[col+1] = colptr[col] + 1 - ptr += 1 - col += 1 - end - if col <= n - colptr[(col+1):(n+1)] = colptr[col] - end - end - - return SparseMatrixCSC(n, n, colptr, rowval, nzval) -end # Sort all the indices in each column of a CSC sparse matrix # sortSparseMatrixCSC!(A, sortindices = :sortcols) # Sort each column with sort() diff --git a/base/sparse/sparsevector.jl b/base/sparse/sparsevector.jl index 80225e4d328c8..630fea82a0a12 100644 --- a/base/sparse/sparsevector.jl +++ b/base/sparse/sparsevector.jl @@ -37,11 +37,11 @@ const SparseVectorUnion{T} = Union{SparseVector{T}, SparseColumnView{T}} ### Basic properties -length(x::SparseVector) = x.n -size(x::SparseVector) = (x.n,) -nnz(x::SparseVector) = length(x.nzval) -countnz(x::SparseVector) = countnz(x.nzval) -count(x::SparseVector) = count(x.nzval) +length(x::SparseVector) = x.n +size(x::SparseVector) = (x.n,) +nnz(x::SparseVector) = length(x.nzval) +count(x::SparseVector) = count(x.nzval) +count(f, x::SparseVector) = count(f, x.nzval) + f(zero(eltype(x)))*(length(x) - nnz(x)) nonzeros(x::SparseVector) = x.nzval function nonzeros(x::SparseColumnView) @@ -955,7 +955,7 @@ vcat(X::Union{Vector,SparseVector}...) = vcat(map(sparse, X)...) # TODO: A definition similar to the third exists in base/linalg/bidiag.jl. These definitions # should be consolidated in a more appropriate location, e.g. base/linalg/special.jl. -const _SparseArrays = Union{SparseVector, SparseMatrixCSC} +const _SparseArrays = Union{SparseVector, SparseMatrixCSC, RowVector{<:Any, <:SparseVector}} const _SpecialArrays = Union{Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal} const _SparseConcatArrays = Union{_SpecialArrays, _SparseArrays} @@ -970,9 +970,9 @@ const _Triangular_DenseArrays{T,A<:Matrix} = Base.LinAlg.AbstractTriangular{T,A} const _Annotated_DenseArrays = Union{_Triangular_DenseArrays, _Symmetric_DenseArrays, _Hermitian_DenseArrays} const _Annotated_Typed_DenseArrays{T} = Union{_Triangular_DenseArrays{T}, _Symmetric_DenseArrays{T}, _Hermitian_DenseArrays{T}} -const _SparseConcatGroup = Union{Vector, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} -const _DenseConcatGroup = Union{Vector, Matrix, _Annotated_DenseArrays} -const _TypedDenseConcatGroup{T} = Union{Vector{T}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} +const _SparseConcatGroup = Union{Vector, RowVector{<:Any, <:Vector}, Matrix, _SparseConcatArrays, _Annotated_SparseConcatArrays, _Annotated_DenseArrays} +const _DenseConcatGroup = Union{Vector, RowVector{<:Any, <:Vector}, Matrix, _Annotated_DenseArrays} +const _TypedDenseConcatGroup{T} = Union{Vector{T}, RowVector{T,Vector{T}}, Matrix{T}, _Annotated_Typed_DenseArrays{T}} # Concatenations involving un/annotated sparse/special matrices/vectors should yield sparse arrays function cat(catdims, Xin::_SparseConcatGroup...) @@ -1401,7 +1401,7 @@ vecnorm(x::SparseVectorUnion, p::Real=2) = vecnorm(nonzeros(x), p) # Transpose # (The only sparse matrix structure in base is CSC, so a one-row sparse matrix is worse than dense) @inline transpose(sv::SparseVector) = RowVector(sv) -@inline ctranspose(sv::SparseVector) = RowVector(conj(sv)) +@inline adjoint(sv::SparseVector) = RowVector(conj(sv)) ### BLAS Level-1 diff --git a/base/sparse/spqr.jl b/base/sparse/spqr.jl index 6b27d0b25146b..d752712027760 100644 --- a/base/sparse/spqr.jl +++ b/base/sparse/spqr.jl @@ -60,7 +60,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, H, # m-by-nh Householder vectors HPinv, # size m row permutation HTau, # 1-by-nh Householder coefficients - CHOLMOD.common()) # /* workspace and parameters */ + CHOLMOD.common_struct) # /* workspace and parameters */ if rnk < 0 error("Sparse QR factorization failed") @@ -79,7 +79,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, # the common struct is updated ccall((:cholmod_l_free, :libcholmod), Void, (Csize_t, Cint, Ptr{CHOLMOD.SuiteSparse_long}, Ptr{Void}), - n, sizeof(CHOLMOD.SuiteSparse_long), e, CHOLMOD.common()) + n, sizeof(CHOLMOD.SuiteSparse_long), e, CHOLMOD.common_struct) end hpinv = HPinv[] if hpinv == C_NULL @@ -94,7 +94,7 @@ function _qr!(ordering::Integer, tol::Real, econ::Integer, getCTX::Integer, # the common struct is updated ccall((:cholmod_l_free, :libcholmod), Void, (Csize_t, Cint, Ptr{CHOLMOD.SuiteSparse_long}, Ptr{Void}), - m, sizeof(CHOLMOD.SuiteSparse_long), hpinv, CHOLMOD.common()) + m, sizeof(CHOLMOD.SuiteSparse_long), hpinv, CHOLMOD.common_struct) end return rnk, _E, _HPinv diff --git a/base/sparse/umfpack.jl b/base/sparse/umfpack.jl index d123ffe04581b..7f7177c55c37c 100644 --- a/base/sparse/umfpack.jl +++ b/base/sparse/umfpack.jl @@ -7,7 +7,7 @@ export UmfpackLU import Base: (\), Ac_ldiv_B, At_ldiv_B, findnz, getindex, show, size import Base.LinAlg: A_ldiv_B!, Ac_ldiv_B!, At_ldiv_B!, Factorization, det, lufact -importall ..SparseArrays +using ..SparseArrays import ..SparseArrays: increment, increment!, decrement, decrement!, nnz include("umfpack_h.jl") @@ -337,11 +337,11 @@ for itype in UmfpackIndexTypes (Ptr{$itype},Ptr{$itype},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Void}, - Ptr{$itype},Ptr{Float64},Ptr{Void}), + Ref{$itype},Ptr{Float64},Ptr{Void}), Lp,Lj,Lx, Up,Ui,Ux, P, Q, C_NULL, - &0, Rs, lu.numeric) + 0, Rs, lu.numeric) (transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), Lx)), SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), Ux), increment!(P), increment!(Q), Rs) @@ -364,11 +364,11 @@ for itype in UmfpackIndexTypes (Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Float64},Ptr{Float64}, Ptr{$itype},Ptr{$itype},Ptr{Void}, Ptr{Void}, - Ptr{$itype},Ptr{Float64},Ptr{Void}), + Ref{$itype},Ptr{Float64},Ptr{Void}), Lp,Lj,Lx,Lz, Up,Ui,Ux,Uz, P, Q, C_NULL, C_NULL, - &0, Rs, lu.numeric) + 0, Rs, lu.numeric) (transpose(SparseMatrixCSC(min(n_row, n_col), n_row, increment!(Lp), increment!(Lj), complex.(Lx, Lz))), SparseMatrixCSC(min(n_row, n_col), n_col, increment!(Up), increment!(Ui), complex.(Ux, Uz)), increment!(P), increment!(Q), Rs) diff --git a/base/special/exp.jl b/base/special/exp.jl index e49bfb5ed942b..ccabe295597db 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -117,11 +117,11 @@ function exp(x::T) where T<:Union{Float32,Float64} if k > -significand_bits(T) # multiply by 2.0 first to prevent overflow, which helps extends the range k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) - twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) return y*twopk else # add significand_bits(T) + 1 to lift the range outside the subnormals - twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) return y * twopk * T(2.0)^(-significand_bits(T) - 1) end elseif xa < reinterpret(Unsigned, exp_small_thres(T)) # |x| < exp_small_thres diff --git a/base/special/exp10.jl b/base/special/exp10.jl index 2cfb4ff50057e..711da3b04549b 100644 --- a/base/special/exp10.jl +++ b/base/special/exp10.jl @@ -67,6 +67,20 @@ MIN_EXP10(::Type{Float32}) = -45.15449934959718f0 # log10 2^-150 @eval exp10_small_thres(::Type{Float64}) = $(2.0^-29) @eval exp10_small_thres(::Type{Float32}) = $(2.0f0^-14) +""" + exp10(x) + +Compute ``10^x``. + +# Examples +```jldoctest +julia> exp10(2) +100.0 + +julia> exp10(0.2) +1.5848931924611136 +``` +""" function exp10(x::T) where T<:Union{Float32,Float64} xa = reinterpret(Unsigned, x) & ~sign_mask(T) xsb = signbit(x) @@ -105,11 +119,11 @@ function exp10(x::T) where T<:Union{Float32,Float64} if k > -significand_bits(T) # multiply by 2.0 first to prevent overflow, extending the range k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) - twopk = reinterpret(T, rem(exponent_bias(T) + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) return y*twopk else # add significand_bits(T) + 1 to lift the range outside the subnormals - twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, fpinttype(T)) << significand_bits(T)) + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) return y * twopk * T(2.0)^(-significand_bits(T) - 1) end elseif xa < reinterpret(Unsigned, exp10_small_thres(T)) # |x| < exp10_small_thres diff --git a/base/special/trig.jl b/base/special/trig.jl index 6c1c81c300d93..2cd837fe3293b 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -9,9 +9,10 @@ struct DoubleFloat32 hi::Float64 end -# *_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 -# which are made available under the following licence: +# 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. +# 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. ## @@ -80,6 +81,111 @@ end sin_kernel(x::Real) = sin(x) cos_kernel(x::Real) = cos(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(t::Float64) = + @horner(t, + 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 function asin_kernel(t::Float64, x::Float64) + pio2_lo = 6.12323399573676603587e-17 + s = sqrt_llvm(t) + p = arc_p(t) # numerator polynomial + q = arc_q(t) # denominator polynomial + if abs(x) >= 0.975 # |x| > 0.975 + Rx = p/q + return flipsign(pi/2 - (2.0*(s + s*Rx) - pio2_lo), x) + else + s0 = reinterpret(Float64, (reinterpret(UInt64, s) >> 32) << 32) + c = (t - s0*s0)/(s + s0) + Rx = p/q + p = 2.0*s*Rx - (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)) + p = arc_p(t) # numerator polynomial + q = arc_q(t) # denominator polynomial + Rx = p/q # rational approximation + flipsign(Float32(pi/2 - 2*(s + s*Rx)), 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} + # Method : + # 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) + # + # For x in [0.5,1] + # 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)) + # + # Special cases: + # if |x|>1, throw DomainError + 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 + # else if |x|<0.5 we use a rational approximation R(x)=p(x)/q(x) such that + # tan(x) ≈ x+x*R(x) + x² = x*x + Rx = arc_p(x²)/arc_q(x²) # rational approximation + return muladd(x, Rx, x) + end + # else 1/2 <= |x| < 1 + t = (T(1.0) - absx)/2 + return asin_kernel(t, x) +end + # multiply in extended precision function mulpi_ext(x::Float64) m = 3.141592653589793 diff --git a/base/stacktraces.jl b/base/stacktraces.jl index 8fbe116a6270c..a12d9c5888590 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -59,7 +59,8 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles pointer::UInt64 # Large enough to be read losslessly on 32- and 64-bit machines. end -StackFrame(func, file, line) = StackFrame(func, file, line, Nullable{Core.MethodInstance}(), false, false, 0) +StackFrame(func, file, line) = StackFrame(Symbol(func), Symbol(file), line, + Nullable{Core.MethodInstance}(), false, false, 0) """ StackTrace diff --git a/base/statistics.jl b/base/statistics.jl index e0e5a3ea2da15..226a8661a85f1 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -59,7 +59,7 @@ julia> mean!([1. 1.], v) """ function mean!(R::AbstractArray, A::AbstractArray) sum!(R, A; init=true) - scale!(R, _length(R) / _length(A)) + scale!(R, max(1, _length(R)) // _length(A)) return R end @@ -175,7 +175,7 @@ function varm!(R::AbstractArray{S}, A::AbstractArray, m::AbstractArray; correcte fill!(R, convert(S, NaN)) else rn = div(_length(A), _length(R)) - Int(corrected) - scale!(centralize_sumabs2!(R, A, m), one(S)/rn) + scale!(centralize_sumabs2!(R, A, m), 1//rn) end return R end @@ -335,12 +335,18 @@ unscaled_covzm(x::AbstractMatrix, y::AbstractMatrix, vardim::Int) = # covzm (with centered data) covzm(x::AbstractVector; corrected::Bool=true) = unscaled_covzm(x) / (_length(x) - Int(corrected)) -covzm(x::AbstractMatrix, vardim::Int=1; corrected::Bool=true) = - scale!(unscaled_covzm(x, vardim), inv(size(x,vardim) - Int(corrected))) +function covzm(x::AbstractMatrix, vardim::Int=1; corrected::Bool=true) + C = unscaled_covzm(x, vardim) + T = promote_type(typeof(first(C) / 1), eltype(C)) + return scale!(convert(AbstractMatrix{T}, C), 1//(size(x, vardim) - corrected)) +end covzm(x::AbstractVector, y::AbstractVector; corrected::Bool=true) = unscaled_covzm(x, y) / (_length(x) - Int(corrected)) -covzm(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int=1; corrected::Bool=true) = - scale!(unscaled_covzm(x, y, vardim), inv(_getnobs(x, y, vardim) - Int(corrected))) +function covzm(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int=1; corrected::Bool=true) + C = unscaled_covzm(x, y, vardim) + T = promote_type(typeof(first(C) / 1), eltype(C)) + return scale!(convert(AbstractArray{T}, C), 1//(_getnobs(x, y, vardim) - corrected)) +end # covm (with provided mean) @@ -593,9 +599,9 @@ function median!(v::AbstractVector) n = length(inds) mid = div(first(inds)+last(inds),2) if isodd(n) - return middle(select!(v,mid)) + return middle(partialsort!(v,mid)) else - m = select!(v, mid:mid+1) + m = partialsort!(v, mid:mid+1) return middle(m[1], m[2]) end end diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 6609cd1417b96..5fd209255eb0c 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -10,20 +10,15 @@ next(s::AbstractString, i::Integer) = next(s,Int(i)) string() = "" string(s::AbstractString) = s -""" - String(s::AbstractString) +(::Type{Vector{UInt8}})(s::AbstractString) = Vector{UInt8}(String(s)) +(::Type{Array{UInt8}})(s::AbstractString) = Vector{UInt8}(s) +(::Type{Vector{Char}})(s::AbstractString) = collect(s) -Convert a string to a contiguous byte array representation encoded as UTF-8 bytes. -This representation is often appropriate for passing strings to C. -""" -String(s::AbstractString) = print_to_string(s) +Symbol(s::AbstractString) = Symbol(String(s)) -convert(::Type{Vector{UInt8}}, s::AbstractString) = convert(Vector{UInt8}, String(s)) -convert(::Type{Array{UInt8}}, s::AbstractString) = convert(Vector{UInt8}, s) -convert(::Type{String}, s::AbstractString) = String(s) -convert(::Type{Vector{Char}}, s::AbstractString) = collect(s) -convert(::Type{Symbol}, s::AbstractString) = Symbol(s) -convert(::Type{String}, s::Symbol) = unsafe_string(Cstring(s)) +# string types are convertible +convert(::Type{T}, s::T) where {T<:AbstractString} = s +convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s) ## generic supplied functions ## @@ -40,7 +35,6 @@ getindex(s::AbstractString, v::AbstractVector{Bool}) = throw(ArgumentError("logical indexing not supported for strings")) get(s::AbstractString, i::Integer, default) = isvalid(s,i) ? s[i] : default -Symbol(s::AbstractString) = Symbol(String(s)) """ sizeof(s::AbstractString) @@ -176,9 +170,7 @@ end ## Generic indexing functions ## prevind(s::DirectIndexString, i::Integer) = Int(i)-1 -prevind(s::AbstractArray , i::Integer) = Int(i)-1 nextind(s::DirectIndexString, i::Integer) = Int(i)+1 -nextind(s::AbstractArray , i::Integer) = Int(i)+1 """ prevind(str::AbstractString, i::Integer) @@ -277,16 +269,7 @@ julia> chr2ind(str, 2) """ function ind2chr(s::AbstractString, i::Integer) s[i] # throws error if invalid - j = 1 - k = start(s) - while true - c, l = next(s,k) - if i <= k - return j - end - j += 1 - k = l - end + unsafe_ind2chr(s, i) end """ @@ -309,22 +292,29 @@ julia> ind2chr(str, 3) """ function chr2ind(s::AbstractString, i::Integer) i < start(s) && throw(BoundsError(s, i)) + k = unsafe_chr2ind(s, i) + s[k] # throws error if invalid + k +end + +function map_chr_ind(s::AbstractString, i::Integer, stop, ret) j = 1 k = start(s) while true - c, l = next(s,k) - if i == j - return k - end + i == stop((j, k)) && return ret((j, k)) # k could point after the last character + _, k = next(s, k) j += 1 - k = l end end +unsafe_ind2chr(s::AbstractString, i::Integer) = map_chr_ind(s, i, last, first) +unsafe_chr2ind(s::AbstractString, i::Integer) = map_chr_ind(s, i, first, last) + + struct EachStringIndex{T<:AbstractString} s::T end -eachindex(s::AbstractString) = EachStringIndex(s) +keys(s::AbstractString) = EachStringIndex(s) length(e::EachStringIndex) = length(e.s) start(e::EachStringIndex) = start(e.s) diff --git a/base/strings/string.jl b/base/strings/string.jl index 6a9ebb3880e39..d81fe940fc8aa 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -42,9 +42,17 @@ end _string_n(n::Integer) = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), n) -convert(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) -convert(::Type{String}, s::String) = s -convert(::Type{String}, v::Vector{UInt8}) = String(v) +""" + String(s::AbstractString) + +Convert a string to a contiguous byte array representation encoded as UTF-8 bytes. +This representation is often appropriate for passing strings to C. +""" +String(s::AbstractString) = print_to_string(s) + +String(s::Symbol) = unsafe_string(Cstring(s)) + +(::Type{Vector{UInt8}})(s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) ## low-level functions ## @@ -65,7 +73,10 @@ codeunit(s::AbstractString, i::Integer) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - unsafe_load(pointer(s),i) + ptr = pointer(s, i) + r = unsafe_load(ptr) + Core.gcuse(s) + r end write(io::IO, s::String) = unsafe_write(io, pointer(s), reinterpret(UInt, sizeof(s))) @@ -152,35 +163,33 @@ const utf8_trailing = [ ## required core functionality ## function endof(s::String) - p = pointer(s) i = sizeof(s) - while i > 0 && is_valid_continuation(unsafe_load(p,i)) + @inbounds while i > 0 && is_valid_continuation(codeunit(s, i)) i -= 1 end i end function length(s::String) - p = pointer(s) cnum = 0 - for i = 1:sizeof(s) - cnum += !is_valid_continuation(unsafe_load(p,i)) + @inbounds for i = 1:sizeof(s) + cnum += !is_valid_continuation(codeunit(s, i)) end cnum end -@noinline function slow_utf8_next(p::Ptr{UInt8}, b::UInt8, i::Int, l::Int) - if is_valid_continuation(b) - throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, unsafe_load(p,i))) +@noinline function slow_utf8_next(s::String, b::UInt8, i::Int, l::Int) + @inbounds if is_valid_continuation(b) + throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, codeunit(s, i))) end trailing = utf8_trailing[b + 1] if l < i + trailing return '\ufffd', i+1 end c::UInt32 = 0 - for j = 1:(trailing + 1) + @inbounds for j = 1:(trailing + 1) c <<= 6 - c += unsafe_load(p,i) + c += codeunit(s, i) i += 1 end c -= utf8_offset[trailing + 1] @@ -198,12 +207,11 @@ done(s::String, state) = state > sizeof(s) @boundscheck if (i < 1) | (i > sizeof(s)) throw(BoundsError(s,i)) end - p = pointer(s) - b = unsafe_load(p, i) + @inbounds b = codeunit(s, i) if b < 0x80 return Char(b), i + 1 end - return slow_utf8_next(p, b, i, sizeof(s)) + return slow_utf8_next(s, b, i, sizeof(s)) end function first_utf8_byte(ch::Char) @@ -217,8 +225,7 @@ end function reverseind(s::String, i::Integer) j = sizeof(s) + 1 - i - p = pointer(s) - while is_valid_continuation(unsafe_load(p,j)) + @inbounds while is_valid_continuation(codeunit(s, j)) j -= 1 end return j @@ -227,7 +234,7 @@ end ## overload methods for efficiency ## isvalid(s::String, i::Integer) = - (1 <= i <= sizeof(s)) && !is_valid_continuation(unsafe_load(pointer(s),i)) + (1 <= i <= sizeof(s)) && ((@inbounds b = codeunit(s, i)); !is_valid_continuation(b)) function getindex(s::String, r::UnitRange{Int}) isempty(r) && return "" @@ -252,7 +259,7 @@ function search(s::String, c::Char, i::Integer = 1) i == sizeof(s) + 1 && return 0 throw(BoundsError(s, i)) end - if is_valid_continuation(codeunit(s,i)) + @inbounds if is_valid_continuation(codeunit(s,i)) throw(UnicodeError(UTF_ERR_INVALID_INDEX, i, codeunit(s,i))) end c < Char(0x80) && return search(s, c%UInt8, i) @@ -394,7 +401,7 @@ function string(a::Union{String,Char}...) end function reverse(s::String) - dat = convert(Vector{UInt8},s) + dat = Vector{UInt8}(s) n = length(dat) n <= 1 && return s buf = StringVector(n) @@ -430,7 +437,7 @@ function repeat(s::String, r::Integer) n = sizeof(s) out = _string_n(n*r) if n == 1 # common case: repeating a single ASCII char - ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, unsafe_load(pointer(s)), r) + @inbounds ccall(:memset, Ptr{Void}, (Ptr{UInt8}, Cint, Csize_t), out, codeunit(s, 1), r) else for i=1:r unsafe_copy!(pointer(out, 1+(i-1)*n), pointer(s), n) diff --git a/base/strings/strings.jl b/base/strings/strings.jl index bc1f302b7a6ad..0acad00f79ca7 100644 --- a/base/strings/strings.jl +++ b/base/strings/strings.jl @@ -7,4 +7,4 @@ include("strings/search.jl") include("strings/util.jl") include("strings/io.jl") include("strings/utf8proc.jl") -importall .UTF8proc +using .UTF8proc diff --git a/base/strings/types.jl b/base/strings/types.jl index 3fe9b0ca0fb78..4df69ea67f3a1 100644 --- a/base/strings/types.jl +++ b/base/strings/types.jl @@ -30,6 +30,10 @@ SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j SubString(s::SubString, i::Int, j::Int) = SubString(s.string, s.offset+i, s.offset+j) SubString(s::AbstractString, i::Integer, j::Integer) = SubString(s, Int(i), Int(j)) SubString(s::AbstractString, i::Integer) = SubString(s, i, endof(s)) +SubString{T}(s::T) where {T<:AbstractString} = SubString(s, 1, endof(s)) + +String(p::SubString{String}) = + unsafe_string(pointer(p.string, p.offset+1), nextind(p, p.endof)-1) sizeof(s::SubString{String}) = s.endof == 0 ? 0 : nextind(s, s.endof) - 1 @@ -73,11 +77,6 @@ chr2ind(s::SubString{<:DirectIndexString}, i::Integer) = begin checkbounds(s,i); nextind(s::SubString, i::Integer) = nextind(s.string, i+s.offset)-s.offset prevind(s::SubString, i::Integer) = prevind(s.string, i+s.offset)-s.offset -convert(::Type{SubString{T}}, s::T) where {T<:AbstractString} = SubString(s, 1, endof(s)) - -String(p::SubString{String}) = - unsafe_string(pointer(p.string, p.offset+1), nextind(p, p.endof)-1) - function getindex(s::AbstractString, r::UnitRange{Int}) checkbounds(s, r) || throw(BoundsError(s, r)) SubString(s, first(r), last(r)) diff --git a/base/strings/utf8proc.jl b/base/strings/utf8proc.jl index a3ebd46ca0414..3c82902a1c01b 100644 --- a/base/strings/utf8proc.jl +++ b/base/strings/utf8proc.jl @@ -13,6 +13,24 @@ export normalize_string, graphemes, is_assigned_char, charwidth, isvalid, iscntrl, ispunct, isspace, isprint, isgraph # whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff + +""" + isvalid(value) -> Bool + +Returns `true` if the given value is valid for its type, which currently can be either +`Char` or `String`. +""" +isvalid(value) + +""" + isvalid(T, value) -> Bool + +Returns `true` if the given value is valid for that type. Types currently can +be either `Char` or `String`. Values for `Char` can be of type `Char` or [`UInt32`](@ref). +Values for `String` can be of that type, or `Vector{UInt8}`. +""" +isvalid(T,value) + isvalid(::Type{Char}, ch::Unsigned) = !((ch - 0xd800 < 0x800) | (ch > 0x10ffff)) isvalid(::Type{Char}, ch::Integer) = isvalid(Char, Unsigned(ch)) isvalid(::Type{Char}, ch::Char) = isvalid(Char, UInt32(ch)) @@ -194,6 +212,15 @@ end charwidth(c) Gives the number of columns needed to print a character. + +# Examples +```jldoctest +julia> charwidth('α') +1 + +julia> charwidth('❤') +2 +``` """ charwidth(c::Char) = Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), c)) @@ -225,6 +252,18 @@ is_assigned_char(c) = category_code(c) != UTF8PROC_CATEGORY_CN Tests whether a character is a lowercase letter. A character is classified as lowercase if it belongs to Unicode category Ll, Letter: Lowercase. + +# Examples +```jldoctest +julia> islower('α') +true + +julia> islower('Γ') +false + +julia> islower('❤') +false +``` """ islower(c::Char) = (category_code(c) == UTF8PROC_CATEGORY_LL) @@ -236,6 +275,18 @@ islower(c::Char) = (category_code(c) == UTF8PROC_CATEGORY_LL) Tests whether a character is an uppercase letter. A character is classified as uppercase if it belongs to Unicode category Lu, Letter: Uppercase, or Lt, Letter: Titlecase. + +# Examples +```jldoctest +julia> isupper('γ') +false + +julia> isupper('Γ') +true + +julia> isupper('❤') +false +``` """ function isupper(c::Char) ccode = category_code(c) @@ -246,6 +297,18 @@ end isdigit(c::Char) -> Bool Tests whether a character is a numeric digit (0-9). + +# Examples +```jldoctest +julia> isdigit('❤') +false + +julia> isdigit('9') +true + +julia> isdigit('α') +false +``` """ isdigit(c::Char) = ('0' <= c <= '9') @@ -255,6 +318,18 @@ isdigit(c::Char) = ('0' <= c <= '9') Tests whether a character is alphabetic. A character is classified as alphabetic if it belongs to the Unicode general category Letter, i.e. a character whose category code begins with 'L'. + +# Examples +```jldoctest +julia> isalpha('❤') +false + +julia> isalpha('α') +true + +julia> isalpha('9') +false +``` """ isalpha(c::Char) = (UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_LO) @@ -264,6 +339,18 @@ isalpha(c::Char) = (UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGO 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'. + +# Examples +```jldoctest +julia> isnumber('9') +true + +julia> isnumber('α') +false + +julia> isnumber('❤') +false +``` """ isnumber(c::Char) = (UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGORY_NO) @@ -273,6 +360,18 @@ isnumber(c::Char) = (UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGO Tests whether a character is alphanumeric. A character is classified as alphabetic if it belongs to the Unicode general category Letter or Number, i.e. a character whose category code begins with 'L' or 'N'. + +# Examples +```jldoctest +julia> isalnum('❤') +false + +julia> isalnum('9') +true + +julia> isalnum('α') +true +``` """ function isalnum(c::Char) ccode = category_code(c) @@ -295,6 +394,18 @@ iscntrl(c::Char) = (c <= Char(0x1f) || Char(0x7f) <= c <= Char(0x9f)) 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::Char) = (UTF8PROC_CATEGORY_PC <= category_code(c) <= UTF8PROC_CATEGORY_PO) @@ -389,8 +500,6 @@ end hash(g::GraphemeIterator, h::UInt) = hash(g.s, h) isless(g1::GraphemeIterator, g2::GraphemeIterator) = isless(g1.s, g2.s) -convert(::Type{S}, g::GraphemeIterator) where {S<:AbstractString} = convert(S, g.s) - show(io::IO, g::GraphemeIterator{S}) where {S} = print(io, "length-$(length(g)) GraphemeIterator{$S} for \"$(g.s)\"") ############################################################################ diff --git a/base/strings/util.jl b/base/strings/util.jl index f7297ae6810fc..665fae4c6398d 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -415,6 +415,18 @@ is a function, each occurrence is replaced with `r(s)` where `s` is the matched If `pat` is a regular expression and `r` is a `SubstitutionString`, 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` (`""`). + +# 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", 1) +"The slow foxes run quickly." + +julia> replace("The quick foxes run quickly.", "quick", "", 1) +"The foxes run quickly." +``` """ replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int)) # TODO: change this to the following when `replace` is removed from deprecated.jl: @@ -425,50 +437,76 @@ replace(s::AbstractString, pat, f) = replace_new(String(s), pat, f, typemax(Int) # hex <-> bytes conversion """ - hex2bytes(s::AbstractString) + hex2bytes(s::Union{AbstractString,AbstractVector{UInt8}}) -Convert an arbitrarily long hexadecimal string to its binary representation. Returns an -`Array{UInt8,1}`, i.e. an array of bytes. +Given a string or array `s` 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 `s` gives the value of one byte in the return vector. + +The length of `s` must be even, and the returned array has half of the length of `s`. +See also [`hex2bytes!`](@ref) for an in-place version, and [`bytes2hex`](@ref) for the inverse. # Examples ```jldoctest -julia> a = hex(12345) +julia> s = hex(12345) "3039" -julia> hex2bytes(a) +julia> hex2bytes(s) 2-element Array{UInt8,1}: 0x30 0x39 + +julia> a = b"01abEF" +6-element Array{UInt8,1}: + 0x30 + 0x31 + 0x61 + 0x62 + 0x45 + 0x46 + +julia> hex2bytes(a) +3-element Array{UInt8,1}: + 0x01 + 0xab + 0xef ``` """ -function hex2bytes(s::AbstractString) - a = zeros(UInt8, div(endof(s), 2)) - i, j = start(s), 0 - while !done(s, i) - c, i = next(s, i) - n = '0' <= c <= '9' ? c - '0' : - 'a' <= c <= 'f' ? c - 'a' + 10 : - 'A' <= c <= 'F' ? c - 'A' + 10 : - throw(ArgumentError("not a hexadecimal string: $(repr(s))")) - done(s, i) && - throw(ArgumentError("string length must be even: length($(repr(s))) == $(length(s))")) - c, i = next(s, i) - n = '0' <= c <= '9' ? n << 4 + c - '0' : - 'a' <= c <= 'f' ? n << 4 + c - 'a' + 10 : - 'A' <= c <= 'F' ? n << 4 + c - 'A' + 10 : - throw(ArgumentError("not a hexadecimal string: $(repr(s))")) - a[j += 1] = n +function hex2bytes end + +hex2bytes(s::AbstractString) = hex2bytes(Vector{UInt8}(String(s))) +hex2bytes(s::AbstractVector{UInt8}) = hex2bytes!(Vector{UInt8}(length(s) >> 1), s) + +""" + hex2bytes!(d::AbstractVector{UInt8}, s::AbstractVector{UInt8}) + +Convert an array `s` of bytes representing a hexadecimal string to its binary +representation, similar to [`hex2bytes`](@ref) except that the output is written in-place +in `d`. The length of `s` must be exactly twice the length of `d`. +""" +function hex2bytes!(d::AbstractVector{UInt8}, s::AbstractVector{UInt8}) + if 2length(d) != length(s) + isodd(length(s)) && throw(ArgumentError("input hex array must have even length")) + throw(ArgumentError("output array must be half length of input array")) + end + j = first(eachindex(d)) - 1 + for i = first(eachindex(s)):2:endof(s) + @inbounds d[j += 1] = number_from_hex(s[i]) << 4 + number_from_hex(s[i+1]) end - resize!(a, j) - return a + return d end +@inline number_from_hex(c) = + (UInt8('0') <= c <= UInt8('9')) ? c - UInt8('0') : + (UInt8('A') <= c <= UInt8('F')) ? c - (UInt8('A') - 0x0a) : + (UInt8('a') <= c <= UInt8('f')) ? c - (UInt8('a') - 0x0a) : + throw(ArgumentError("byte is not an ASCII hexadecimal digit")) + """ bytes2hex(bin_arr::Array{UInt8, 1}) -> String Convert an array of bytes to its hexadecimal representation. All characters are in lower-case. - # Examples ```jldoctest julia> a = hex(12345) diff --git a/base/subarray.jl b/base/subarray.jl index ad591d6ad5e9c..5db883f964c06 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -57,6 +57,30 @@ size(V::SubArray) = (@_inline_meta; map(n->Int(unsafe_length(n)), indices(V))) similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) +""" + parent(A) + +Returns the "parent array" of an array view type (e.g., `SubArray`), or the array itself if +it is not a view. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> s_a = Symmetric(a) +2×2 Symmetric{Int64,Array{Int64,2}}: + 1 2 + 2 4 + +julia> parent(s_a) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" parent(V::SubArray) = V.parent parentindexes(V::SubArray) = V.indexes @@ -346,7 +370,7 @@ end replace_ref_end!(ex) Recursively replace occurrences of the symbol :end in a "ref" expression (i.e. A[...]) `ex` -with the appropriate function calls (`endof`, `size` or `trailingsize`). Replacement uses +with the appropriate function calls (`endof` or `size`). Replacement uses the closest enclosing ref, so A[B[end]] @@ -378,7 +402,7 @@ function replace_ref_end_!(ex, withex) else n = 1 J = endof(ex.args) - for j = 2:J-1 + for j = 2:J exj, used = replace_ref_end_!(ex.args[j],:($size($S,$n))) used_S |= used ex.args[j] = exj @@ -394,13 +418,11 @@ function replace_ref_end_!(ex, withex) n += 1 end end - ex.args[J], used = replace_ref_end_!(ex.args[J],:($trailingsize($S,$n))) - used_S |= used end if used_S && S !== ex.args[1] S0 = ex.args[1] ex.args[1] = S - ex = Expr(:let, ex, :($S = $S0)) + ex = Expr(:let, :($S = $S0), ex) end else # recursive search @@ -449,8 +471,8 @@ macro view(ex) if Meta.isexpr(ex, :ref) ex = Expr(:call, view, ex.args...) else # ex replaced by let ...; foo[...]; end - assert(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[1], :ref)) - ex.args[1] = Expr(:call, view, ex.args[1].args...) + assert(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[2], :ref)) + ex.args[2] = Expr(:call, view, ex.args[2].args...) end Expr(:&&, true, esc(ex)) else @@ -510,12 +532,13 @@ function _views(ex::Expr) end Expr(:let, + Expr(:block, + :($a = $(_views(lhs.args[1]))), + [:($(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...))), - _views.(ex.args[2:end])...)), - :($a = $(_views(lhs.args[1]))), - [:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...) + _views.(ex.args[2:end])...))) else Expr(ex.head, _views.(ex.args)...) end diff --git a/base/sysimg.jl b/base/sysimg.jl index a30ec87bf7f5e..6c6a3213ba60c 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -51,7 +51,7 @@ convert(::Type{T}, arg) where {T<:VecElement} = T(arg) convert(::Type{T}, arg::T) where {T<:VecElement} = arg # init core docsystem -import Core: @doc, @__doc__, @doc_str +import Core: @doc, @__doc__, @doc_str, WrappedException if isdefined(Core, :Inference) import Core.Inference.CoreDocs Core.atdoc!(CoreDocs.docm) @@ -72,7 +72,8 @@ end ## Load essential files and libraries include("essentials.jl") include("ctypes.jl") -include("base.jl") +include("gcutils.jl") +include("nullabletype.jl") include("generator.jl") include("reflection.jl") include("options.jl") @@ -83,7 +84,6 @@ include("tuple.jl") include("pair.jl") include("traits.jl") include("range.jl") -include("twiceprecision.jl") include("expr.jl") include("error.jl") @@ -95,7 +95,7 @@ include("operators.jl") include("pointer.jl") include("refpointer.jl") include("checked.jl") -importall .Checked +using .Checked # buggy handling of ispure in type-inference means this should be # after re-defining the basic operations that they might try to call @@ -132,8 +132,9 @@ Matrix(m::Integer, n::Integer) = Matrix{Any}(Int(m), Int(n)) # numeric operations include("hashing.jl") include("rounding.jl") -importall .Rounding +using .Rounding include("float.jl") +include("twiceprecision.jl") include("complex.jl") include("rational.jl") include("multinverses.jl") @@ -152,7 +153,7 @@ include("strings/string.jl") # SIMD loops include("simdloop.jl") -importall .SimdLoop +using .SimdLoop # map-reduce operators include("reduce.jl") @@ -162,6 +163,12 @@ include("reshapedarray.jl") include("bitarray.jl") include("intset.jl") include("associative.jl") + +if !isdefined(Core, :Inference) + include("docs/core.jl") + Core.atdoc!(CoreDocs.docm) +end + include("dict.jl") include("set.jl") include("iterators.jl") @@ -188,11 +195,6 @@ include(string((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include("osutils.jl") include("c.jl") -if !isdefined(Core, :Inference) - include("docs/core.jl") - Core.atdoc!(CoreDocs.docm) -end - # Core I/O include("io.jl") include("iostream.jl") @@ -217,7 +219,7 @@ using .PermutedDimsArrays include("nullable.jl") include("broadcast.jl") -importall .Broadcast +using .Broadcast # define the real ntuple functions @generated function ntuple(f::F, ::Val{N}) where {F,N} @@ -240,7 +242,7 @@ end # base64 conversions (need broadcast) include("base64.jl") -importall .Base64 +using .Base64 # version include("version.jl") @@ -265,10 +267,10 @@ include("weakkeydict.jl") include("stream.jl") include("socket.jl") include("filesystem.jl") -importall .Filesystem +using .Filesystem include("process.jl") include("multimedia.jl") -importall .Multimedia +using .Multimedia include("grisu/grisu.jl") import .Grisu.print_shortest include("methodshow.jl") @@ -276,7 +278,8 @@ include("methodshow.jl") # core math functions include("floatfuncs.jl") include("math.jl") -importall .Math +using .Math +import .Math: gamma const (√)=sqrt const (∛)=cbrt @@ -287,21 +290,21 @@ include("reducedim.jl") # macros in this file relies on string.jl # basic data structures include("ordering.jl") -importall .Order +using .Order # Combinatorics include("sort.jl") -importall .Sort +using .Sort # Fast math include("fastmath.jl") -importall .FastMath +using .FastMath function deepcopy_internal end # BigInts and BigFloats include("gmp.jl") -importall .GMP +using .GMP for T in [Signed, Integer, BigInt, Float32, Float64, Real, Complex, Rational] @eval flipsign(x::$T, ::Unsigned) = +x @@ -309,7 +312,7 @@ for T in [Signed, Integer, BigInt, Float32, Float64, Real, Complex, Rational] end include("mpfr.jl") -importall .MPFR +using .MPFR big(n::Integer) = convert(BigInt,n) big(x::AbstractFloat) = convert(BigFloat,x) big(q::Rational) = big(numerator(q))//big(denominator(q)) @@ -321,26 +324,30 @@ include("hashing2.jl") # irrational mathematical constants include("irrationals.jl") +include("mathconstants.jl") +using .MathConstants: ℯ, π, pi # random number generation -include("dSFMT.jl") -include("random.jl") -importall .Random +include("random/dSFMT.jl") +include("random/random.jl") +using .Random +import .Random: rand, rand! # (s)printf macros include("printf.jl") -importall .Printf +using .Printf # metaprogramming include("meta.jl") # enums include("Enums.jl") -importall .Enums +using .Enums # concurrency and parallelism include("serialize.jl") -importall .Serializer +using .Serializer +import .Serializer: serialize, deserialize include("channels.jl") # memory-mapped and shared arrays @@ -349,7 +356,7 @@ import .Mmap # utilities - timing, help, edit include("datafmt.jl") -importall .DataFmt +using .DataFmt include("deepcopy.jl") include("interactiveutil.jl") include("summarysize.jl") @@ -368,14 +375,14 @@ include("client.jl") # Stack frames and traces include("stacktraces.jl") -importall .StackTraces +using .StackTraces # misc useful functions & macros include("util.jl") # dense linear algebra include("linalg/linalg.jl") -importall .LinAlg +using .LinAlg const ⋅ = dot const × = cross @@ -390,7 +397,7 @@ include("pkg/pkg.jl") # profiler include("profile.jl") -importall .Profile +using .Profile # dates include("dates/Dates.jl") @@ -398,12 +405,12 @@ import .Dates: Date, DateTime, DateFormat, @dateformat_str, now # sparse matrices, vectors, and sparse linear algebra include("sparse/sparse.jl") -importall .SparseArrays +using .SparseArrays include("asyncmap.jl") include("distributed/Distributed.jl") -importall .Distributed +using .Distributed include("sharedarray.jl") # code loading @@ -416,7 +423,6 @@ include("threadcall.jl") include("deprecated.jl") # Some basic documentation -include("docs/helpdb.jl") include("docs/basedocs.jl") # Documentation -- should always be included last in sysimg. diff --git a/base/test.jl b/base/test.jl index 9faad132c9902..f118d105cebbc 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1140,7 +1140,7 @@ Body: julia> @inferred f(1,2,3) ERROR: return type Int64 does not match inferred return type Union{Float64, Int64} Stacktrace: - [1] error(::String) at ./error.jl:21 + [1] error(::String) at ./error.jl:33 julia> @inferred max(1,2) 2 @@ -1366,7 +1366,6 @@ with string types besides the standard `String` type. struct GenericString <: AbstractString string::AbstractString end -Base.convert(::Type{GenericString}, s::AbstractString) = GenericString(s) Base.endof(s::GenericString) = endof(s.string) Base.next(s::GenericString, i::Int) = next(s.string, i) @@ -1416,11 +1415,13 @@ end GenericArray{T}(args...) where {T} = GenericArray(Array{T}(args...)) GenericArray{T,N}(args...) where {T,N} = GenericArray(Array{T,N}(args...)) -Base.eachindex(a::GenericArray) = eachindex(a.a) +Base.keys(a::GenericArray) = keys(a.a) Base.indices(a::GenericArray) = indices(a.a) Base.length(a::GenericArray) = length(a.a) Base.size(a::GenericArray) = size(a.a) Base.getindex(a::GenericArray, i...) = a.a[i...] Base.setindex!(a::GenericArray, x, i...) = a.a[i...] = x +Base.similar(A::GenericArray, s::Integer...) = GenericArray(similar(A.a, s...)) + end # module diff --git a/base/tuple.jl b/base/tuple.jl index bab87697249e6..1ffc85f9c47c2 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -17,11 +17,11 @@ NTuple length(t::Tuple) = nfields(t) endof(t::Tuple) = length(t) -size(t::Tuple, d) = d==1 ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) -getindex(t::Tuple, i::Int) = getfield(t, i) -getindex(t::Tuple, i::Real) = getfield(t, convert(Int, i)) +size(t::Tuple, d) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) +@eval getindex(t::Tuple, i::Int) = getfield(t, i, $(Expr(:boundscheck))) +@eval getindex(t::Tuple, i::Real) = getfield(t, convert(Int, i), $(Expr(:boundscheck))) getindex(t::Tuple, r::AbstractArray{<:Any,1}) = ([t[ri] for ri in r]...) -getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t,find(b)) : throw(BoundsError(t, b)) +getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, find(b)) : throw(BoundsError(t, b)) # returns new tuple; N.B.: becomes no-op if i is out-of-bounds setindex(x::Tuple, v, i::Integer) = (@_inline_meta; _setindex(v, i, x...)) @@ -38,9 +38,12 @@ start(t::Tuple) = 1 done(t::Tuple, i::Int) = (length(t) < i) next(t::Tuple, i::Int) = (t[i], i+1) -eachindex(t::Tuple) = 1:length(t) +keys(t::Tuple) = 1:length(t) -function eachindex(t::Tuple, t2::Tuple...) +prevind(t::Tuple, i::Integer) = Int(i)-1 +nextind(t::Tuple, i::Integer) = Int(i)+1 + +function keys(t::Tuple, t2::Tuple...) @_inline_meta 1:_maxlength(t, t2...) end diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 6607c12e1d1ce..a4e8f055377e5 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -6,6 +6,157 @@ # 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 +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 +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 = x * y + ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h))) +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 +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)) @@ -16,7 +167,7 @@ Float64`. `hi` represents the high bits (most significant bits) and `num//denom` can be approximated conveniently using the syntax `TwicePrecision{T}((num, denom))`. -When used with `T<:AbstractFloat` to construct an exact +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 @@ -35,30 +186,52 @@ struct TwicePrecision{T} lo::T # least significant bits end -function TwicePrecision{T}(nd::Tuple{I,I}) where {T,I} +TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) + +function TwicePrecision{T}(x) where {T} + xT = convert(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, zero(T)) / d + TwicePrecision{T}(n/d) +end + +function TwicePrecision{T}(nd::Tuple{Any,Any}) where {T} + n, d = nd + TwicePrecision{T}(n) / d end function TwicePrecision{T}(nd::Tuple{I,I}, nb::Integer) where {T,I} twiceprecision(TwicePrecision{T}(nd), nb) end -function twiceprecision(val::T, nb::Integer) where T<:Number +# 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<:Number +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{Float64}, len, offset) = min(26, nbitslen(len, offset)) -nbitslen(::Type{Float32}, len, offset) = min(12, nbitslen(len, offset)) -nbitslen(::Type{Float16}, len, offset) = min(5, nbitslen(len, offset)) -nbitslen(len, offset) = len < 2 ? 0 : ceil(Int, log2(max(offset-1, len-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 : ceil(Int, log2(max(offset-1, len-offset))) + 1 eltype(::Type{TwicePrecision{T}}) where {T} = T @@ -83,25 +256,109 @@ big(x::TwicePrecision) = big(x.hi) + big(x.lo) zero(::Type{TwicePrecision{T}}) where {T} = TwicePrecision{T}(0, 0) +# 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{typeof(x.hi*v)}(v) +end +function *(x::TwicePrecision{<:IEEEFloat}, v::Integer) + v == 0 && return TwicePrecision(x.hi*v, x.lo*v) + nb = ceil(Int, log2(abs(v))) + 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{typeof(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 -# If using TwicePrecision numbers, deliberately force user to specify offset -StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, len::Integer, offset::Integer) where {T} = +# Use TwicePrecision only for Float64; use Float64 for T<:Union{Float16,Float32} +# 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), Int(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], Int(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), Int(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), Int(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 if len < 2 - return StepRangeLen(TwicePrecision{T}((start_n, den)), - TwicePrecision{T}((step_n, den)), Int(len), 1) + return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) end # index of smallest-magnitude value imin = clamp(round(Int, -start_n/step_n+1), 1, Int(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(TwicePrecision{T}((ref_n, den)), - TwicePrecision{T}((step_n, den), nb), Int(len), imin) + steprangelen_hp(T, (ref_n, den), (step_n, den), nb, Int(len), imin) end function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::AbstractFloat) @@ -116,8 +373,7 @@ function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::Abs end # Fallback (misses the opportunity to set offset different from 1, # but otherwise this is still high-precision) - StepRangeLen(TwicePrecision{T}((a,divisor)), - TwicePrecision{T}((st,divisor), nbitslen(T, len, 1)), Int(len), 1) + steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), Int(len), 1) end function colon(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} @@ -158,7 +414,7 @@ function colon(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float6 # if we've overshot the end, subtract one: len -= (start < stop < stop′) + (start > stop > stop′) end - StepRangeLen(TwicePrecision(start, zero(T)), twiceprecision(step, nbitslen(T, len, 1)), len) + steprangelen_hp(T, start, step, 0, len, 1) end function range(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64} @@ -175,38 +431,28 @@ function range(a::T, st::T, len::Integer) where T<:Union{Float16,Float32,Float64 return floatrange(T, start_n, step_n, len, den) end end - StepRangeLen(TwicePrecision(a, zero(T)), TwicePrecision(st, zero(T)), len) -end - -step(r::StepRangeLen{T,R,S}) where {T,R,S<:TwicePrecision} = convert(eltype(S), r.step) - -start(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}) = 1 -done(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Int) = length(r) < i -function next(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Int) - @_inline_meta - unsafe_getindex(r, i), i+1 + steprangelen_hp(T, a, st, 0, len, 1) 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 add2 + # Very similar to _getindex_hiprec, but optimized to avoid a 2nd call to add12 @_inline_meta u = i - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo - x_hi, x_lo = add2(r.ref.hi, shift_hi) + 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) u = i - r.offset shift_hi, shift_lo = u*r.step.hi, u*r.step.lo - x_hi, x_lo = add2(r.ref.hi, shift_hi) - x_hi, x_lo = add2(x_hi, x_lo + (shift_lo + r.ref.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{<:Integer}) where T - @_inline_meta @boundscheck checkbounds(r, s) soffset = 1 + round(Int, (r.offset - first(s))/step(s)) soffset = clamp(soffset, 1, length(s)) @@ -234,11 +480,15 @@ convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{T,R,S}) where {T<:AbstractF convert(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = _convertSRL(StepRangeLen{T,R,S}, r) -convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:Union{Float16,Float32,Float64}} = - _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) +convert(::Type{StepRangeLen{Float64}}, r::StepRangeLen) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) +convert(::Type{StepRangeLen{T}}, r::StepRangeLen) where {T<:IEEEFloat} = + _convertSRL(StepRangeLen{T,Float64,Float64}, r) -convert(::Type{StepRangeLen{T}}, r::Range) where {T<:Union{Float16,Float32,Float64}} = - _convertSRL(StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}, r) +convert(::Type{StepRangeLen{Float64}}, r::Range) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) +convert(::Type{StepRangeLen{T}}, r::Range) where {T<:IEEEFloat} = + _convertSRL(StepRangeLen{T,Float64,Float64}, r) function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) @@ -285,12 +535,12 @@ function sum(r::StepRangeLen) sp, sn = sumpair(np), sumpair(nn) tp = _prod(r.step, sp[1], sp[2]) tn = _prod(r.step, sn[1], sn[2]) - s_hi, s_lo = add2(tp.hi, -tn.hi) + 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 = add2(s_hi, ref.hi) - add2(sm_hi, sm_lo + ref.lo)[1] + 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 @@ -316,10 +566,10 @@ end ## LinSpace # For Float16, Float32, and Float64, linspace returns a StepRangeLen -function linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float32,Float64} +function linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} len < 2 && return _linspace1(T, start, stop, len) if start == stop - return StepRangeLen(TwicePrecision(start,zero(T)), TwicePrecision(zero(T),zero(T)), len) + return steprangelen_hp(T, start, zero(T), 0, len, 1) end # Attempt to find exact rational approximations start_n, start_d = rat(start) @@ -338,15 +588,15 @@ function linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float3 _linspace(start, stop, len) end -function _linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float32,Float64} +function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} (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, Int(len) end - tmin = -(start/Δ)/Δfac # interpolation t such that return value is 0 - imin = round(Int, tmin*(len-1)+1) + tmin = -(start/Δ)/Δfac # t such that (1-t)*start + t*stop == 0 + imin = round(Int, tmin*(len-1)+1) # index approximately corresponding to t if 1 < imin < len # The smallest-magnitude element is in the interior t = (imin-1)/(len-1) @@ -364,8 +614,7 @@ function _linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float if len == 2 && !isfinite(step) # For very large endpoints where step overflows, exploit the # split-representation to handle the overflow - return StepRangeLen(TwicePrecision(start, zero(T)), - TwicePrecision(-start, stop), 2) + return steprangelen_hp(T, start, (-start, stop), 0, 2, 1) end # 2x calculations to get high precision endpoint matching while also # preventing overflow in ref_hi+(i-offset)*step_hi @@ -373,33 +622,27 @@ function _linspace(start::T, stop::T, len::Integer) where T<:Union{Float16,Float 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 = add2((1-imin)*step_hi, ref) - x2_hi, x2_lo = add2((len-imin)*step_hi, ref) + 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(TwicePrecision(ref, ref_lo), TwicePrecision(step_hi, step_lo), Int(len), imin) + steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, Int(len), imin) end # linspace for rational numbers, start = start_n/den, stop = stop_n/den # Note this returns a StepRangeLen function linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) - start_n == stop_n && return StepRangeLen(TwicePrecision{T}((start_n, den)), zero(TwicePrecision{T}), len) + start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len) tmin = -start_n/(Float64(stop_n) - Float64(start_n)) imin = round(Int, tmin*(len-1)+1) imin = clamp(imin, 1, Int(len)) - # Compute (1-t)*a and t*b separately in 2x precision (itp = interpolant)... - dent = (den, len-1) # represent products as a tuple to eliminate risk of overflow - start_itp = proddiv(T, (len-imin, start_n), dent) - stop_itp = proddiv(T, (imin-1, stop_n), dent) - # ...and then combine them to make ref - ref = start_itp + stop_itp - # Compute step to 2x precision without risking overflow... - rend = proddiv(T, (stop_n,), dent) - rbeg = proddiv(T, (-start_n,), dent) - step = twiceprecision(rbeg + rend, nbitslen(T, len, imin)) # ...and truncate hi-bits as needed - StepRangeLen(ref, step, Int(len), imin) + ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n + ref_denom = Int128(len-1) * den + ref = (ref_num, ref_denom) + step_full = (Int128(stop_n) - Int128(start_n), ref_denom) + steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), Int(len), imin) end # For len < 2 @@ -422,7 +665,7 @@ function rat(x) y = x a = d = 1 b = c = 0 - m = maxintfloat(narrow(typeof(x))) + m = maxintfloat(narrow(typeof(x)), Int) while abs(y) <= m f = trunc(Int,y) y -= f @@ -435,86 +678,17 @@ function rat(x) return a, b end +narrow(::Type{T}) where {T<:AbstractFloat} = Float64 narrow(::Type{Float64}) = Float32 narrow(::Type{Float32}) = Float16 narrow(::Type{Float16}) = Float16 -function add2(u::T, v::T) where T<:Number - @_inline_meta - u, v = ifelse(abs(v) > abs(u), (v, u), (u, v)) - w = u + v - w, (u-w) + v -end - -add2(u, v) = _add2(promote(u, v)...) -_add2(u::T, v::T) where {T<:Number} = add2(u, v) -_add2(u, v) = error("$u::$(typeof(u)) and $v::$(typeof(v)) cannot be promoted to a common type") - -function +(x::TwicePrecision, y::Number) - s_hi, s_lo = add2(x.hi, y) - TwicePrecision(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(r, s) -end -+(x::TwicePrecision, y::TwicePrecision) = _add2(promote(x, y)...) -_add2(x::T, y::T) where {T<:TwicePrecision} = x + y -_add2(x::TwicePrecision, y::TwicePrecision) = TwicePrecision(x.hi+y.hi, x.lo+y.lo) - -function *(x::TwicePrecision, v::Integer) - v == 0 && return TwicePrecision(x.hi*v, x.lo*v) - nb = ceil(Int, log2(abs(v))) - u = truncbits(x.hi, nb) - y_hi, y_lo = add2(u*v, ((x.hi-u) + x.lo)*v) - TwicePrecision(y_hi, y_lo) -end - -function _mul2(x::TwicePrecision{T}, v::T) where T<:Union{Float16,Float32,Float64} - v == 0 && return TwicePrecision(T(0), T(0)) - xhh, xhl = splitprec(x.hi) - vh, vl = splitprec(v) - y_hi, y_lo = add2(xhh*vh, xhh*vl + xhl*vh) - TwicePrecision(y_hi, y_lo + xhl*vl + x.lo*v) -end - -_mul2(x::TwicePrecision, v::Number) = TwicePrecision(x.hi*v, x.lo*v) - -function *(x::TwicePrecision{R}, v::S) where R where S<:Number - T = promote_type(R, S) - _mul2(convert(TwicePrecision{T}, x), convert(T, v)) -end - -*(v::Number, x::TwicePrecision) = x*v - -function /(x::TwicePrecision, v::Number) - hi = x.hi/v - w = TwicePrecision(hi, zero(hi)) * v - lo = (((x.hi - w.hi) - w.lo) + x.lo)/v - y_hi, y_lo = add2(hi, lo) - TwicePrecision(y_hi, y_lo) -end - -# hi-precision version of prod(num)/prod(den) -# num and den are tuples to avoid risk of overflow -function proddiv(T, num, den) - @_inline_meta - t = TwicePrecision(T(num[1]), zero(T)) - t = _prod(t, tail(num)...) - _divt(t, den...) -end function _prod(t::TwicePrecision, x, y...) @_inline_meta _prod(t * x, y...) end _prod(t::TwicePrecision) = t -function _divt(t::TwicePrecision, x, y...) - @_inline_meta - _divt(t / x, y...) -end -_divt(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 diff --git a/base/util.jl b/base/util.jl index 3edd92a861675..5011e1d1f7445 100644 --- a/base/util.jl +++ b/base/util.jl @@ -827,7 +827,7 @@ function crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0")) # 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 = Array{UInt8}(min(nb, 24576)) + buf = Vector{UInt8}(min(nb, 24576)) while !eof(io) && nb > 24576 n = readbytes!(io, buf) crc = unsafe_crc32c(buf, n, crc) diff --git a/base/version.jl b/base/version.jl index 05019bb0eb34f..9e18099faeae4 100644 --- a/base/version.jl +++ b/base/version.jl @@ -2,21 +2,23 @@ ## semantic version numbers (http://semver.org) +const VInt = UInt32 + struct VersionNumber - major::Int - minor::Int - patch::Int - prerelease::Tuple{Vararg{Union{Int,String}}} - build::Tuple{Vararg{Union{Int,String}}} - - function VersionNumber(major::Int, minor::Int, patch::Int, - pre::Tuple{Vararg{Union{Int,String}}}, - bld::Tuple{Vararg{Union{Int,String}}}) + major::VInt + minor::VInt + patch::VInt + prerelease::Tuple{Vararg{Union{UInt64,String}}} + build::Tuple{Vararg{Union{UInt64,String}}} + + function VersionNumber(major::VInt, minor::VInt, patch::VInt, + pre::Tuple{Vararg{Union{UInt64,String}}}, + bld::Tuple{Vararg{Union{UInt64,String}}}) 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 isa(ident,Int) + if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative pre-release identifier: $ident")) else if !ismatch(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || @@ -26,7 +28,7 @@ struct VersionNumber end end for ident in bld - if isa(ident,Int) + if ident isa Integer ident >= 0 || throw(ArgumentError("invalid negative build identifier: $ident")) else if !ismatch(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || @@ -41,9 +43,9 @@ end VersionNumber(major::Integer, minor::Integer = 0, patch::Integer = 0, pre::Tuple{Vararg{Union{Integer,AbstractString}}} = (), bld::Tuple{Vararg{Union{Integer,AbstractString}}} = ()) = - VersionNumber(Int(major), Int(minor), Int(patch), - map(x->isa(x,Integer) ? Int(x) : String(x), pre), - map(x->isa(x,Integer) ? Int(x) : String(x), bld)) + 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)) function print(io::IO, v::VersionNumber) v == typemax(VersionNumber) && return print(io, "∞") @@ -82,7 +84,7 @@ function split_idents(s::AbstractString) idents = split(s, '.') ntuple(length(idents)) do i ident = idents[i] - ismatch(r"^\d+$", ident) ? parse(Int, ident) : String(ident) + ismatch(r"^\d+$", ident) ? parse(UInt64, ident) : String(ident) end end @@ -91,9 +93,9 @@ function VersionNumber(v::AbstractString) m = match(VERSION_REGEX, v) m === nothing && throw(ArgumentError("invalid version string: $v")) major, minor, patch, minus, prerl, plus, build = m.captures - major = parse(Int, major) - minor = minor !== nothing ? parse(Int, minor) : 0 - patch = patch !== nothing ? parse(Int, patch) : 0 + major = parse(VInt, major) + 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 @@ -107,15 +109,21 @@ convert(::Type{VersionNumber}, v::AbstractString) = VersionNumber(v) macro v_str(v); VersionNumber(v); end typemin(::Type{VersionNumber}) = v"0-" -typemax(::Type{VersionNumber}) = VersionNumber(typemax(Int),typemax(Int),typemax(Int),(),("",)) -ident_cmp(a::Int, b::Int) = cmp(a,b) -ident_cmp(a::Int, b::String) = isempty(b) ? +1 : -1 -ident_cmp(a::String, b::Int) = isempty(a) ? -1 : +1 -ident_cmp(a::String, b::String) = cmp(a,b) +function typemax(::Type{VersionNumber}) + ∞ = typemax(VInt) + VersionNumber(∞, ∞, ∞, (), ("",)) +end + +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::Tuple{Vararg{Union{Int,String}}}, - B::Tuple{Vararg{Union{Int,String}}}) +function ident_cmp( + A::Tuple{Vararg{Union{Integer,String}}}, + B::Tuple{Vararg{Union{Integer,String}}}, +) i = start(A) j = start(B) while !done(A,i) && !done(B,i) @@ -132,8 +140,8 @@ 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 + (ident_cmp(a.prerelease, b.prerelease) != 0) && return false + (ident_cmp(a.build, b.build) != 0) && return false return true end @@ -186,7 +194,7 @@ function check_new_version(existing::Vector{VersionNumber}, ver::VersionNumber) end error("$ver is not a valid initial version (try 0.0.0, 0.0.1, 0.1 or 1.0)") end - idx = searchsortedlast(existing,ver) + idx = searchsortedlast(existing, ver) prv = existing[idx] ver == prv && error("version $ver already exists") nxt = thismajor(ver) != thismajor(prv) ? nextmajor(prv) : diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index 17712ebc2f9f6..1ff64f5b172b1 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -137,11 +137,4 @@ function next(t::WeakKeyDict{K,V}, i) where V where K return (kv, (i, gc_token)) end -function filter!(f, d::WeakKeyDict) - for (k, v) in d - if !f(k, v) - delete!(d, k) - end - end - return d -end +filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d) diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000000000..c520f272981ce --- /dev/null +++ b/circle.yml @@ -0,0 +1,80 @@ +version: 2 +workflows: + version: 2 + linux-builds: + jobs: + - build-i686 + - build-x86_64 + +jobs: + build-i686: + docker: + - image: circleci/python:2.7 + environment: + JULIA_CPU_CORES: 6 + JULIA_TEST_MAXRSS_MB: 800 + ARCH: i686 + steps: &steps + - run: | # install build dependencies + sudo apt-get install -y g++-4.8-multilib gfortran-4.8-multilib \ + time ccache bar && + for prog in gcc g++ gfortran; do + sudo ln -s /usr/bin/$prog-4.8 /usr/local/bin/$prog; + done + - checkout # circle ci code checkout step +# (FIXME: need to unset url."ssh://git@github.com".insteadOf or libgit2 tests fail) + - run: | # checkout merge commit, set versioning info and Make.user variables + git config --global --unset url."ssh://git@github.com".insteadOf && + if [ -n "$CIRCLE_PULL_REQUEST" ]; then + git fetch origin +refs/pull/$(basename $CIRCLE_PULL_REQUEST)/merge && + git checkout -qf FETCH_HEAD; + fi && + make -C base version_git.jl.phony && + echo "override ARCH = $ARCH" | tee -a Make.user && + for var in FORCE_ASSERTIONS LLVM_ASSERTIONS USECCACHE NO_GIT; do + echo "override $var = 1" | tee -a Make.user; + done && + echo "$ARCH $HOME $(date +%Y%W)" | tee /tmp/weeknumber + - restore_cache: # silly to take a checksum of the tag file here instead of + keys: # its contents but this is the only thing that works on circle + - ccache-{{ checksum "/tmp/weeknumber" }} + - run: | # compile julia deps + contrib/download_cmake.sh && + make -j8 -C deps || make + - run: | # build julia, output ccache stats when done + make -j8 all && + make prefix=/tmp/julia install && + ccache -s && + make build-stats + - run: | # move source tree out of the way, run tests from install tree + cd .. && + mv project julia-src && + /tmp/julia/bin/julia -e 'versioninfo()' && + /tmp/julia/bin/julia --precompiled=no -e 'true' && + /tmp/julia/bin/julia-debug --precompiled=no -e 'true' && + pushd /tmp/julia/share/julia/test && + /tmp/julia/bin/julia --check-bounds=yes runtests.jl all --skip socket | bar -i 30 && + /tmp/julia/bin/julia --check-bounds=yes runtests.jl libgit2-online download pkg && + popd && + mkdir /tmp/embedding-test && + make check -C /tmp/julia/share/doc/julia/examples/embedding \ + JULIA=/tmp/julia/bin/julia BIN=/tmp/embedding-test \ + "$(cd julia-src && make print-CC)" && + mv julia-src project +# - run: cd project && make -C doc deploy +# - run: +# command: sudo dmesg +# when: on_fail + - save_cache: + key: ccache-{{ checksum "/tmp/weeknumber" }} + paths: + - ~/.ccache + + build-x86_64: + docker: + - image: circleci/python:2.7 + environment: + JULIA_CPU_CORES: 6 + JULIA_TEST_MAXRSS_MB: 800 + ARCH: x86_64 + steps: *steps diff --git a/contrib/julia.xml b/contrib/julia.xml index 577fc37de5d62..65ebbc95ee351 100644 --- a/contrib/julia.xml +++ b/contrib/julia.xml @@ -306,8 +306,8 @@ im π pi + e - eu γ eulergamma catalan diff --git a/contrib/windows/Vagrantfile b/contrib/windows/Vagrantfile index 246bb68f4aba0..c3da1be4df6a7 100644 --- a/contrib/windows/Vagrantfile +++ b/contrib/windows/Vagrantfile @@ -13,7 +13,7 @@ mkdir -Force $cyginstall | Out-Null foreach ($pkg in @("git,make,curl,patch,python,gcc-g++,m4,cmake,p7zip", "mingw64-$arch-gcc-g++,mingw64-$arch-gcc-fortran")) { & "$cyginstall\\$setup" -q -n -R $cyginstall -l $cyginstall\\packages ` - -s http://mirrors.mit.edu/cygwin -g -P $pkg | Where-Object ` + -s http://mirrors.kernel.org/sourceware/cygwin -g -P $pkg | Where-Object ` -FilterScript {$_ -notlike "Installing file *"} | Write-Output } & "$cyginstall\\bin\\sh" -lc "if ! [ -e julia$bits ]; then git clone \\ diff --git a/contrib/windows/appveyor_build.sh b/contrib/windows/appveyor_build.sh index ee42adab148a9..927151e5d407b 100755 --- a/contrib/windows/appveyor_build.sh +++ b/contrib/windows/appveyor_build.sh @@ -204,6 +204,7 @@ fi echo 'FORCE_ASSERTIONS = 1' >> Make.user cat Make.user +make -j3 VERBOSE=1 all make -j3 VERBOSE=1 install make VERBOSE=1 -C examples cp usr/bin/busybox.exe julia-*/bin diff --git a/contrib/windows/install-cygwin.ps1 b/contrib/windows/install-cygwin.ps1 index 3e1a832537e34..81088a2999510 100644 --- a/contrib/windows/install-cygwin.ps1 +++ b/contrib/windows/install-cygwin.ps1 @@ -4,6 +4,6 @@ mkdir -Force C:\cygdownloads | Out-Null (new-object net.webclient).DownloadFile( "http://cygwin.com/$setup", "C:\cygdownloads\$setup") & "C:\cygdownloads\$setup" -q -n -R C:\cygwin-$env:ARCH ` - -l C:\cygdownloads -s http://mirrors.mit.edu/cygwin -g -I ` + -l C:\cygdownloads -s http://mirrors.kernel.org/sourceware/cygwin -g -I ` -P "make,curl,time,p7zip,mingw64-$env:ARCH-gcc-g++,mingw64-$env:ARCH-gcc-fortran" | Where-Object ` -FilterScript {$_ -notlike "Installing file *"} | Write-Output diff --git a/deps/Versions.make b/deps/Versions.make index f7bdc70bb59a3..48235e4424ca9 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -1,5 +1,5 @@ LLVM_VER = 3.9.1 -PCRE_VER = 10.23 +PCRE_VER = 10.30 DSFMT_VER = 2.2.3 LAPACK_VER = 3.5.0 ARPACK_VER = 3.3.0 @@ -9,5 +9,5 @@ OSXUNWIND_VER = 0.0.3 GMP_VER = 6.1.2 MPFR_VER = 3.1.5 PATCHELF_VER = 0.9 -MBEDTLS_VER = 2.5.1 -CURL_VER = 7.54.1 +MBEDTLS_VER = 2.6.0 +CURL_VER = 7.55.1 diff --git a/deps/checksums/curl-7.54.1.tar.bz2/md5 b/deps/checksums/curl-7.54.1.tar.bz2/md5 deleted file mode 100644 index d06698526d327..0000000000000 --- a/deps/checksums/curl-7.54.1.tar.bz2/md5 +++ /dev/null @@ -1 +0,0 @@ -6b6eb722f512e7a24855ff084f54fe55 diff --git a/deps/checksums/curl-7.54.1.tar.bz2/sha512 b/deps/checksums/curl-7.54.1.tar.bz2/sha512 deleted file mode 100644 index 5a4942693612b..0000000000000 --- a/deps/checksums/curl-7.54.1.tar.bz2/sha512 +++ /dev/null @@ -1 +0,0 @@ -eb9639677f0ca1521ca631c520ab83ad071c52b31690e5e7f31546f6a44b2f11d1bb62282056cffb570eb290bf1e7830e87cb536295ac6a54a904663e795f2da diff --git a/deps/checksums/curl-7.55.1.tar.bz2/md5 b/deps/checksums/curl-7.55.1.tar.bz2/md5 new file mode 100644 index 0000000000000..0aca15d410cc0 --- /dev/null +++ b/deps/checksums/curl-7.55.1.tar.bz2/md5 @@ -0,0 +1 @@ +8c153f282bbe482495214654cdcd4182 diff --git a/deps/checksums/curl-7.55.1.tar.bz2/sha512 b/deps/checksums/curl-7.55.1.tar.bz2/sha512 new file mode 100644 index 0000000000000..c5263aca18f9b --- /dev/null +++ b/deps/checksums/curl-7.55.1.tar.bz2/sha512 @@ -0,0 +1 @@ +bfeb39e94b8378519b2efba0a476636b80dbee3434104b98464ee81ce3871eb134e065f52abe8bedb69681b43576cb30655c8be0be6115386859d0cb426d745b diff --git a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 b/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 deleted file mode 100644 index 07d3ac158b709..0000000000000 --- a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -0e6ec181c8bfd322a17dbd843b2b8ebb diff --git a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 b/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 deleted file mode 100644 index 704a70e7378a9..0000000000000 --- a/deps/checksums/libuv-52d72a52cc7ccd570929990f010ed16e2ec604c8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4bf9c8b16617691b70ea43c667ecc575d2a06b3d0c347b949d10360c012138585a13ecea5e18de81b1585a96cf7734b7cc3d6072490898f1d5531c702cf5afab diff --git a/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 new file mode 100644 index 0000000000000..3399b978883bd --- /dev/null +++ b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/md5 @@ -0,0 +1 @@ +8e84e1a3332fd2eb63e6c9d0eaa731b1 diff --git a/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 new file mode 100644 index 0000000000000..cdaf96971b71f --- /dev/null +++ b/deps/checksums/libuv-c5a4e584989669ad48c1728b35063291e16f85ee.tar.gz/sha512 @@ -0,0 +1 @@ +53f636422d8ac2c1e738625868e4b000b0e332c1f362564ced7e740ff94019a68017ab8674f85653133a0a9c5c48db78dd043e4d2ab9b9e8451b2cb10a27ddd0 diff --git a/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 b/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 deleted file mode 100644 index 63d6cb36d9fb9..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-apache.tgz/md5 +++ /dev/null @@ -1 +0,0 @@ -1299f38583cb4f93c78c99c14a8c913f diff --git a/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 b/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 deleted file mode 100644 index ac443c5cc2d35..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-apache.tgz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8bde8f3d1d025a13c2a20fa8d2a4b9711adf0bb32d2226f1dd898dda3f3337c929b3d60e53684fbcf8094142568dd1d780353a92b94cb15c28c31c6d0542e6fc diff --git a/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 b/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 deleted file mode 100644 index f02d4fc23affe..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-gpl.tgz/md5 +++ /dev/null @@ -1 +0,0 @@ -313f637f65b5f6d74d45310482a9c84f diff --git a/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 b/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 deleted file mode 100644 index 14d290278fb5b..0000000000000 --- a/deps/checksums/mbedtls-2.5.1-gpl.tgz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fa05a5f61d9d79bc74e47badc89f68f8b6fd228537f10706cbdacc03b02455171b88121b3c1c5f56dff648c39ff88936413a9c5eea0dd0e046fb1f889e173c63 diff --git a/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 b/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 new file mode 100644 index 0000000000000..aed0379ec76f0 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-apache.tgz/md5 @@ -0,0 +1 @@ +01ede06f7d00dd8a6626494d95a63f6b diff --git a/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 b/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 new file mode 100644 index 0000000000000..86ec33c3a2630 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-apache.tgz/sha512 @@ -0,0 +1 @@ +5eb47d95a31c63e43074a115d141dedae869c43cbe62d5cf7bde11440e14fb8879ac6ed204d0d741b3501b8ba551019a5d47cbdf6673d18b61296be4463e9ffd diff --git a/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 b/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 new file mode 100644 index 0000000000000..9200bb43df37a --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-gpl.tgz/md5 @@ -0,0 +1 @@ +f03b8cf455f246e70e83662d534e156f diff --git a/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 b/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 new file mode 100644 index 0000000000000..6a65a69420c01 --- /dev/null +++ b/deps/checksums/mbedtls-2.6.0-gpl.tgz/sha512 @@ -0,0 +1 @@ +ed0765a476bf8a318fac1389b49d2b42e1a4ac5d4df749d241b24b8c8acea59c62fbb17e4b14057203f16ba7c58b8723ececf74d8a6b065a233a87696155076a diff --git a/deps/checksums/pcre2-10.23.tar.bz2/md5 b/deps/checksums/pcre2-10.23.tar.bz2/md5 deleted file mode 100644 index a562ef4a269d3..0000000000000 --- a/deps/checksums/pcre2-10.23.tar.bz2/md5 +++ /dev/null @@ -1 +0,0 @@ -b2cd00ca7e24049040099b0a46bb3649 diff --git a/deps/checksums/pcre2-10.23.tar.bz2/sha512 b/deps/checksums/pcre2-10.23.tar.bz2/sha512 deleted file mode 100644 index 61a5555f9b950..0000000000000 --- a/deps/checksums/pcre2-10.23.tar.bz2/sha512 +++ /dev/null @@ -1 +0,0 @@ -3e5910bd2405cc35934d91e4be760abe4f2e900202a20b6ba74adb7a3acb2b74b3bf9b0e97e8de10f8e8534133e0722e0bf0f5fb40d6c2c4520d1ed61749d456 diff --git a/deps/checksums/pcre2-10.30.tar.bz2/md5 b/deps/checksums/pcre2-10.30.tar.bz2/md5 new file mode 100644 index 0000000000000..ab6a1e91af07a --- /dev/null +++ b/deps/checksums/pcre2-10.30.tar.bz2/md5 @@ -0,0 +1 @@ +d3adf4b130eed854a530390f00020a65 diff --git a/deps/checksums/pcre2-10.30.tar.bz2/sha512 b/deps/checksums/pcre2-10.30.tar.bz2/sha512 new file mode 100644 index 0000000000000..06addeba2e3e9 --- /dev/null +++ b/deps/checksums/pcre2-10.30.tar.bz2/sha512 @@ -0,0 +1 @@ +f247a9f917c75920793b9919a45bb1426d126246e7a5d04e39d9407e44b5781f894a90cd3d232b385436b2f22be391335ab782664dd3a28c79058a2fcc74dc3e diff --git a/deps/libuv.version b/deps/libuv.version index dd4ea6f5e5760..1a59e7be3fd44 100644 --- a/deps/libuv.version +++ b/deps/libuv.version @@ -1,2 +1,2 @@ LIBUV_BRANCH=julia-uv1.9.0 -LIBUV_SHA1=52d72a52cc7ccd570929990f010ed16e2ec604c8 +LIBUV_SHA1=c5a4e584989669ad48c1728b35063291e16f85ee diff --git a/deps/patches/pcre-mingw.patch b/deps/patches/pcre-mingw.patch deleted file mode 100644 index 862cd4bf3a0a9..0000000000000 --- a/deps/patches/pcre-mingw.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/pcre2grep.c b/src/pcre2grep.c -index e98d743..f00dac2 100644 ---- a/src/pcre2grep.c -+++ b/src/pcre2grep.c -@@ -628,7 +628,7 @@ z/OS, and "no support". */ - - /************* Directory scanning Unix-style and z/OS ***********/ - --#if (defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS -+#if ((defined HAVE_SYS_STAT_H && defined HAVE_DIRENT_H && defined HAVE_SYS_TYPES_H) || defined NATIVE_ZOS) && !defined WIN32 - #include - #include - #include diff --git a/deps/pcre.mk b/deps/pcre.mk index c5e86f338ef30..6375c47bcdb99 100644 --- a/deps/pcre.mk +++ b/deps/pcre.mk @@ -13,12 +13,7 @@ $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted: $(SRCCACHE)/pcre2-$(PCRE_VER).ta touch -c $(SRCCACHE)/pcre2-$(PCRE_VER)/configure # old target echo $1 > $@ -# patch for mingw build https://bugs.exim.org/show_bug.cgi?id=2067 -$(SRCCACHE)/pcre2-$(PCRE_VER)/pcre-mingw.patch-applied: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted - cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/pcre-mingw.patch - echo 1 > $@ - -$(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted $(SRCCACHE)/pcre2-$(PCRE_VER)/pcre-mingw.patch-applied +$(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted mkdir -p $(dir $@) cd $(dir $@) && \ $(dir $<)/configure $(CONFIGURE_COMMON) --enable-jit --includedir=$(build_includedir) CFLAGS="$(CFLAGS) $(PCRE_CFLAGS)" LDFLAGS="$(LDFLAGS) $(PCRE_LDFLAGS)" diff --git a/doc/man/julia.1 b/doc/man/julia.1 index cc77d36e07969..2944eca7f4436 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -108,8 +108,8 @@ Run processes on hosts listed in Interactive mode; REPL runs and isinteractive() is true .TP --q, --quiet -Quiet startup without banner +--banner={yes|no} +Enable or disable startup banner .TP --color={yes|no} @@ -160,6 +160,10 @@ or adhere to declarations in source code --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ('error' turns warnings into errors) +.TP +--warn-overwrite={yes|no} +Enable or disable method overwrite warnings + .TP --output-o Generate an object file (including system image data) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 88df6ae64f613..a13e6f4bf2660 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -1,7 +1,7 @@ # Julia ASTs Julia has two representations of code. First there is a surface syntax AST returned by the parser -(e.g. the [`parse()`](@ref) function), and manipulated by macros. It is a structured representation +(e.g. the [`parse`](@ref) function), and manipulated by macros. It is a structured representation of code as it is written, constructed by `julia-parser.scm` from a character stream. Next there is a lowered form, or IR (intermediate representation), which is used by type inference and code generation. In the lowered form there are fewer types of nodes, all macros are expanded, and all @@ -170,8 +170,8 @@ These symbols appear in the `head` field of `Expr`s in lowered form. * `boundscheck` - Indicates the beginning or end of a section of code that performs a bounds check. Like `inbounds`, - a stack is maintained, and the second argument can be one of: `true`, `false`, or `:pop`. + Has the value `false` if inlined into a section of code marked with `@inbounds`, + otherwise has the value `true`. * `copyast` @@ -486,7 +486,8 @@ they are parsed as a block: `(for (block (= v1 iter1) (= v2 iter2)) body)`. `break` and `continue` are parsed as 0-argument expressions `(break)` and `(continue)`. -`let` is parsed as `(let body (= var1 val1) (= var2 val2) ...)`. +`let` is parsed as `(let (= var val) body)` or `(let (block (= var1 val1) (= var2 val2) ...) body)`, +like `for` loops. A basic function definition is parsed as `(function (call f x) body)`. A more complex example: diff --git a/doc/src/devdocs/boundscheck.md b/doc/src/devdocs/boundscheck.md index 0647e01a722f1..9eed150ea1497 100644 --- a/doc/src/devdocs/boundscheck.md +++ b/doc/src/devdocs/boundscheck.md @@ -5,17 +5,15 @@ accessing arrays. In tight inner loops or other performance critical situations, to skip these bounds checks to improve runtime performance. For instance, in order to emit vectorized (SIMD) instructions, your loop body cannot contain branches, and thus cannot contain bounds checks. Consequently, Julia includes an `@inbounds(...)` macro to tell the compiler to skip such bounds -checks within the given block. For the built-in `Array` type, the magic happens inside the `arrayref` -and `arrayset` intrinsics. User-defined array types instead use the `@boundscheck(...)` macro +checks within the given block. User-defined array types can use the `@boundscheck(...)` macro to achieve context-sensitive code selection. ## Eliding bounds checks -The `@boundscheck(...)` macro marks blocks of code that perform bounds checking. When such blocks -appear inside of an `@inbounds(...)` block, the compiler removes these blocks. When the `@boundscheck(...)` -is nested inside of a calling function containing an `@inbounds(...)`, the compiler will remove -the `@boundscheck` block *only if it is inlined* into the calling function. For example, you might -write the method `sum` as: +The `@boundscheck(...)` macro marks blocks of code that perform bounds checking. +When such blocks are inlined into an `@inbounds(...)` block, the compiler may remove these blocks. +The compiler removes the `@boundscheck` block *only if it is inlined* into the calling function. +For example, you might write the method `sum` as: ```julia function sum(A::AbstractArray) diff --git a/doc/src/devdocs/libgit2.md b/doc/src/devdocs/libgit2.md index fb86054d00811..f485d77f3e0f6 100644 --- a/doc/src/devdocs/libgit2.md +++ b/doc/src/devdocs/libgit2.md @@ -17,11 +17,15 @@ Base.LibGit2.Buffer Base.LibGit2.CachedCredentials Base.LibGit2.CheckoutOptions Base.LibGit2.CloneOptions +Base.LibGit2.DescribeOptions +Base.LibGit2.DescribeFormatOptions Base.LibGit2.DiffDelta Base.LibGit2.DiffFile Base.LibGit2.DiffOptionsStruct Base.LibGit2.FetchHead Base.LibGit2.FetchOptions +Base.LibGit2.GitAnnotated +Base.LibGit2.GitBlame Base.LibGit2.GitBlob Base.LibGit2.GitCommit Base.LibGit2.GitHash @@ -30,6 +34,7 @@ Base.LibGit2.GitRemote Base.LibGit2.GitRemoteAnon Base.LibGit2.GitRepo Base.LibGit2.GitRepoExt +Base.LibGit2.GitRevWalker Base.LibGit2.GitShortHash Base.LibGit2.GitSignature Base.LibGit2.GitStatus @@ -37,6 +42,7 @@ Base.LibGit2.GitTag Base.LibGit2.GitTree Base.LibGit2.IndexEntry Base.LibGit2.IndexTime +Base.LibGit2.BlameOptions Base.LibGit2.MergeOptions Base.LibGit2.ProxyOptions Base.LibGit2.PushOptions @@ -50,6 +56,7 @@ Base.LibGit2.StatusOptions Base.LibGit2.StrArrayStruct Base.LibGit2.TimeStruct Base.LibGit2.UserPasswordCredentials +Base.LibGit2.add! Base.LibGit2.add_fetch! Base.LibGit2.add_push! Base.LibGit2.addblob! @@ -62,20 +69,32 @@ Base.LibGit2.checkused! Base.LibGit2.clone Base.LibGit2.commit Base.LibGit2.committer +Base.LibGit2.count(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) +Base.LibGit2.counthunks Base.LibGit2.create_branch Base.LibGit2.credentials_callback Base.LibGit2.credentials_cb Base.LibGit2.default_signature Base.LibGit2.delete_branch Base.LibGit2.diff_files +Base.LibGit2.entryid +Base.LibGit2.entrytype Base.LibGit2.fetch +Base.LibGit2.fetchheads Base.LibGit2.fetch_refspecs Base.LibGit2.fetchhead_foreach_cb +Base.LibGit2.merge_base Base.LibGit2.merge!(::Base.LibGit2.GitRepo; ::Any...) +Base.LibGit2.merge!(::Base.LibGit2.GitRepo, ::Vector{Base.LibGit2.GitAnnotated}; ::Base.LibGit2.MergeOptions, ::Base.LibGit2.CheckoutOptions) +Base.LibGit2.merge!(::Base.LibGit2.GitRepo, ::Vector{Base.LibGit2.GitAnnotated}, ::Bool; ::Base.LibGit2.MergeOptions, ::Base.LibGit2.CheckoutOptions) Base.LibGit2.ffmerge! Base.LibGit2.fullname +Base.LibGit2.features +Base.LibGit2.filename +Base.LibGit2.filemode Base.LibGit2.get_creds! Base.LibGit2.gitdir +Base.LibGit2.@githash_str Base.LibGit2.head Base.LibGit2.head! Base.LibGit2.head_oid @@ -87,10 +106,14 @@ Base.LibGit2.iscommit Base.LibGit2.isdiff Base.LibGit2.isdirty Base.LibGit2.isorphan +Base.LibGit2.isset +Base.LibGit2.iszero Base.LibGit2.lookup_branch +Base.LibGit2.map(::Function, ::Base.LibGit2.GitRevWalker; ::Base.LibGit2.GitHash, ::Cint, ::Bool) Base.LibGit2.mirror_callback Base.LibGit2.mirror_cb Base.LibGit2.message +Base.LibGit2.merge_analysis Base.LibGit2.name Base.LibGit2.need_update Base.LibGit2.objtype @@ -98,12 +121,17 @@ Base.LibGit2.path Base.LibGit2.peel Base.LibGit2.posixpath Base.LibGit2.push +Base.LibGit2.push!(::Base.LibGit2.GitRevWalker, ::Base.LibGit2.GitHash) +Base.LibGit2.push_head! Base.LibGit2.push_refspecs +Base.LibGit2.raw Base.LibGit2.read_tree! Base.LibGit2.rebase! Base.LibGit2.ref_list Base.LibGit2.reftype Base.LibGit2.remotes +Base.LibGit2.remove! +Base.LibGit2.reset Base.LibGit2.reset! Base.LibGit2.restore Base.LibGit2.revcount @@ -116,9 +144,15 @@ Base.LibGit2.tag_create Base.LibGit2.tag_delete Base.LibGit2.tag_list Base.LibGit2.target +Base.LibGit2.toggle +Base.LibGit2.transact Base.LibGit2.treewalk Base.LibGit2.upstream +Base.LibGit2.update! Base.LibGit2.url +Base.LibGit2.version Base.LibGit2.with +Base.LibGit2.with_warn Base.LibGit2.workdir +Base.LibGit2.GitObject(::Base.LibGit2.GitTreeEntry) ``` diff --git a/doc/src/devdocs/reflection.md b/doc/src/devdocs/reflection.md index 09d2bba65abec..f835bdac7b1dc 100644 --- a/doc/src/devdocs/reflection.md +++ b/doc/src/devdocs/reflection.md @@ -10,7 +10,7 @@ returns symbols for all bindings in `m`, regardless of export status. ## DataType fields -The names of `DataType` fields may be interrogated using [`fieldnames()`](@ref). For example, +The names of `DataType` fields may be interrogated using [`fieldnames`](@ref). For example, given the following type, `fieldnames(Point)` returns an arrays of [`Symbol`](@ref) elements representing the field names: @@ -49,7 +49,7 @@ of these fields is the `types` field observed in the example above. ## Subtypes -The *direct* subtypes of any `DataType` may be listed using [`subtypes()`](@ref). For example, +The *direct* subtypes of any `DataType` may be listed using [`subtypes`](@ref). For example, the abstract `DataType` [`AbstractFloat`](@ref) has four (concrete) subtypes: ```jldoctest @@ -62,7 +62,7 @@ julia> subtypes(AbstractFloat) ``` Any abstract subtype will also be included in this list, but further subtypes thereof will not; -recursive application of [`subtypes()`](@ref) may be used to inspect the full type tree. +recursive application of [`subtypes`](@ref) may be used to inspect the full type tree. ## DataType layout @@ -73,12 +73,12 @@ returns the (byte) offset for field *i* relative to the start of the type. ## Function methods -The methods of any generic function may be listed using [`methods()`](@ref). The method dispatch -table may be searched for methods accepting a given type using [`methodswith()`](@ref). +The methods of any generic function may be listed using [`methods`](@ref). The method dispatch +table may be searched for methods accepting a given type using [`methodswith`](@ref). ## Expansion and lowering -As discussed in the [Metaprogramming](@ref) section, the [`macroexpand()`](@ref) function gives +As discussed in the [Metaprogramming](@ref) section, the [`macroexpand`](@ref) function gives the unquoted and interpolated expression (`Expr`) form for a given macro. To use `macroexpand`, `quote` the expression block itself (otherwise, the macro will be evaluated and the result will be passed instead!). For example: @@ -88,10 +88,10 @@ julia> macroexpand(@__MODULE__, :(@edit println("")) ) :((Base.edit)(println, (Base.typesof)(""))) ``` -The functions `Base.Meta.show_sexpr()` and [`dump()`](@ref) are used to display S-expr style views +The functions `Base.Meta.show_sexpr` and [`dump`](@ref) are used to display S-expr style views and depth-nested detail views for any expression. -Finally, the [`expand()`](@ref) function gives the `lowered` form of any expression and is of +Finally, the [`expand`](@ref) function gives the `lowered` form of any expression and is of particular interest for understanding both macros and top-level statements such as function declarations and variable assignments: @@ -113,7 +113,7 @@ Inspecting the lowered form for functions requires selection of the specific met because generic functions may have many methods with different type signatures. For this purpose, method-specific code-lowering is available using [`code_lowered(f::Function, (Argtypes...))`](@ref), and the type-inferred form is available using [`code_typed(f::Function, (Argtypes...))`](@ref). -[`code_warntype(f::Function, (Argtypes...))`](@ref) adds highlighting to the output of [`code_typed()`](@ref) +[`code_warntype(f::Function, (Argtypes...))`](@ref) adds highlighting to the output of [`code_typed`](@ref) (see [`@code_warntype`](@ref)). Closer to the machine, the LLVM intermediate representation of a function may be printed using diff --git a/doc/src/devdocs/stdio.md b/doc/src/devdocs/stdio.md index f78d7c623e62b..2adc259e03640 100644 --- a/doc/src/devdocs/stdio.md +++ b/doc/src/devdocs/stdio.md @@ -50,7 +50,7 @@ $ echo hello | julia -e 'println(typeof((STDIN, STDOUT, STDERR)))' | cat Tuple{Base.PipeEndpoint,Base.PipeEndpoint,Base.TTY} ``` -The [`Base.read()`](@ref) and [`Base.write()`](@ref) methods for these streams use [`ccall`](@ref) +The [`Base.read`](@ref) and [`Base.write`](@ref) methods for these streams use [`ccall`](@ref) to call libuv wrappers in `src/jl_uv.c`, e.g.: ``` diff --git a/doc/src/devdocs/subarrays.md b/doc/src/devdocs/subarrays.md index d77c7f68fb96b..e030434d25b57 100644 --- a/doc/src/devdocs/subarrays.md +++ b/doc/src/devdocs/subarrays.md @@ -36,15 +36,31 @@ cartesian, rather than linear, indexing. Consider making 2d slices of a 3d array: -```julia -S1 = view(A, :, 5, 2:6) -S2 = view(A, 5, :, 2:6) +```@meta +DocTestSetup = :(srand(1234)) +``` +```jldoctest subarray +julia> A = rand(2,3,4); + +julia> S1 = view(A, :, 1, 2:3) +2×2 SubArray{Float64,2,Array{Float64,3},Tuple{Base.Slice{Base.OneTo{Int64}},Int64,UnitRange{Int64}},false}: + 0.200586 0.066423 + 0.298614 0.956753 + +julia> S2 = view(A, 1, :, 2:3) +3×2 SubArray{Float64,2,Array{Float64,3},Tuple{Int64,Base.Slice{Base.OneTo{Int64}},UnitRange{Int64}},true}: + 0.200586 0.066423 + 0.246837 0.646691 + 0.648882 0.276021 +``` +```@meta +DocTestSetup = nothing ``` `view` drops "singleton" dimensions (ones that are specified by an `Int`), so both `S1` and `S2` are two-dimensional `SubArray`s. Consequently, the natural way to index these is with `S1[i,j]`. - To extract the value from the parent array `A`, the natural approach is to replace `S1[i,j]` -with `A[i,5,(2:6)[j]]` and `S2[i,j]` with `A[5,i,(2:6)[j]]`. +To extract the value from the parent array `A`, the natural approach is to replace `S1[i,j]` +with `A[i,1,(2:3)[j]]` and `S2[i,j]` with `A[1,i,(2:3)[j]]`. The key feature of the design of SubArrays is that this index replacement can be performed without any runtime overhead. @@ -71,13 +87,14 @@ a `Tuple` of the types of the indices for each dimension. The final one, `L`, is as a convenience for dispatch; it's a boolean that represents whether the index types support fast linear indexing. More on that later. -If in our example above `A` is a `Array{Float64, 3}`, our `S1` case above would be a `SubArray{Int64,2,Array{Int64,3},Tuple{Colon,Int64,UnitRange{Int64}},false}`. +If in our example above `A` is a `Array{Float64, 3}`, our `S1` case above would be a +`SubArray{Float64,2,Array{Float64,3},Tuple{Base.Slice{Base.OneTo{Int64}},Int64,UnitRange{Int64}},false}`. Note in particular the tuple parameter, which stores the types of the indices used to create -`S1`. Likewise, +`S1`. Likewise, -```julia-repl +```jldoctest subarray julia> S1.indexes -(Colon(),5,2:6) +(Base.Slice(Base.OneTo(2)), 1, 2:3) ``` Storing these values allows index replacement, and having the types encoded as parameters allows @@ -138,7 +155,7 @@ For `SubArray` types, the availability of efficient linear indexing is based pur of the indices, and does not depend on values like the size of the parent array. You can ask whether a given set of indices supports fast linear indexing with the internal `Base.viewindexing` function: -```julia-repl +```jldoctest subarray julia> Base.viewindexing(S1.indexes) IndexCartesian() diff --git a/doc/src/devdocs/sysimg.md b/doc/src/devdocs/sysimg.md index dbda7567e1076..2248911710e90 100644 --- a/doc/src/devdocs/sysimg.md +++ b/doc/src/devdocs/sysimg.md @@ -24,7 +24,7 @@ Julia session, type: include(joinpath(JULIA_HOME, Base.DATAROOTDIR, "julia", "build_sysimg.jl")) ``` -This will include a `build_sysimg()` function: +This will include a `build_sysimg` function: ```@docs BuildSysImg.build_sysimg diff --git a/doc/src/devdocs/types.md b/doc/src/devdocs/types.md index 3008a5990611e..c43e8c846e050 100644 --- a/doc/src/devdocs/types.md +++ b/doc/src/devdocs/types.md @@ -28,7 +28,7 @@ julia> typeintersect(Int, Float64) Union{} julia> Union{Int, Float64} -Union{Float64, Int64} +Union{Int64, Float64} julia> typejoin(Int, Float64) Real diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index ecdcf33cb3855..e9b5d50d8580d 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -76,7 +76,7 @@ omitted it will default to [`Float64`](@ref). The syntax `[A, B, C, ...]` constructs a 1-d array (vector) of its arguments. If all arguments have a common [promotion type](@ref conversion-and-promotion) then they get -converted to that type using `convert()`. +converted to that type using `convert`. ### Concatenation @@ -94,11 +94,11 @@ The concatenation functions are used so often that they have special syntax: | Expression | Calls | |:----------------- |:----------------- | -| `[A; B; C; ...]` | [`vcat()`](@ref) | -| `[A B C ...]` | [`hcat()`](@ref) | -| `[A B; C D; ...]` | [`hvcat()`](@ref) | +| `[A; B; C; ...]` | [`vcat`](@ref) | +| `[A B C ...]` | [`hcat`](@ref) | +| `[A B; C D; ...]` | [`hvcat`](@ref) | -[`hvcat()`](@ref) concatenates in both dimension 1 (with semicolons) and dimension 2 (with spaces). +[`hvcat`](@ref) concatenates in both dimension 1 (with semicolons) and dimension 2 (with spaces). ### Typed array initializers @@ -278,7 +278,7 @@ julia> x[1, [2 3; 4 1]] ``` Empty ranges of the form `n:n-1` are sometimes used to indicate the inter-index location between -`n-1` and `n`. For example, the [`searchsorted()`](@ref) function uses this convention to indicate +`n-1` and `n`. For example, the [`searchsorted`](@ref) function uses this convention to indicate the insertion point of a value not found in a sorted array: ```jldoctest @@ -311,7 +311,7 @@ Just as in [Indexing](@ref man-array-indexing), the `end` keyword may be used to represent the last index of each dimension within the indexing brackets, as determined by the size of the array being assigned into. Indexed assignment syntax without the `end` keyword is equivalent to a call to -[`setindex!()`](@ref): +[`setindex!`](@ref): ``` setindex!(A, X, I_1, I_2, ..., I_n) @@ -440,7 +440,7 @@ vector of `CartesianIndex{N}`s where its values are `true`. A logical index must be a vector of the same length as the dimension it indexes into, or it must be the only index provided and match the size and dimensionality of the array it indexes into. It is generally more efficient to use boolean arrays as -indices directly instead of first calling [`find()`](@ref). +indices directly instead of first calling [`find`](@ref). ```jldoctest julia> x = reshape(1:16, 4, 4) @@ -546,7 +546,7 @@ Note that comparisons such as `==` operate on whole arrays, giving a single bool answer. Use dot operators like `.==` for elementwise comparisons. (For comparison operations like `<`, *only* the elementwise `.<` version is applicable to arrays.) -Also notice the difference between `max.(a,b)`, which `broadcast`s [`max()`](@ref) +Also notice the difference between `max.(a,b)`, which `broadcast`s [`max`](@ref) elementwise over `a` and `b`, and `maximum(a)`, which finds the largest value within `a`. The same relationship holds for `min.(a,b)` and `minimum(a)`. @@ -565,7 +565,7 @@ julia> repmat(a,1,3)+A 1.56851 1.86401 1.67846 ``` -This is wasteful when dimensions get large, so Julia offers [`broadcast()`](@ref), which expands +This is wasteful when dimensions get large, so Julia offers [`broadcast`](@ref), which expands singleton dimensions in array arguments to match the corresponding dimension in the other array without using extra memory, and applies the given function elementwise: @@ -587,21 +587,21 @@ julia> broadcast(+, a, b) [Dotted operators](@ref man-dot-operators) such as `.+` and `.*` are equivalent to `broadcast` calls (except that they fuse, as described below). There is also a -[`broadcast!()`](@ref) function to specify an explicit destination (which can also -be accessed in a fusing fashion by `.=` assignment), and functions [`broadcast_getindex()`](@ref) -and [`broadcast_setindex!()`](@ref) that broadcast the indices before indexing. Moreover, `f.(args...)` +[`broadcast!`](@ref) function to specify an explicit destination (which can also +be accessed in a fusing fashion by `.=` assignment), and functions [`broadcast_getindex`](@ref) +and [`broadcast_setindex!`](@ref) that broadcast the indices before indexing. Moreover, `f.(args...)` is equivalent to `broadcast(f, args...)`, providing a convenient syntax to broadcast any function ([dot syntax](@ref man-vectorized)). Nested "dot calls" `f.(...)` (including calls to `.+` etcetera) [automatically fuse](@ref man-dot-operators) into a single `broadcast` call. -Additionally, [`broadcast()`](@ref) is not limited to arrays (see the function documentation), +Additionally, [`broadcast`](@ref) is not limited to arrays (see the function documentation), it also handles tuples and treats any argument that is not an array, tuple or `Ref` (except for `Ptr`) as a "scalar". ```jldoctest julia> convert.(Float32, [1, 2]) 2-element Array{Float32,1}: - 1.0 - 2.0 + 1.0f0 + 2.0f0 julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7]) 2×2 Array{UInt8,2}: @@ -627,13 +627,13 @@ The `AbstractArray` type includes anything vaguely array-like, and implementatio be quite different from conventional arrays. For example, elements might be computed on request rather than stored. However, any concrete `AbstractArray{T,N}` type should generally implement at least [`size(A)`](@ref) (returning an `Int` tuple), [`getindex(A,i)`](@ref) and [`getindex(A,i1,...,iN)`](@ref getindex); -mutable arrays should also implement [`setindex!()`](@ref). It is recommended that these operations +mutable arrays should also implement [`setindex!`](@ref). It is recommended that these operations have nearly constant time complexity, or technically Õ(1) complexity, as otherwise some array functions may be unexpectedly slow. Concrete types should also typically provide a [`similar(A,T=eltype(A),dims=size(A))`](@ref) -method, which is used to allocate a similar array for [`copy()`](@ref) and other out-of-place +method, which is used to allocate a similar array for [`copy`](@ref) and other out-of-place operations. No matter how an `AbstractArray{T,N}` is represented internally, `T` is the type of object returned by *integer* indexing (`A[1, ..., 1]`, when `A` is not empty) and `N` should be -the length of the tuple returned by [`size()`](@ref). +the length of the tuple returned by [`size`](@ref). `DenseArray` is an abstract subtype of `AbstractArray` intended to include all arrays that are laid out at regular offsets in memory, and which can therefore be passed to external C and Fortran @@ -650,9 +650,9 @@ basic storage-specific operations are all that have to be implemented for [`Arra that the rest of the array library can be implemented in a generic manner. `SubArray` is a specialization of `AbstractArray` that performs indexing by reference rather than -by copying. A `SubArray` is created with the [`view()`](@ref) function, which is called the same -way as [`getindex()`](@ref) (with an array and a series of index arguments). The result of [`view()`](@ref) -looks the same as the result of [`getindex()`](@ref), except the data is left in place. [`view()`](@ref) +by copying. A `SubArray` is created with the [`view`](@ref) function, which is called the same +way as [`getindex`](@ref) (with an array and a series of index arguments). The result of [`view`](@ref) +looks the same as the result of [`getindex`](@ref), except the data is left in place. [`view`](@ref) stores the input index vectors in a `SubArray` object, which can later be used to index the original array indirectly. By putting the [`@views`](@ref) macro in front of an expression or block of code, any `array[...]` slice in that expression will be converted to @@ -743,10 +743,10 @@ them is by doing a double transpose. In some applications, it is convenient to store explicit zero values in a `SparseMatrixCSC`. These *are* accepted by functions in `Base` (but there is no guarantee that they will be preserved in mutating operations). Such explicitly stored zeros are treated as structural nonzeros by many -routines. The [`nnz()`](@ref) function returns the number of elements explicitly stored in the +routines. The [`nnz`](@ref) function returns the number of elements explicitly stored in the sparse data structure, including structural nonzeros. In order to count the exact number of -numerical nonzeros, use [`countnz()`](@ref), which inspects every stored element of a sparse -matrix. [`dropzeros()`](@ref), and the in-place [`dropzeros!()`](@ref), can be used to +numerical nonzeros, use [`count(!iszero, x)`](@ref), which inspects every stored element of a sparse +matrix. [`dropzeros`](@ref), and the in-place [`dropzeros!`](@ref), can be used to remove stored zeros from the sparse matrix. ```jldoctest @@ -781,8 +781,8 @@ stored zeros. (See [Sparse Matrix Storage](@ref man-csc).). ### Sparse Vector and Matrix Constructors -The simplest way to create sparse arrays is to use functions equivalent to the [`zeros()`](@ref) -and [`eye()`](@ref) functions that Julia provides for working with dense arrays. To produce +The simplest way to create sparse arrays is to use functions equivalent to the [`zeros`](@ref) +and [`eye`](@ref) functions that Julia provides for working with dense arrays. To produce sparse arrays instead, you can use the same names with an `sp` prefix: ```jldoctest @@ -796,7 +796,7 @@ julia> speye(3,5) [3, 3] = 1.0 ``` -The [`sparse()`](@ref) function is often a handy way to construct sparse arrays. For +The [`sparse`](@ref) function is often a handy way to construct sparse arrays. For example, to construct a sparse matrix we can input a vector `I` of row indices, a vector `J` of column indices, and a vector `V` of stored values (this is also known as the [COO (coordinate) format](https://en.wikipedia.org/wiki/Sparse_matrix#Coordinate_list_.28COO.29)). @@ -823,8 +823,8 @@ julia> R = sparsevec(I,V) [5] = 3 ``` -The inverse of the [`sparse()`](@ref) and [`sparsevec`](@ref) functions is -[`findnz()`](@ref), which retrieves the inputs used to create the sparse array. +The inverse of the [`sparse`](@ref) and [`sparsevec`](@ref) functions is +[`findnz`](@ref), which retrieves the inputs used to create the sparse array. There is also a [`findn`](@ref) function which only returns the index vectors. ```jldoctest sparse_function @@ -846,7 +846,7 @@ julia> findn(R) ``` Another way to create a sparse array is to convert a dense array into a sparse array using -the [`sparse()`](@ref) function: +the [`sparse`](@ref) function: ```jldoctest julia> sparse(eye(5)) @@ -863,7 +863,7 @@ julia> sparse([1.0, 0.0, 1.0]) [3] = 1.0 ``` -You can go in the other direction using the [`Array`](@ref) constructor. The [`issparse()`](@ref) +You can go in the other direction using the [`Array`](@ref) constructor. The [`issparse`](@ref) function can be used to query if a matrix is sparse. ```jldoctest @@ -876,7 +876,7 @@ true Arithmetic operations on sparse matrices also work as they do on dense matrices. Indexing of, assignment into, and concatenation of sparse matrices work in the same way as dense matrices. Indexing operations, especially assignment, are expensive, when carried out one element at a time. -In many cases it may be better to convert the sparse matrix into `(I,J,V)` format using [`findnz()`](@ref), +In many cases it may be better to convert the sparse matrix into `(I,J,V)` format using [`findnz`](@ref), manipulate the values or the structure in the dense vectors `(I,J,V)`, and then reconstruct the sparse matrix. @@ -894,7 +894,7 @@ section of the standard library reference. | Sparse | Dense | Description | |:-------------------------- |:---------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`spzeros(m,n)`](@ref) | [`zeros(m,n)`](@ref) | Creates a *m*-by-*n* matrix of zeros. ([`spzeros(m,n)`](@ref) is empty.) | -| [`spones(S)`](@ref) | [`ones(m,n)`](@ref) | Creates a matrix filled with ones. Unlike the dense version, [`spones()`](@ref) has the same sparsity pattern as *S*. | +| [`spones(S)`](@ref) | [`ones(m,n)`](@ref) | Creates a matrix filled with ones. Unlike the dense version, [`spones`](@ref) has the same sparsity pattern as *S*. | | [`speye(n)`](@ref) | [`eye(n)`](@ref) | Creates a *n*-by-*n* identity matrix. | | [`full(S)`](@ref) | [`sparse(A)`](@ref) | Interconverts between dense and sparse formats. | | [`sprand(m,n,d)`](@ref) | [`rand(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed uniformly on the half-open interval ``[0, 1)``. | diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 6c0070dedfed7..a0ef7744c4b06 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -153,12 +153,12 @@ For example, to match C prototypes of the form: typedef returntype (*functiontype)(argumenttype,...) ``` -The function [`cfunction()`](@ref) generates the C-compatible function pointer for a call to a -Julia function. Arguments to [`cfunction()`](@ref) are as follows: +The function [`cfunction`](@ref) generates the C-compatible function pointer for a call to a +Julia function. Arguments to [`cfunction`](@ref) are as follows: 1. A Julia Function 2. Return type -3. A tuple of input types +3. A tuple type of input types Only platform-default C calling convention is supported. `cfunction`-generated pointers cannot be used in calls where WINAPI expects `stdcall` function on 32-bit windows, but can be used on WIN64 @@ -192,11 +192,11 @@ a C `int`, so we must be sure to return `Cint` via a call to `convert` and a `ty In order to pass this function to C, we obtain its address using the function `cfunction`: ```jldoctest mycompare -julia> const mycompare_c = cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})); +julia> const mycompare_c = cfunction(mycompare, Cint, Tuple{Ref{Cdouble}, Ref{Cdouble}}); ``` -[`cfunction()`](@ref) accepts three arguments: the Julia function (`mycompare`), the return type -(`Cint`), and a tuple of the argument types, in this case to sort an array of `Cdouble` +[`cfunction`](@ref) accepts three arguments: the Julia function (`mycompare`), the return type +(`Cint`), and a tuple type of the input argument types, in this case to sort an array of `Cdouble` ([`Float64`](@ref)) elements. The final call to `qsort` looks like this: @@ -239,7 +239,7 @@ Julia code from a C header file.) ### Auto-conversion: -Julia automatically inserts calls to the [`Base.cconvert()`](@ref) function to convert each argument +Julia automatically inserts calls to the [`Base.cconvert`](@ref) function to convert each argument to the specified type. For example, the following call: ```julia @@ -254,12 +254,12 @@ ccall((:foo, "libfoo"), Void, (Int32, Float64), Base.unsafe_convert(Float64, Base.cconvert(Float64, y))) ``` -[`Base.cconvert()`](@ref) normally just calls [`convert()`](@ref), but can be defined to return an +[`Base.cconvert`](@ref) normally just calls [`convert`](@ref), but can be defined to return an arbitrary new object more appropriate for passing to C. This should be used to perform all allocations of memory that will be accessed by the C code. For example, this is used to convert an `Array` of objects (e.g. strings) to an array of pointers. -[`Base.unsafe_convert()`](@ref) handles conversion to `Ptr` types. It is considered unsafe because +[`Base.unsafe_convert`](@ref) handles conversion to `Ptr` types. It is considered unsafe because converting an object to a native pointer can hide the object from the garbage collector, causing it to be freed prematurely. @@ -323,9 +323,9 @@ same: (for example, to pass a `Float64` array to a function that operates on uninterpreted bytes), you can declare the argument as `Ptr{Void}`. - If an array of eltype `Ptr{T}` is passed as a `Ptr{Ptr{T}}` argument, [`Base.cconvert()`](@ref) + If an array of eltype `Ptr{T}` is passed as a `Ptr{Ptr{T}}` argument, [`Base.cconvert`](@ref) will attempt to first make a null-terminated copy of the array with each element replaced by its - [`Base.cconvert()`](@ref) version. This allows, for example, passing an `argv` pointer array of type + [`Base.cconvert`](@ref) version. This allows, for example, passing an `argv` pointer array of type `Vector{String}` to an argument of type `Ptr{Ptr{Cchar}}`. On all systems we currently support, basic C/C++ value types may be translated to Julia types @@ -555,7 +555,7 @@ allocated in Julia to be freed by an external library) is equally invalid. In Julia code wrapping calls to external C routines, ordinary (non-pointer) data should be declared to be of type `T` inside the [`ccall`](@ref), as they are passed by value. For C code accepting pointers, `Ref{T}` should generally be used for the types of input arguments, allowing the use -of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert()`](@ref). +of pointers to memory managed by either Julia or C through the implicit call to [`Base.cconvert`](@ref). In contrast, pointers returned by the C function called should be declared to be of output type `Ptr{T}`, reflecting that the memory pointed to is managed by C only. Pointers contained in C structs should be represented as fields of type `Ptr{T}` within the corresponding Julia struct @@ -590,12 +590,12 @@ For translating a C argument list to Julia: * `Any` * argument value must be a valid Julia object - * currently unsupported by [`cfunction()`](@ref) + * currently unsupported by [`cfunction`](@ref) * `jl_value_t**` * `Ref{Any}` * argument value must be a valid Julia object (or `C_NULL`) - * currently unsupported by [`cfunction()`](@ref) + * currently unsupported by [`cfunction`](@ref) * `T*` * `Ref{T}`, where `T` is the Julia type corresponding to `T` @@ -603,7 +603,7 @@ For translating a C argument list to Julia: object * `(T*)(...)` (e.g. a pointer to a function) - * `Ptr{Void}` (you may need to use [`cfunction()`](@ref) explicitly to create this pointer) + * `Ptr{Void}` (you may need to use [`cfunction`](@ref) explicitly to create this pointer) * `...` (e.g. a vararg) * `T...`, where `T` is the Julia type @@ -654,7 +654,7 @@ For translating a C return type to Julia: * `Ptr{T}`, where `T` is the Julia type corresponding to `T` * `(T*)(...)` (e.g. a pointer to a function) - * `Ptr{Void}` (you may need to use [`cfunction()`](@ref) explicitly to create this pointer) + * `Ptr{Void}` (you may need to use [`cfunction`](@ref) explicitly to create this pointer) ### Passing Pointers for Modifying Inputs @@ -687,8 +687,8 @@ function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) incx = incy = 1 product = ccall((:ddot_, "libLAPACK"), Float64, - (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), - &n, DX, &incx, DY, &incy) + (Ref{Int32}, Ptr{Float64}, Ref{Int32}, Ptr{Float64}, Ref{Int32}), + n, DX, incx, DY, incy) return product end ``` @@ -728,7 +728,7 @@ end The [GNU Scientific Library](https://www.gnu.org/software/gsl/) (here assumed to be accessible through `:libgsl`) defines an opaque pointer, `gsl_permutation *`, as the return type of the C -function `gsl_permutation_alloc()`. As user code never has to look inside the `gsl_permutation` +function `gsl_permutation_alloc`. As user code never has to look inside the `gsl_permutation` struct, the corresponding Julia wrapper simply needs a new type declaration, `gsl_permutation`, that has no internal fields and whose sole purpose is to be placed in the type parameter of a `Ptr` type. The return type of the [`ccall`](@ref) is declared as `Ptr{gsl_permutation}`, since @@ -757,7 +757,7 @@ end Here, the input `p` is declared to be of type `Ref{gsl_permutation}`, meaning that the memory that `p` points to may be managed by Julia or by C. A pointer to memory allocated by C should -be of type `Ptr{gsl_permutation}`, but it is convertable using [`Base.cconvert()`](@ref) and therefore +be of type `Ptr{gsl_permutation}`, but it is convertable using [`Base.cconvert`](@ref) and therefore can be used in the same (covariant) context of the input argument to a [`ccall`](@ref). A pointer to memory allocated by Julia must be of type `Ref{gsl_permutation}`, to ensure that the memory address pointed to is valid and that Julia's garbage collector manages the chunk of memory pointed @@ -809,7 +809,7 @@ the C function may end up throwing an invalid memory access exception. ## Garbage Collection Safety -When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer()`](@ref) function. +When passing data to a [`ccall`](@ref), it is best to avoid using the [`pointer`](@ref) function. Instead define a convert method and pass the variables directly to the [`ccall`](@ref). [`ccall`](@ref) automatically arranges that all of its arguments will be preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the [`ccall`](@ref) @@ -818,9 +818,9 @@ way to handle this is to make a global variable of type `Array{Ref,1}` to hold t the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data exists until -you are done with using the pointer. Many methods in Julia such as [`unsafe_load()`](@ref) and -[`String()`](@ref) make copies of data instead of taking ownership of the buffer, so that it is -safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap()`](@ref) +you are done with using the pointer. Many methods in Julia such as [`unsafe_load`](@ref) and +[`String`](@ref) make copies of data instead of taking ownership of the buffer, so that it is +safe to free (or alter) the original data without affecting Julia. A notable exception is [`unsafe_wrap`](@ref) which, for performance reasons, shares (or can be told to take ownership of) the underlying buffer. The garbage collector does not guarantee any order of finalization. That is, if `a` contained @@ -873,6 +873,25 @@ mylibvar = Libdl.dlopen("mylib") ccall(@dlsym("myfunc", mylibvar), Void, ()) ``` +## Closing a Library + +It is sometimes useful to close (unload) a library so that it can be reloaded. +For instance, when developing C code for use with Julia, one may need to compile, +call the C code from Julia, then close the library, make an edit, recompile, +and load in the new changes. One can either restart Julia or use the +`Libdl` functions to manage the library explicitly, such as: + +```julia +lib = Libdl.dlopen("./my_lib.so") # Open the library explicitly. +sym = Libdl.dlsym(lib, :my_fcn) # Get a symbol for the function to call. +ccall(sym, ...) # Use the symbol instead of the (symbol, library) tuple (remaining arguments are the same). +Libdl.dlclose(lib) # Close the library explicitly. +``` + +Note that when using `ccall` with the tuple input +(e.g., `ccall((:my_fcn, "./my_lib.so"), ...)`), the library is opened implicitly +and it may not be explicitly closed. + ## Calling Convention The second argument to [`ccall`](@ref) can optionally be a calling convention specifier (immediately @@ -904,8 +923,8 @@ unlike the equivalent Julia functions exposed by `Core.Intrinsics`. ## Accessing Global Variables -Global variables exported by native libraries can be accessed by name using the [`cglobal()`](@ref) -function. The arguments to [`cglobal()`](@ref) are a symbol specification identical to that used +Global variables exported by native libraries can be accessed by name using the [`cglobal`](@ref) +function. The arguments to [`cglobal`](@ref) are a symbol specification identical to that used by [`ccall`](@ref), and a type describing the value stored in the variable: ```julia-repl @@ -914,7 +933,7 @@ Ptr{Int32} @0x00007f418d0816b8 ``` The result is a pointer giving the address of the value. The value can be manipulated through -this pointer using [`unsafe_load()`](@ref) and [`unsafe_store!()`](@ref). +this pointer using [`unsafe_load`](@ref) and [`unsafe_store!`](@ref). ## Accessing Data through a Pointer @@ -924,7 +943,7 @@ cause Julia to terminate abruptly. Given a `Ptr{T}`, the contents of type `T` can generally be copied from the referenced memory into a Julia object using `unsafe_load(ptr, [index])`. The index argument is optional (default is 1), and follows the Julia-convention of 1-based indexing. This function is intentionally similar -to the behavior of [`getindex()`](@ref) and [`setindex!()`](@ref) (e.g. `[]` access syntax). +to the behavior of [`getindex`](@ref) and [`setindex!`](@ref) (e.g. `[]` access syntax). The return value will be a new object initialized to contain a copy of the contents of the referenced memory. The referenced memory can safely be freed or released. diff --git a/doc/src/manual/complex-and-rational-numbers.md b/doc/src/manual/complex-and-rational-numbers.md index b76a128bb72ab..d656f37c376f4 100644 --- a/doc/src/manual/complex-and-rational-numbers.md +++ b/doc/src/manual/complex-and-rational-numbers.md @@ -111,9 +111,9 @@ julia> angle(1 + 2im) # phase angle in radians 1.1071487177940904 ``` -As usual, the absolute value ([`abs()`](@ref)) of a complex number is its distance from zero. -[`abs2()`](@ref) gives the square of the absolute value, and is of particular use for complex -numbers where it avoids taking a square root. [`angle()`](@ref) returns the phase angle in radians +As usual, the absolute value ([`abs`](@ref)) of a complex number is its distance from zero. +[`abs2`](@ref) gives the square of the absolute value, and is of particular use for complex +numbers where it avoids taking a square root. [`angle`](@ref) returns the phase angle in radians (also known as the *argument* or *arg* function). The full gamut of other [Elementary Functions](@ref) is also defined for complex numbers: @@ -135,7 +135,7 @@ julia> sinh(1 + 2im) ``` Note that mathematical functions typically return real values when applied to real numbers and -complex values when applied to complex numbers. For example, [`sqrt()`](@ref) behaves differently +complex values when applied to complex numbers. For example, [`sqrt`](@ref) behaves differently when applied to `-1` versus `-1 + 0im` even though `-1 == -1 + 0im`: ```jldoctest @@ -159,7 +159,7 @@ julia> a = 1; b = 2; a + b*im 1 + 2im ``` -However, this is *not* recommended; Use the [`complex()`](@ref) function instead to construct +However, this is *not* recommended; Use the [`complex`](@ref) function instead to construct a complex value directly from its real and imaginary parts: ```jldoctest @@ -209,7 +209,7 @@ julia> -4//-12 This normalized form for a ratio of integers is unique, so equality of rational values can be tested by checking for equality of the numerator and denominator. The standardized numerator and -denominator of a rational value can be extracted using the [`numerator()`](@ref) and [`denominator()`](@ref) +denominator of a rational value can be extracted using the [`numerator`](@ref) and [`denominator`](@ref) functions: ```jldoctest diff --git a/doc/src/manual/constructors.md b/doc/src/manual/constructors.md index 0d77cdcb56373..66f6283f4b999 100644 --- a/doc/src/manual/constructors.md +++ b/doc/src/manual/constructors.md @@ -354,7 +354,7 @@ following additional outer constructor method: julia> Point(x::Int64, y::Float64) = Point(convert(Float64,x),y); ``` -This method uses the [`convert()`](@ref) function to explicitly convert `x` to [`Float64`](@ref) +This method uses the [`convert`](@ref) function to explicitly convert `x` to [`Float64`](@ref) and then delegates construction to the general constructor for the case where both arguments are [`Float64`](@ref). With this method definition what was previously a [`MethodError`](@ref) now successfully creates a point of type `Point{Float64}`: @@ -513,7 +513,7 @@ table for the `Type` type. This means that you can declare more flexible constru for abstract types, by explicitly defining methods for the appropriate types. However, in some cases you could consider adding methods to `Base.convert` *instead* of defining -a constructor, because Julia falls back to calling [`convert()`](@ref) if no matching constructor +a constructor, because Julia falls back to calling [`convert`](@ref) if no matching constructor is found. For example, if no constructor `T(args...) = ...` exists `Base.convert(::Type{T}, args...) = ...` is called. diff --git a/doc/src/manual/control-flow.md b/doc/src/manual/control-flow.md index 4d751a836aceb..37f44ee167343 100644 --- a/doc/src/manual/control-flow.md +++ b/doc/src/manual/control-flow.md @@ -6,8 +6,8 @@ Julia provides a variety of control flow constructs: * [Conditional Evaluation](@ref man-conditional-evaluation): `if`-`elseif`-`else` and `?:` (ternary operator). * [Short-Circuit Evaluation](@ref): `&&`, `||` and chained comparisons. * [Repeated Evaluation: Loops](@ref man-loops): `while` and `for`. - * [Exception Handling](@ref): `try`-`catch`, [`error()`](@ref) and [`throw()`](@ref). - * [Tasks (aka Coroutines)](@ref man-tasks): [`yieldto()`](@ref). + * [Exception Handling](@ref): `try`-`catch`, [`error`](@ref) and [`throw`](@ref). + * [Tasks (aka Coroutines)](@ref man-tasks): [`yieldto`](@ref). The first five control flow mechanisms are standard to high-level programming languages. [`Task`](@ref)s are not so standard: they provide non-local control flow, making it possible to switch between @@ -565,7 +565,7 @@ below all interrupt the normal flow of control. | [`UndefVarError`](@ref) | | `UnicodeError` | -For example, the [`sqrt()`](@ref) function throws a [`DomainError`](@ref) if applied to a negative +For example, the [`sqrt`](@ref) function throws a [`DomainError`](@ref) if applied to a negative real value: ```jldoctest @@ -584,10 +584,10 @@ You may define your own exceptions in the following way: julia> struct MyCustomException <: Exception end ``` -### The [`throw()`](@ref) function +### The [`throw`](@ref) function -Exceptions can be created explicitly with [`throw()`](@ref). For example, a function defined only -for nonnegative numbers could be written to [`throw()`](@ref) a [`DomainError`](@ref) if the argument +Exceptions can be created explicitly with [`throw`](@ref). For example, a function defined only +for nonnegative numbers could be written to [`throw`](@ref) a [`DomainError`](@ref) if the argument is negative: ```jldoctest @@ -646,11 +646,11 @@ julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " not defin ### Errors -The [`error()`](@ref) function is used to produce an [`ErrorException`](@ref) that interrupts +The [`error`](@ref) function is used to produce an [`ErrorException`](@ref) that interrupts the normal flow of control. Suppose we want to stop execution immediately if the square root of a negative number is taken. -To do this, we can define a fussy version of the [`sqrt()`](@ref) function that raises an error +To do this, we can define a fussy version of the [`sqrt`](@ref) function that raises an error if its argument is negative: ```jldoctest fussy_sqrt @@ -799,7 +799,7 @@ julia> try error() end # Returns nothing 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. There are situations where no error has occurred, but the ability to unwind the stack and pass a value to a higher level -is desirable. Julia provides the [`rethrow()`](@ref), [`backtrace()`](@ref) and [`catch_backtrace()`](@ref) +is desirable. Julia provides the [`rethrow`](@ref), [`backtrace`](@ref) and [`catch_backtrace`](@ref) functions for more advanced error handling. ### `finally` Clauses @@ -855,7 +855,7 @@ multiple tasks reading from and writing to it. Let's define a producer task, which produces values via the [`put!`](@ref) call. To consume values, we need to schedule the producer to run in a new task. A special [`Channel`](@ref) constructor which accepts a 1-arg function as an argument can be used to run a task bound to a channel. -We can then [`take!()`](@ref) values repeatedly from the channel object: +We can then [`take!`](@ref) values repeatedly from the channel object: ```jldoctest producer julia> function producer(c::Channel) @@ -888,7 +888,7 @@ julia> take!(chnl) ``` One way to think of this behavior is that `producer` was able to return multiple times. Between -calls to [`put!()`](@ref), the producer's execution is suspended and the consumer has control. +calls to [`put!`](@ref), the producer's execution is suspended and the consumer has control. The returned [`Channel`](@ref) can be used as an iterable object in a `for` loop, in which case the loop variable takes on all the produced values. The loop is terminated when the channel is closed. @@ -906,16 +906,16 @@ stop ``` Note that we did not have to explicitly close the channel in the producer. This is because -the act of binding a [`Channel`](@ref) to a [`Task()`](@ref) associates the open lifetime of +the act of binding a [`Channel`](@ref) to a [`Task`](@ref) associates the open lifetime of a channel with that of the bound task. The channel object is closed automatically when the task terminates. Multiple channels can be bound to a task, and vice-versa. -While the [`Task()`](@ref) constructor expects a 0-argument function, the [`Channel()`](@ref) +While the [`Task`](@ref) constructor expects a 0-argument function, the [`Channel`](@ref) method which creates a channel bound task expects a function that accepts a single argument of type [`Channel`](@ref). A common pattern is for the producer to be parameterized, in which case a partial function application is needed to create a 0 or 1 argument [anonymous function](@ref man-anonymous-functions). -For [`Task()`](@ref) objects this can be done either directly or by use of a convenience macro: +For [`Task`](@ref) objects this can be done either directly or by use of a convenience macro: ```julia function mytask(myarg) @@ -927,8 +927,8 @@ taskHdl = Task(() -> mytask(7)) taskHdl = @task mytask(7) ``` -To orchestrate more advanced work distribution patterns, [`bind()`](@ref) and [`schedule()`](@ref) -can be used in conjunction with [`Task()`](@ref) and [`Channel()`](@ref) +To orchestrate more advanced work distribution patterns, [`bind`](@ref) and [`schedule`](@ref) +can be used in conjunction with [`Task`](@ref) and [`Channel`](@ref) constructors to explicitly link a set of channels with a set of producer/consumer tasks. Note that currently Julia tasks are not scheduled to run on separate CPU cores. @@ -936,27 +936,27 @@ True kernel threads are discussed under the topic of [Parallel Computing](@ref). ### Core task operations -Let us explore the low level construct [`yieldto()`](@ref) to underestand how task switching works. +Let us explore the low level construct [`yieldto`](@ref) to underestand how task switching works. `yieldto(task,value)` suspends the current task, switches to the specified `task`, and causes -that task's last [`yieldto()`](@ref) call to return the specified `value`. Notice that [`yieldto()`](@ref) +that task's last [`yieldto`](@ref) call to return the specified `value`. Notice that [`yieldto`](@ref) is the only operation required to use task-style control flow; instead of calling and returning we are always just switching to a different task. This is why this feature is also called "symmetric coroutines"; each task is switched to and from using the same mechanism. -[`yieldto()`](@ref) is powerful, but most uses of tasks do not invoke it directly. Consider why +[`yieldto`](@ref) is powerful, but most uses of tasks do not invoke it directly. Consider why this might be. If you switch away from the current task, you will probably want to switch back to it at some point, but knowing when to switch back, and knowing which task has the responsibility -of switching back, can require considerable coordination. For example, [`put!()`](@ref) and [`take!()`](@ref) +of switching back, can require considerable coordination. For example, [`put!`](@ref) and [`take!`](@ref) are blocking operations, which, when used in the context of channels maintain state to remember -who the consumers are. Not needing to manually keep track of the consuming task is what makes [`put!()`](@ref) -easier to use than the low-level [`yieldto()`](@ref). +who the consumers are. Not needing to manually keep track of the consuming task is what makes [`put!`](@ref) +easier to use than the low-level [`yieldto`](@ref). -In addition to [`yieldto()`](@ref), a few other basic functions are needed to use tasks effectively. +In addition to [`yieldto`](@ref), a few other basic functions are needed to use tasks effectively. - * [`current_task()`](@ref) gets a reference to the currently-running task. - * [`istaskdone()`](@ref) queries whether a task has exited. - * [`istaskstarted()`](@ref) queries whether a task has run yet. - * [`task_local_storage()`](@ref) manipulates a key-value store specific to the current task. + * [`current_task`](@ref) gets a reference to the currently-running task. + * [`istaskdone`](@ref) queries whether a task has exited. + * [`istaskstarted`](@ref) queries whether a task has run yet. + * [`task_local_storage`](@ref) manipulates a key-value store specific to the current task. ### Tasks and events @@ -964,23 +964,23 @@ Most task switches occur as a result of waiting for events such as I/O requests, by a scheduler included in the standard library. The scheduler maintains a queue of runnable tasks, and executes an event loop that restarts tasks based on external events such as message arrival. -The basic function for waiting for an event is [`wait()`](@ref). Several objects implement [`wait()`](@ref); -for example, given a `Process` object, [`wait()`](@ref) will wait for it to exit. [`wait()`](@ref) -is often implicit; for example, a [`wait()`](@ref) can happen inside a call to [`read()`](@ref) +The basic function for waiting for an event is [`wait`](@ref). Several objects implement [`wait`](@ref); +for example, given a `Process` object, [`wait`](@ref) will wait for it to exit. [`wait`](@ref) +is often implicit; for example, a [`wait`](@ref) can happen inside a call to [`read`](@ref) to wait for data to be available. -In all of these cases, [`wait()`](@ref) ultimately operates on a [`Condition`](@ref) object, which -is in charge of queueing and restarting tasks. When a task calls [`wait()`](@ref) on a [`Condition`](@ref), +In all of these cases, [`wait`](@ref) ultimately operates on a [`Condition`](@ref) object, which +is in charge of queueing and restarting tasks. When a task calls [`wait`](@ref) on a [`Condition`](@ref), the task is marked as non-runnable, added to the condition's queue, and switches to the scheduler. The scheduler will then pick another task to run, or block waiting for external events. If all -goes well, eventually an event handler will call [`notify()`](@ref) on the condition, which causes +goes well, eventually an event handler will call [`notify`](@ref) on the condition, which causes tasks waiting for that condition to become runnable again. A task created explicitly by calling [`Task`](@ref) is initially not known to the scheduler. This -allows you to manage tasks manually using [`yieldto()`](@ref) if you wish. However, when such +allows you to manage tasks manually using [`yieldto`](@ref) if you wish. However, when such a task waits for an event, it still gets restarted automatically when the event happens, as you would expect. It is also possible to make the scheduler run a task whenever it can, without necessarily -waiting for any events. This is done by calling [`schedule()`](@ref), or using the [`@schedule`](@ref) +waiting for any events. This is done by calling [`schedule`](@ref), or using the [`@schedule`](@ref) or [`@async`](@ref) macros (see [Parallel Computing](@ref) for more details). ### Task states diff --git a/doc/src/manual/conversion-and-promotion.md b/doc/src/manual/conversion-and-promotion.md index 3ce8b87830ef8..59f8679972078 100644 --- a/doc/src/manual/conversion-and-promotion.md +++ b/doc/src/manual/conversion-and-promotion.md @@ -89,12 +89,12 @@ since type constructors fall back to convert methods. Some languages consider parsing strings as numbers or formatting numbers as strings to be conversions (many dynamic languages will even perform conversion for you automatically), however Julia does not: even though some strings can be parsed as numbers, most strings are not valid representations -of numbers, and only a very limited subset of them are. Therefore in Julia the dedicated `parse()` +of numbers, and only a very limited subset of them are. Therefore in Julia the dedicated `parse` function must be used to perform this operation, making it more explicit. ### Defining New Conversions -To define a new conversion, simply provide a new method for `convert()`. That's really all there +To define a new conversion, simply provide a new method for `convert`. That's really all there is to it. For example, the method to convert a real number to a boolean is this: ```julia diff --git a/doc/src/manual/dates.md b/doc/src/manual/dates.md index ec2c5eb54ed1a..7c8ebda32bbfe 100644 --- a/doc/src/manual/dates.md +++ b/doc/src/manual/dates.md @@ -98,7 +98,7 @@ noting the transition `"yyyymm"` from one period character to the next. Support for text-form month parsing is also supported through the `u` and `U` characters, for abbreviated and full-length month names, respectively. By default, only English month names are supported, so `u` corresponds to "Jan", "Feb", "Mar", etc. And `U` corresponds to "January", "February", -"March", etc. Similar to other name=>value mapping functions [`dayname()`](@ref) and [`monthname()`](@ref), +"March", etc. Similar to other name=>value mapping functions [`dayname`](@ref) and [`monthname`](@ref), custom locales can be loaded by passing in the `locale=>Dict{String,Int}` mapping to the `MONTHTOVALUEABBR` and `MONTHTOVALUE` dicts for abbreviated and full-name month names, respectively. @@ -296,10 +296,10 @@ julia> Dates.dayofquarter(t) 31 ``` -The [`dayname()`](@ref) and [`monthname()`](@ref) methods can also take an optional `locale` keyword +The [`dayname`](@ref) and [`monthname`](@ref) methods can also take an optional `locale` keyword that can be used to return the name of the day or month of the year for other languages/locales. There are also versions of these functions returning the abbreviated names, namely -[`dayabbr()`](@ref) and [`monthabbr()`](@ref). +[`dayabbr`](@ref) and [`monthabbr`](@ref). First the mapping is loaded into the `LOCALES` variable: ```jldoctest tdate2 @@ -328,7 +328,7 @@ julia> Dates.monthabbr(t;locale="french") ``` Since the abbreviated versions of the days are not loaded, trying to use the -function `dayabbr()` will error. +function `dayabbr` will error. ```jldoctest tdate2 julia> Dates.dayabbr(t;locale="french") @@ -350,7 +350,7 @@ little as possible when doing [`Period`](@ref) arithmetic. This approach is also *calendrical* arithmetic or what you would probably guess if someone were to ask you the same calculation in a conversation. Why all the fuss about this? Let's take a classic example: add 1 month to January 31st, 2014. What's the answer? Javascript will say [March 3](http://www.markhneedham.com/blog/2009/01/07/javascript-add-a-month-to-a-date/) -(assumes 31 days). PHP says [March 2](http://stackoverflow.com/questions/5760262/php-adding-months-to-a-date-while-not-exceeding-the-last-day-of-the-month) +(assumes 31 days). PHP says [March 2](https://stackoverflow.com/questions/5760262/php-adding-months-to-a-date-while-not-exceeding-the-last-day-of-the-month) (assumes 30 days). The fact is, there is no right answer. In the `Dates` module, it gives the result of February 28th. How does it figure that out? I like to think of the classic 7-7-7 gambling game in casinos. @@ -451,7 +451,7 @@ julia> Dates.lastdayofquarter(Date(2014,7,16)) # Adjusts to the last day of the 2014-09-30 ``` -The next two higher-order methods, [`tonext()`](@ref), and [`toprev()`](@ref), generalize working +The next two higher-order methods, [`tonext`](@ref), and [`toprev`](@ref), generalize working with temporal expressions by taking a `DateFunction` as first argument, along with a starting [`TimeType`](@ref). A `DateFunction` is just a function, usually anonymous, that takes a single [`TimeType`](@ref) as input and returns a [`Bool`](@ref), `true` indicating a satisfied @@ -481,7 +481,7 @@ julia> Dates.tonext(Date(2014,7,13)) do x 2014-11-27 ``` -The [`Base.filter()`](@ref) method can be used to obtain all valid dates/moments in a specified +The [`Base.filter`](@ref) method can be used to obtain all valid dates/moments in a specified range: ```jldoctest @@ -545,7 +545,7 @@ julia> div(y3,3) # mirrors integer division ## Rounding [`Date`](@ref) and [`DateTime`](@ref) values can be rounded to a specified resolution (e.g., 1 -month or 15 minutes) with [`floor()`](@ref), [`ceil()`](@ref), or [`round()`](@ref): +month or 15 minutes) with [`floor`](@ref), [`ceil`](@ref), or [`round`](@ref): ```jldoctest julia> floor(Date(1985, 8, 16), Dates.Month) @@ -558,8 +558,8 @@ julia> round(DateTime(2016, 8, 6, 20, 15), Dates.Day) 2016-08-07T00:00:00 ``` -Unlike the numeric [`round()`](@ref) method, which breaks ties toward the even number by default, -the [`TimeType`](@ref)[`round()`](@ref) method uses the `RoundNearestTiesUp` rounding mode. (It's +Unlike the numeric [`round`](@ref) method, which breaks ties toward the even number by default, +the [`TimeType`](@ref)[`round`](@ref) method uses the `RoundNearestTiesUp` rounding mode. (It's difficult to guess what breaking ties to nearest "even" [`TimeType`](@ref) would entail.) Further details on the available `RoundingMode` s can be found in the [API reference](@ref stdlib-dates). diff --git a/doc/src/manual/documentation.md b/doc/src/manual/documentation.md index a8176c4f1a59f..289bdbb30f0d2 100644 --- a/doc/src/manual/documentation.md +++ b/doc/src/manual/documentation.md @@ -254,13 +254,13 @@ Documentation written in non-toplevel blocks, such as `begin`, `if`, `for`, and added to the documentation system as blocks are evaluated. For example: ```julia -if VERSION > v"0.5" +if condition() "..." f(x) = x end ``` -will add documentation to `f(x)` when the condition is `true`. Note that even if `f(x)` goes +will add documentation to `f(x)` when `condition()` is `true`. Note that even if `f(x)` goes out of scope at the end of the block, its documentation will remain. ### Dynamic documentation diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index cb0d7c9a39696..5ccf9d250acf7 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -94,7 +94,7 @@ julia> x 3 ``` -Here we created a function `change_array!()`, that assigns `5` to the first element of the passed +Here we created a function `change_array!`, that assigns `5` to the first element of the passed array (bound to `x` at the call site, and bound to `A` within the function). Notice that, after the function call, `x` is still bound to the same array, but the content of that array changed: the variables `A` and `x` were distinct bindings refering to the same mutable `Array` object. @@ -235,18 +235,18 @@ ERROR: DomainError with -5: Cannot raise an integer x to a negative power -5. Make x a float by adding a zero decimal (e.g., 2.0^-5 instead of 2^-5), or write 1/x^5, float(x)^-5, or (x//1)^-5 Stacktrace: - [1] throw_domerr_powbysq(::Int64) at ./intfuncs.jl:163 - [2] power_by_squaring at ./intfuncs.jl:178 [inlined] - [3] ^ at ./intfuncs.jl:202 [inlined] - [4] literal_pow(::Base.#^, ::Int64, ::Val{-5}) at ./intfuncs.jl:213 + [1] throw_domerr_powbysq(::Int64) at ./intfuncs.jl:164 + [2] power_by_squaring at ./intfuncs.jl:179 [inlined] + [3] ^ at ./intfuncs.jl:203 [inlined] + [4] literal_pow(::Base.#^, ::Int64, ::Val{-5}) at ./intfuncs.jl:214 ``` This behavior is an inconvenient consequence of the requirement for type-stability. In the case -of [`sqrt()`](@ref), most users want `sqrt(2.0)` to give a real number, and would be unhappy if -it produced the complex number `1.4142135623730951 + 0.0im`. One could write the [`sqrt()`](@ref) +of [`sqrt`](@ref), most users want `sqrt(2.0)` to give a real number, and would be unhappy if +it produced the complex number `1.4142135623730951 + 0.0im`. One could write the [`sqrt`](@ref) function to switch to a complex-valued output only when passed a negative number (which is what -[`sqrt()`](@ref) does in some other languages), but then the result would not be [type-stable](@ref man-type-stability) -and the [`sqrt()`](@ref) function would have poor performance. +[`sqrt`](@ref) does in some other languages), but then the result would not be [type-stable](@ref man-type-stability) +and the [`sqrt`](@ref) function would have poor performance. In these and other cases, you can get the result you want by choosing an *input type* that conveys your willingness to accept an *output type* in which the result can be represented: diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 2f417d9466596..89d8387d945ac 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -143,7 +143,7 @@ julia> +(1,2,3) The infix form is exactly equivalent to the function application form -- in fact the former is parsed to produce the function call internally. This also means that you can assign and pass around -operators such as [`+()`](@ref) and [`*()`](@ref) just like you would with other function values: +operators such as [`+`](@ref) and [`*`](@ref) just like you would with other function values: ```jldoctest julia> f = +; @@ -160,14 +160,14 @@ A few special expressions correspond to calls to functions with non-obvious name | Expression | Calls | |:----------------- |:---------------------- | -| `[A B C ...]` | [`hcat()`](@ref) | -| `[A; B; C; ...]` | [`vcat()`](@ref) | -| `[A B; C D; ...]` | [`hvcat()`](@ref) | -| `A'` | [`ctranspose()`](@ref) | -| `A.'` | [`transpose()`](@ref) | -| `1:n` | [`colon()`](@ref) | -| `A[i]` | [`getindex()`](@ref) | -| `A[i]=x` | [`setindex!()`](@ref) | +| `[A B C ...]` | [`hcat`](@ref) | +| `[A; B; C; ...]` | [`vcat`](@ref) | +| `[A B; C D; ...]` | [`hvcat`](@ref) | +| `A'` | [`adjoint`](@ref) | +| `A.'` | [`transpose`](@ref) | +| `1:n` | [`colon`](@ref) | +| `A[i]` | [`getindex`](@ref) | +| `A[i]=x` | [`setindex!`](@ref) | ## [Anonymous Functions](@id man-anonymous-functions) @@ -192,7 +192,7 @@ This creates a function taking one argument `x` and returning the value of the p name based on consecutive numbering. The primary use for anonymous functions is passing them to functions which take other functions -as arguments. A classic example is [`map()`](@ref), which applies a function to each value of +as arguments. A classic example is [`map`](@ref), which applies a function to each value of an array and returns a new array containing the resulting values: ```jldoctest @@ -204,7 +204,7 @@ julia> map(round, [1.2,3.5,1.7]) ``` This is fine if a named function effecting the transform already exists to pass as the first argument -to [`map()`](@ref). Often, however, a ready-to-use, named function does not exist. In these +to [`map`](@ref). Often, however, a ready-to-use, named function does not exist. In these situations, the anonymous function construct allows easy creation of a single-use function object without needing a name: @@ -219,7 +219,7 @@ julia> map(x -> x^2 + 2x - 1, [1,3,-1]) An anonymous function accepting multiple arguments can be written using the syntax `(x,y,z)->2x+y-z`. A zero-argument anonymous function is written as `()->3`. The idea of a function with no arguments may seem strange, but is useful for "delaying" a computation. In this usage, a block of code is -wrapped in a zero-argument function, which is later invoked by calling it as `f()`. +wrapped in a zero-argument function, which is later invoked by calling it as `f`. ## Multiple Return Values @@ -267,6 +267,25 @@ end This has the exact same effect as the previous definition of `foo`. +## Argument destructuring + +The destructuring feature can also be used within a function argument. +If a function argument name is written as a tuple (e.g. `(x, y)`) instead of just +a symbol, then an assignment `(x, y) = argument` will be inserted for you: + +```julia +julia> minmax(x, y) = (y < x) ? (y, x) : (x, y) + +julia> range((min, max)) = max - min + +julia> range(minmax(10, 2)) +8 +``` + +Notice the extra set of parentheses in the definition of `range`. +Without those, `range` would be a two-argument function, and this example would +not work. + ## Varargs Functions It is often convenient to be able to write functions taking an arbitrary number of arguments. @@ -488,7 +507,7 @@ the `b` in `a=b` refers to a `b` in an outer scope, not the subsequent argument Passing functions as arguments to other functions is a powerful technique, but the syntax for it is not always convenient. Such calls are especially awkward to write when the function argument -requires multiple lines. As an example, consider calling [`map()`](@ref) on a function with several +requires multiple lines. As an example, consider calling [`map`](@ref) on a function with several cases: ```julia @@ -519,16 +538,16 @@ end ``` The `do x` syntax creates an anonymous function with argument `x` and passes it as the first argument -to [`map()`](@ref). Similarly, `do a,b` would create a two-argument anonymous function, and a +to [`map`](@ref). Similarly, `do a,b` would create a two-argument anonymous function, and a plain `do` would declare that what follows is an anonymous function of the form `() -> ...`. -How these arguments are initialized depends on the "outer" function; here, [`map()`](@ref) will +How these arguments are initialized depends on the "outer" function; here, [`map`](@ref) will sequentially set `x` to `A`, `B`, `C`, calling the anonymous function on each, just as would happen in the syntax `map(func, [A, B, C])`. This syntax makes it easier to use functions to effectively extend the language, since calls look -like normal code blocks. There are many possible uses quite different from [`map()`](@ref), such -as managing system state. For example, there is a version of [`open()`](@ref) that runs code ensuring +like normal code blocks. There are many possible uses quite different from [`map`](@ref), such +as managing system state. For example, there is a version of [`open`](@ref) that runs code ensuring that the opened file is eventually closed: ```julia @@ -550,8 +569,8 @@ function open(f::Function, args...) end ``` -Here, [`open()`](@ref) first opens the file for writing and then passes the resulting output stream -to the anonymous function you defined in the `do ... end` block. After your function exits, [`open()`](@ref) +Here, [`open`](@ref) first opens the file for writing and then passes the resulting output stream +to the anonymous function you defined in the `do ... end` block. After your function exits, [`open`](@ref) will make sure that the stream is properly closed, regardless of whether your function exited normally or threw an exception. (The `try/finally` construct will be described in [Control Flow](@ref).) diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index d41d6b1bdfa03..aaafc82a2b769 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -114,7 +114,7 @@ julia [switches] -- [programfile] [args...] --machinefile Run processes on hosts listed in -i Interactive mode; REPL runs and isinteractive() is true - -q, --quiet Quiet startup (no banner) + --banner={yes|no} Enable or disable startup banner --color={yes|no} Enable or disable color text --history-file={yes|no} Load or save history @@ -127,6 +127,7 @@ julia [switches] -- [programfile] [args...] --math-mode={ieee,fast} Disallow or enable unsafe floating point optimizations (overrides @fastmath declaration) --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings ("error" turns warnings into errors) + --warn-overwrite={yes|no} Enable or disable method overwrite warnings --output-o name Generate an object file (including system image data) --output-ji name Generate a system image data file (.ji) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 1b819938cc625..59a3961ce837e 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -162,7 +162,7 @@ UInt8 ``` The minimum and maximum representable values of primitive numeric types such as integers are given -by the [`typemin()`](@ref) and [`typemax()`](@ref) functions: +by the [`typemin`](@ref) and [`typemax`](@ref) functions: ```jldoctest julia> (typemin(Int32), typemax(Int32)) @@ -183,7 +183,7 @@ julia> for T in [Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt12 UInt128: [0,340282366920938463463374607431768211455] ``` -The values returned by [`typemin()`](@ref) and [`typemax()`](@ref) are always of the given argument +The values returned by [`typemin`](@ref) and [`typemax`](@ref) are always of the given argument type. (The above expression uses several features we have yet to introduce, including [for loops](@ref man-loops), [Strings](@ref man-strings), and [Interpolation](@ref), but should be easy enough to understand for users with some existing programming experience.) @@ -212,7 +212,7 @@ is recommended instead. ### Division errors Integer division (the `div` function) has two exceptional cases: dividing by zero, and dividing -the lowest negative number ([`typemin()`](@ref)) by -1. Both of these cases throw a [`DivideError`](@ref). +the lowest negative number ([`typemin`](@ref)) by -1. Both of these cases throw a [`DivideError`](@ref). The remainder and modulus functions (`rem` and `mod`) throw a [`DivideError`](@ref) when their second argument is zero. @@ -371,7 +371,7 @@ julia> 0 * Inf NaN ``` -The [`typemin()`](@ref) and [`typemax()`](@ref) functions also apply to floating-point types: +The [`typemin`](@ref) and [`typemax`](@ref) functions also apply to floating-point types: ```jldoctest julia> (typemin(Float16),typemax(Float16)) @@ -390,7 +390,7 @@ Most real numbers cannot be represented exactly with floating-point numbers, and it is important to know the distance between two adjacent representable floating-point numbers, which is often known as [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon). -Julia provides [`eps()`](@ref), which gives the distance between `1.0` and the next larger representable +Julia provides [`eps`](@ref), which gives the distance between `1.0` and the next larger representable floating-point value: ```jldoctest @@ -405,7 +405,7 @@ julia> eps() # same as eps(Float64) ``` These values are `2.0^-23` and `2.0^-52` as [`Float32`](@ref) and [`Float64`](@ref) values, -respectively. The [`eps()`](@ref) function can also take a floating-point value as an +respectively. The [`eps`](@ref) function can also take a floating-point value as an argument, and gives the absolute difference between that value and the next representable floating point value. That is, `eps(x)` yields a value of the same type as `x` such that `x + eps(x)` is the next representable floating-point value larger than `x`: @@ -430,7 +430,7 @@ numbers are densest in the real number line near zero, and grow sparser exponent farther away from zero. By definition, `eps(1.0)` is the same as `eps(Float64)` since `1.0` is a 64-bit floating-point value. -Julia also provides the [`nextfloat()`](@ref) and [`prevfloat()`](@ref) functions which return +Julia also provides the [`nextfloat`](@ref) and [`prevfloat`](@ref) functions which return the next largest or smallest representable floating-point number to the argument respectively: ```jldoctest @@ -478,8 +478,8 @@ The default mode used is always [`RoundNearest`](@ref), which rounds to the near value, with ties rounded towards the nearest value with an even least significant bit. !!! warning - Rounding is generally only correct for basic arithmetic functions ([`+()`](@ref), [`-()`](@ref), - [`*()`](@ref), [`/()`](@ref) and [`sqrt()`](@ref)) and type conversion operations. Many other + Rounding is generally only correct for basic arithmetic functions ([`+`](@ref), [`-`](@ref), + [`*`](@ref), [`/`](@ref) and [`sqrt`](@ref)) and type conversion operations. Many other functions assume the default [`RoundNearest`](@ref) mode is set, and can give erroneous results when operating under other rounding modes. @@ -511,7 +511,7 @@ the [GNU Multiple Precision Arithmetic Library (GMP)](https://gmplib.org) and th respectively. The [`BigInt`](@ref) and [`BigFloat`](@ref) types are available in Julia for arbitrary precision integer and floating point numbers respectively. -Constructors exist to create these types from primitive numerical types, and [`parse()`](@ref) +Constructors exist to create these types from primitive numerical types, and [`parse`](@ref) can be used to construct them from `AbstractString`s. Once created, they participate in arithmetic with all other numeric types thanks to Julia's [type promotion and conversion mechanism](@ref conversion-and-promotion): @@ -556,7 +556,7 @@ BigInt ``` The default precision (in number of bits of the significand) and rounding mode of [`BigFloat`](@ref) -operations can be changed globally by calling [`setprecision()`](@ref) and [`setrounding()`](@ref), +operations can be changed globally by calling [`setprecision`](@ref) and [`setrounding`](@ref), and all further calculations will take these changes in account. Alternatively, the precision or the rounding can be changed only within the execution of a particular block of code by using the same functions with a `do` block: diff --git a/doc/src/manual/interacting-with-julia.md b/doc/src/manual/interacting-with-julia.md index 515a640dad5d8..40b68bf8221b3 100644 --- a/doc/src/manual/interacting-with-julia.md +++ b/doc/src/manual/interacting-with-julia.md @@ -139,41 +139,49 @@ control-key, there are also meta-key bindings. These vary more by platform, but default to using alt- or option- held down with a key to send the meta-key (or can be configured to do so). -| Keybinding | Description | -|:------------------- |:------------------------------------------------------------------------------------------ | -| **Program control** |   | -| `^D` | Exit (when buffer is empty) | -| `^C` | Interrupt or cancel | -| `^L` | Clear console screen | -| Return/Enter, `^J` | New line, executing if it is complete | -| meta-Return/Enter | Insert new line without executing it | -| `?` or `;` | Enter help or shell mode (when at start of a line) | -| `^R`, `^S` | Incremental history search, described above | -| **Cursor movement** |   | -| Right arrow, `^F` | Move right one character | -| Left arrow, `^B` | Move left one character | -| Home, `^A` | Move to beginning of line | -| End, `^E` | Move to end of line | -| `^P` | Change to the previous or next history entry | -| `^N` | Change to the next history entry | -| Up arrow | Move up one line (or to the previous history entry) | -| Down arrow | Move down one line (or to the next history entry) | -| Page-up | Change to the previous history entry that matches the text before the cursor | -| Page-down | Change to the next history entry that matches the text before the cursor | -| `meta-F` | Move right one word | -| `meta-B` | Move left one word | -| `meta-<` | Change to the first history entry | -| `meta->` | Change to the last history entry | -| **Editing** |   | -| Backspace, `^H` | Delete the previous character | -| Delete, `^D` | Forward delete one character (when buffer has text) | -| meta-Backspace | Delete the previous word | -| `meta-D` | Forward delete the next word | -| `^W` | Delete previous text up to the nearest whitespace | -| `^K` | "Kill" to end of line, placing the text in a buffer | -| `^Y` | "Yank" insert the text from the kill buffer | -| `^T` | Transpose the characters about the cursor | -| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | +| Keybinding | Description | +|:------------------- |:---------------------------------------------------------------------------------------------------------- | +| **Program control** |   | +| `^D` | Exit (when buffer is empty) | +| `^C` | Interrupt or cancel | +| `^L` | Clear console screen | +| Return/Enter, `^J` | New line, executing if it is complete | +| meta-Return/Enter | Insert new line without executing it | +| `?` or `;` | Enter help or shell mode (when at start of a line) | +| `^R`, `^S` | Incremental history search, described above | +| **Cursor movement** |   | +| Right arrow, `^F` | Move right one character | +| Left arrow, `^B` | Move left one character | +| ctrl-Right, `meta-F`| Move right one word | +| ctrl-Left, `meta-B` | Move left one word | +| Home, `^A` | Move to beginning of line | +| End, `^E` | Move to end of line | +| Up arrow, `^P` | Move up one line (or change to the previous history entry that matches the text before the cursor) | +| Down arrow, `^N` | Move down one line (or change to the next history entry that matches the text before the cursor) | +| Page-up, `meta-P` | Change to the previous history entry | +| Page-down, `meta-N` | Change to the next history entry | +| `meta-<` | Change to the first history entry (of the current session if it is before the current position in history) | +| `meta->` | Change to the last history entry | +| `^-Space` | Set the "mark" in the editing region | +| `^X^X` | Exchange the current position with the mark | +| **Editing** |   | +| Backspace, `^H` | Delete the previous character | +| Delete, `^D` | Forward delete one character (when buffer has text) | +| meta-Backspace | Delete the previous word | +| `meta-d` | Forward delete the next word | +| `^W` | Delete previous text up to the nearest whitespace | +| `meta-w` | Copy the current region in the kill ring | +| `meta-W` | "Kill" the current region, placing the text in the kill ring | +| `^K` | "Kill" to end of line, placing the text in the kill ring | +| `^Y` | "Yank" insert the text from the kill ring | +| `meta-y` | Replace a previously yanked text with an older entry from the kill ring | +| `^T` | Transpose the characters about the cursor | +| `meta-u` | Change the next word to uppercase | +| `meta-c` | Change the next word to titlecase | +| `meta-l` | Change the next word to lowercase | +| `^/`, `^_` | Undo previous editing action | +| `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | + ### Customizing keybindings diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 3b679db3d8aa8..f321efb204aac 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -31,7 +31,7 @@ to generically build upon those behaviors. | `HasEltype()` | `eltype(IterType)` | | `EltypeUnknown()` | (*none*) | -Sequential iteration is implemented by the methods [`start()`](@ref), [`done()`](@ref), and [`next()`](@ref). Instead +Sequential iteration is implemented by the methods [`start`](@ref), [`done`](@ref), and [`next`](@ref). Instead of mutating objects as they are iterated over, Julia provides these three methods to keep track of the iteration state externally from the object. The `start(iter)` method returns the initial state for the iterable object `iter`. That state gets passed along to `done(iter, state)`, which @@ -92,7 +92,7 @@ julia> for i in Squares(7) 49 ``` -We can use many of the builtin methods that work with iterables, like [`in()`](@ref), [`mean()`](@ref) and [`std()`](@ref): +We can use many of the builtin methods that work with iterables, like [`in`](@ref), [`mean`](@ref) and [`std`](@ref): ```jldoctest squaretype julia> 25 in Squares(10) @@ -107,11 +107,11 @@ julia> std(Squares(100)) There are a few more methods we can extend to give Julia more information about this iterable collection. We know that the elements in a `Squares` sequence will always be `Int`. By extending -the [`eltype()`](@ref) method, we can give that information to Julia and help it make more specialized +the [`eltype`](@ref) method, we can give that information to Julia and help it make more specialized code in the more complicated methods. We also know the number of elements in our sequence, so -we can extend [`length()`](@ref), too. +we can extend [`length`](@ref), too. -Now, when we ask Julia to [`collect()`](@ref) all the elements into an array it can preallocate a `Vector{Int}` +Now, when we ask Julia to [`collect`](@ref) all the elements into an array it can preallocate a `Vector{Int}` of the right size instead of blindly [`push!`](@ref)ing each element into a `Vector{Any}`: ```jldoctest squaretype @@ -146,7 +146,7 @@ be used in their specific case. For the `Squares` iterable above, we can easily compute the `i`th element of the sequence by squaring it. We can expose this as an indexing expression `S[i]`. To opt into this behavior, `Squares` -simply needs to define [`getindex()`](@ref): +simply needs to define [`getindex`](@ref): ```jldoctest squaretype julia> function Base.getindex(S::Squares, i::Int) @@ -158,7 +158,7 @@ julia> Squares(100)[23] 529 ``` -Additionally, to support the syntax `S[end]`, we must define [`endof()`](@ref) to specify the last valid +Additionally, to support the syntax `S[end]`, we must define [`endof`](@ref) to specify the last valid index: ```jldoctest squaretype @@ -168,7 +168,7 @@ julia> Squares(23)[end] 529 ``` -Note, though, that the above *only* defines [`getindex()`](@ref) with one integer index. Indexing with +Note, though, that the above *only* defines [`getindex`](@ref) with one integer index. Indexing with anything other than an `Int` will throw a [`MethodError`](@ref) saying that there was no matching method. In order to support indexing with ranges or vectors of `Int`s, separate methods must be written: @@ -191,27 +191,27 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref ## [Abstract Arrays](@id man-interface-array) -| Methods to implement |   | Brief description | -|:----------------------------------------------- |:---------------------------------------- |:------------------------------------------------------------------------------------- | -| `size(A)` |   | Returns a tuple containing the dimensions of `A` | -| `getindex(A, i::Int)` |   | (if `IndexLinear`) Linear scalar indexing | -| `getindex(A, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexing | -| `setindex!(A, v, i::Int)` |   | (if `IndexLinear`) Scalar indexed assignment | -| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | -| **Optional methods** | **Default definition** | **Brief description** | -| `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | -| `getindex(A, I...)` | defined in terms of scalar `getindex()` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | -| `setindex!(A, I...)` | defined in terms of scalar `setindex!()` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | -| `start()`/`next()`/`done()` | defined in terms of scalar `getindex()` | Iteration | -| `length(A)` | `prod(size(A))` | Number of elements | -| `similar(A)` | `similar(A, eltype(A), size(A))` | Return a mutable array with the same shape and element type | -| `similar(A, ::Type{S})` | `similar(A, S, size(A))` | Return a mutable array with the same shape and the specified element type | -| `similar(A, dims::NTuple{Int})` | `similar(A, eltype(A), dims)` | Return a mutable array with the same element type and size *dims* | -| `similar(A, ::Type{S}, dims::NTuple{Int})` | `Array{S}(dims)` | Return a mutable array with the specified element type and size | -| **Non-traditional indices** | **Default definition** | **Brief description** | -| `indices(A)` | `map(OneTo, size(A))` | Return the `AbstractUnitRange` of valid indices | -| `Base.similar(A, ::Type{S}, inds::NTuple{Ind})` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | -| `Base.similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | +| Methods to implement |   | Brief description | +|:----------------------------------------------- |:-------------------------------------- |:------------------------------------------------------------------------------------- | +| `size(A)` |   | Returns a tuple containing the dimensions of `A` | +| `getindex(A, i::Int)` |   | (if `IndexLinear`) Linear scalar indexing | +| `getindex(A, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexing | +| `setindex!(A, v, i::Int)` |   | (if `IndexLinear`) Scalar indexed assignment | +| `setindex!(A, v, I::Vararg{Int, N})` |   | (if `IndexCartesian`, where `N = ndims(A)`) N-dimensional scalar indexed assignment | +| **Optional methods** | **Default definition** | **Brief description** | +| `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | +| `getindex(A, I...)` | defined in terms of scalar `getindex` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | +| `setindex!(A, I...)` | defined in terms of scalar `setindex!` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | +| `start`/`next`/`done` | defined in terms of scalar `getindex` | Iteration | +| `length(A)` | `prod(size(A))` | Number of elements | +| `similar(A)` | `similar(A, eltype(A), size(A))` | Return a mutable array with the same shape and element type | +| `similar(A, ::Type{S})` | `similar(A, S, size(A))` | Return a mutable array with the same shape and the specified element type | +| `similar(A, dims::NTuple{Int})` | `similar(A, eltype(A), dims)` | Return a mutable array with the same element type and size *dims* | +| `similar(A, ::Type{S}, dims::NTuple{Int})` | `Array{S}(dims)` | Return a mutable array with the specified element type and size | +| **Non-traditional indices** | **Default definition** | **Brief description** | +| `indices(A)` | `map(OneTo, size(A))` | Return the `AbstractUnitRange` of valid indices | +| `Base.similar(A, ::Type{S}, inds::NTuple{Ind})` | `similar(A, S, Base.to_shape(inds))` | Return a mutable array with the specified indices `inds` (see below) | +| `Base.similar(T::Union{Type,Function}, inds)` | `T(Base.to_shape(inds))` | Return an array similar to `T` with the specified indices `inds` (see below) | If a type is defined as a subtype of `AbstractArray`, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See @@ -233,7 +233,7 @@ efficiently converts the indices into one linear index and then calls the above arrays, on the other hand, require methods to be defined for each supported dimensionality with `ndims(A)` `Int` indices. For example, the built-in [`SparseMatrixCSC`](@ref) type only supports two dimensions, so it just defines -`getindex(A::SparseMatrixCSC, i::Int, j::Int)`. The same holds for `setindex!()`. +`getindex(A::SparseMatrixCSC, i::Int, j::Int)`. The same holds for `setindex!`. Returning to the sequence of squares from above, we could instead define it as a subtype of an `AbstractArray{Int, 1}`: @@ -251,7 +251,7 @@ julia> Base.getindex(S::SquaresVector, i::Int) = i*i ``` Note that it's very important to specify the two parameters of the `AbstractArray`; the first -defines the [`eltype()`](@ref), and the second defines the [`ndims()`](@ref). That supertype and those three +defines the [`eltype`](@ref), and the second defines the [`ndims`](@ref). That supertype and those three methods are all it takes for `SquaresVector` to be an iterable, indexable, and completely functional array: @@ -302,8 +302,8 @@ julia> Base.getindex(A::SparseArray{T,N}, I::Vararg{Int,N}) where {T,N} = get(A. julia> Base.setindex!(A::SparseArray{T,N}, v, I::Vararg{Int,N}) where {T,N} = (A.data[I] = v) ``` -Notice that this is an `IndexCartesian` array, so we must manually define [`getindex()`](@ref) and [`setindex!()`](@ref) -at the dimensionality of the array. Unlike the `SquaresVector`, we are able to define [`setindex!()`](@ref), +Notice that this is an `IndexCartesian` array, so we must manually define [`getindex`](@ref) and [`setindex!`](@ref) +at the dimensionality of the array. Unlike the `SquaresVector`, we are able to define [`setindex!`](@ref), and so we can mutate the array: ```jldoctest squarevectype @@ -327,7 +327,7 @@ julia> A[:] = 1:length(A); A ``` The result of indexing an `AbstractArray` can itself be an array (for instance when indexing by -a `Range`). The `AbstractArray` fallback methods use [`similar()`](@ref) to allocate an `Array` of the +a `Range`). The `AbstractArray` fallback methods use [`similar`](@ref) to allocate an `Array` of the appropriate size and element type, which is filled in using the basic indexing method described above. However, when implementing an array wrapper you often want the result to be wrapped as well: @@ -342,8 +342,8 @@ julia> A[1:2,:] In this example it is accomplished by defining `Base.similar{T}(A::SparseArray, ::Type{T}, dims::Dims)` to create the appropriate wrapped array. (Note that while `similar` supports 1- and 2-argument forms, in most case you only need to specialize the 3-argument form.) For this to work it's important -that `SparseArray` is mutable (supports `setindex!`). Defining `similar()`, `getindex()` and -`setindex!()` for `SparseArray` also makes it possible to [`copy()`](@ref) the array: +that `SparseArray` is mutable (supports `setindex!`). Defining `similar`, `getindex` and +`setindex!` for `SparseArray` also makes it possible to [`copy`](@ref) the array: ```jldoctest squarevectype julia> copy(A) diff --git a/doc/src/manual/linear-algebra.md b/doc/src/manual/linear-algebra.md index 8bb0e2101a8d6..8d895eac02873 100644 --- a/doc/src/manual/linear-algebra.md +++ b/doc/src/manual/linear-algebra.md @@ -175,17 +175,17 @@ as well as whether hooks to various optimized methods for them in LAPACK are ava ### Elementary operations -| Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | -|:------------------------- |:--- |:--- |:--- |:--- |:------------------------------------------------------------------- | -| [`Symmetric`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`expm()`](@ref) | -| [`Hermitian`](@ref) | | | | MV | [`inv()`](@ref), [`sqrtm()`](@ref), [`expm()`](@ref) | -| [`UpperTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | -| [`LowerTriangular`](@ref) | | | MV | MV | [`inv()`](@ref), [`det()`](@ref) | -| [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax()`](@ref), [`eigmin()`](@ref) | -| [`Tridiagonal`](@ref) | M | M | MS | MV | | -| [`Bidiagonal`](@ref) | M | M | MS | MV | | -| [`Diagonal`](@ref) | M | M | MV | MV | [`inv()`](@ref), [`det()`](@ref), [`logdet()`](@ref), [`/()`](@ref) | -| [`UniformScaling`](@ref) | M | M | MVS | MVS | [`/()`](@ref) | +| Matrix type | `+` | `-` | `*` | `\` | Other functions with optimized methods | +|:------------------------- |:--- |:--- |:--- |:--- |:----------------------------------------------------------- | +| [`Symmetric`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`exp`](@ref) | +| [`Hermitian`](@ref) | | | | MV | [`inv`](@ref), [`sqrt`](@ref), [`exp`](@ref) | +| [`UpperTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref) | +| [`LowerTriangular`](@ref) | | | MV | MV | [`inv`](@ref), [`det`](@ref) | +| [`SymTridiagonal`](@ref) | M | M | MS | MV | [`eigmax`](@ref), [`eigmin`](@ref) | +| [`Tridiagonal`](@ref) | M | M | MS | MV | | +| [`Bidiagonal`](@ref) | M | M | MS | MV | | +| [`Diagonal`](@ref) | M | M | MV | MV | [`inv`](@ref), [`det`](@ref), [`logdet`](@ref), [`/`](@ref) | +| [`UniformScaling`](@ref) | M | M | MVS | MVS | [`/`](@ref) | Legend: @@ -197,16 +197,16 @@ Legend: ### Matrix factorizations -| Matrix type | LAPACK | [`eig()`](@ref) | [`eigvals()`](@ref) | [`eigvecs()`](@ref) | [`svd()`](@ref) | [`svdvals()`](@ref) | -|:------------------------- |:------ |:--------------- |:------------------- |:------------------- |:--------------- |:------------------- | -| [`Symmetric`](@ref) | SY |   | ARI |   |   |   | -| [`Hermitian`](@ref) | HE |   | ARI |   |   |   | -| [`UpperTriangular`](@ref) | TR | A | A | A |   |   | -| [`LowerTriangular`](@ref) | TR | A | A | A |   |   | -| [`SymTridiagonal`](@ref) | ST | A | ARI | AV |   |   | -| [`Tridiagonal`](@ref) | GT |   |   |   |   |   | -| [`Bidiagonal`](@ref) | BD |   |   |   | A | A | -| [`Diagonal`](@ref) | DI |   | A |   |   |   | +| Matrix type | LAPACK | [`eig`](@ref) | [`eigvals`](@ref) | [`eigvecs`](@ref) | [`svd`](@ref) | [`svdvals`](@ref) | +|:------------------------- |:------ |:------------- |:----------------- |:----------------- |:------------- |:----------------- | +| [`Symmetric`](@ref) | SY | | ARI | | | | +| [`Hermitian`](@ref) | HE | | ARI | | | | +| [`UpperTriangular`](@ref) | TR | A | A | A | | | +| [`LowerTriangular`](@ref) | TR | A | A | A | | | +| [`SymTridiagonal`](@ref) | ST | A | ARI | AV | | | +| [`Tridiagonal`](@ref) | GT | | | | | | +| [`Bidiagonal`](@ref) | BD | | | | A | A | +| [`Diagonal`](@ref) | DI | | A | | | | Legend: diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 0311108131697..1f4fc3a3dddd2 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -266,7 +266,7 @@ situations like hash key comparisons: | [`isinf(x)`](@ref) | `x` is infinite | | [`isnan(x)`](@ref) | `x` is not a number | -[`isequal()`](@ref) considers `NaN`s equal to each other: +[`isequal`](@ref) considers `NaN`s equal to each other: ```jldoctest julia> isequal(NaN, NaN) @@ -279,7 +279,7 @@ julia> isequal(NaN, NaN32) true ``` -`isequal()` can also be used to distinguish signed zeros: +`isequal` can also be used to distinguish signed zeros: ```jldoctest julia> -0.0 == 0.0 @@ -292,9 +292,9 @@ false Mixed-type comparisons between signed integers, unsigned integers, and floats can be tricky. A great deal of care has been taken to ensure that Julia does them correctly. -For other types, `isequal()` defaults to calling [`==()`](@ref), so if you want to define -equality for your own types then you only need to add a [`==()`](@ref) method. If you define -your own equality function, you should probably define a corresponding [`hash()`](@ref) method +For other types, `isequal` defaults to calling [`==`](@ref), so if you want to define +equality for your own types then you only need to add a [`==`](@ref) method. If you define +your own equality function, you should probably define a corresponding [`hash`](@ref) method to ensure that `isequal(x,y)` implies `hash(x) == hash(y)`. ### Chaining comparisons @@ -462,7 +462,7 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define | [`cld(x,y)`](@ref) | ceiling division; quotient rounded towards `+Inf` | | [`rem(x,y)`](@ref) | remainder; satisfies `x == div(x,y)*y + rem(x,y)`; sign matches `x` | | [`mod(x,y)`](@ref) | modulus; satisfies `x == fld(x,y)*y + mod(x,y)`; sign matches `y` | -| [`mod1(x,y)`](@ref) | `mod()` with offset 1; returns `r∈(0,y]` for `y>0` or `r∈[y,0)` for `y<0`, where `mod(r, y) == mod(x, y)` | +| [`mod1(x,y)`](@ref) | `mod` with offset 1; returns `r∈(0,y]` for `y>0` or `r∈[y,0)` for `y<0`, where `mod(r, y) == mod(x, y)` | | [`mod2pi(x)`](@ref) | modulus with respect to 2pi; `0 <= mod2pi(x)   < 2pi` | | [`divrem(x,y)`](@ref) | returns `(div(x,y),rem(x,y))` | | [`fldmod(x,y)`](@ref) | returns `(fld(x,y),mod(x,y))` | @@ -498,7 +498,7 @@ See [Conversion and Promotion](@ref conversion-and-promotion) for how to define | [`exponent(x)`](@ref) | binary exponent of `x` | | [`significand(x)`](@ref) | binary significand (a.k.a. mantissa) of a floating-point number `x` | -For an overview of why functions like [`hypot()`](@ref), [`expm1()`](@ref), and [`log1p()`](@ref) +For an overview of why functions like [`hypot`](@ref), [`expm1`](@ref), and [`log1p`](@ref) are necessary and useful, see John D. Cook's excellent pair of blog posts on the subject: [expm1, log1p, erfc](https://www.johndcook.com/blog/2010/06/07/math-library-functions-that-seem-unnecessary/), and [hypot](https://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/). diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 8bb806c546cd7..9f38f38f8ee29 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -70,7 +70,7 @@ true **The key point here is that Julia code is internally represented as a data structure that is accessible from the language itself.** -The [`dump()`](@ref) function provides indented and annotated display of `Expr` objects: +The [`dump`](@ref) function provides indented and annotated display of `Expr` objects: ```jldoctest prog julia> dump(ex2) @@ -157,10 +157,10 @@ julia> typeof(ex) Expr ``` -(to view the structure of this expression, try `ex.head` and `ex.args`, or use [`dump()`](@ref) +(to view the structure of this expression, try `ex.head` and `ex.args`, or use [`dump`](@ref) as above or [`Meta.@dump`](@ref)) -Note that equivalent expressions may be constructed using [`parse()`](@ref) or the direct `Expr` +Note that equivalent expressions may be constructed using [`parse`](@ref) or the direct `Expr` form: ```jldoctest @@ -243,10 +243,10 @@ The use of `$` for expression interpolation is intentionally reminiscent of [str and [command interpolation](@ref command-interpolation). Expression interpolation allows convenient, readable programmatic construction of complex Julia expressions. -### [`eval()`](@ref) and effects +### [`eval`](@ref) and effects Given an expression object, one can cause Julia to evaluate (execute) it at global scope using -[`eval()`](@ref): +[`eval`](@ref): ```jldoctest interp1 julia> :(1 + 2) @@ -268,8 +268,8 @@ julia> eval(ex) 3 ``` -Every [module](@ref modules) has its own [`eval()`](@ref) function that evaluates expressions in its global -scope. Expressions passed to [`eval()`](@ref) are not limited to returning values -- they can +Every [module](@ref modules) has its own [`eval`](@ref) function that evaluates expressions in its global +scope. Expressions passed to [`eval`](@ref) are not limited to returning values -- they can also have side-effects that alter the state of the enclosing module's environment: ```jldoctest @@ -290,7 +290,7 @@ Here, the evaluation of an expression object causes a value to be assigned to th `x`. Since expressions are just `Expr` objects which can be constructed programmatically and then evaluated, -it is possible to dynamically generate arbitrary code which can then be run using [`eval()`](@ref). +it is possible to dynamically generate arbitrary code which can then be run using [`eval`](@ref). Here is a simple example: ```julia-repl @@ -320,7 +320,7 @@ value 1 and the variable `b`. Note the important distinction between the way `a` As hinted above, one extremely useful feature of Julia is the capability to generate and manipulate Julia code within Julia itself. We have already seen one example of a function returning `Expr` -objects: the [`parse()`](@ref) function, which takes a string of Julia code and returns the corresponding +objects: the [`parse`](@ref) function, which takes a string of Julia code and returns the corresponding `Expr`. A function can also take one or more `Expr` objects as arguments, and return another `Expr`. Here is a simple, motivating example: @@ -363,7 +363,7 @@ julia> eval(ex) Macros provide a method to include generated code in the final body of a program. A macro maps a tuple of arguments to a returned *expression*, and the resulting expression is compiled directly -rather than requiring a runtime [`eval()`](@ref) call. Macro arguments may include expressions, +rather than requiring a runtime [`eval`](@ref) call. Macro arguments may include expressions, literal values, and symbols. ### Basics @@ -410,7 +410,7 @@ julia> @sayhello("human") Hello, human ``` -We can view the quoted return expression using the function [`macroexpand()`](@ref) (**important note:** +We can view the quoted return expression using the function [`macroexpand`](@ref) (**important note:** this is an extremely useful tool for debugging macros): ```julia-repl sayhello2 @@ -433,7 +433,7 @@ julia> @macroexpand @sayhello "human" ### Hold up: why macros? -We have already seen a function `f(::Expr...) -> Expr` in a previous section. In fact, [`macroexpand()`](@ref) +We have already seen a function `f(::Expr...) -> Expr` in a previous section. In fact, [`macroexpand`](@ref) is also such a function. So, why do macros exist? Macros are necessary because they execute when code is parsed, therefore, macros allow the programmer @@ -451,7 +451,7 @@ julia> ex = macroexpand(Main, :(@twostep :(1, 2, 3)) ); I execute at parse time. The argument is: $(Expr(:quote, :((1, 2, 3)))) ``` -The first call to [`println()`](@ref) is executed when [`macroexpand()`](@ref) is called. The +The first call to [`println`](@ref) is executed when [`macroexpand`](@ref) is called. The resulting expression contains *only* the second `println`: ```julia-repl whymacros @@ -484,7 +484,7 @@ above; it passes the tuple `(expr1, expr2, ...)` as one argument to the macro: ``` It is important to emphasize that macros receive their arguments as expressions, literals, or -symbols. One way to explore macro arguments is to call the [`show()`](@ref) function within the +symbols. One way to explore macro arguments is to call the [`show`](@ref) function within the macro body: ```jldoctest @@ -610,7 +610,7 @@ There is yet another case that the actual `@assert` macro handles: what if, in a "a should equal b," we wanted to print their values? One might naively try to use string interpolation in the custom message, e.g., `@assert a==b "a ($a) should equal b ($b)!"`, but this won't work as expected with the above macro. Can you see why? Recall from [string interpolation](@ref string-interpolation) that -an interpolated string is rewritten to a call to [`string()`](@ref). Compare: +an interpolated string is rewritten to a call to [`string`](@ref). Compare: ```jldoctest julia> typeof(:("a should equal b")) @@ -633,7 +633,7 @@ Expr So now instead of getting a plain string in `msg_body`, the macro is receiving a full expression that will need to be evaluated in order to display as expected. This can be spliced directly into -the returned expression as an argument to the [`string()`](@ref) call; see [`error.jl`](https://github.com/JuliaLang/julia/blob/master/base/error.jl) +the returned expression as an argument to the [`string`](@ref) call; see [`error.jl`](https://github.com/JuliaLang/julia/blob/master/base/error.jl) for the complete implementation. The `@assert` macro makes great use of splicing into quoted expressions to simplify the manipulation @@ -670,7 +670,7 @@ end ``` Here, we want `t0`, `t1`, and `val` to be private temporary variables, and we want `time` to refer -to the [`time()`](@ref) function in the standard library, not to any `time` variable the user +to the [`time`](@ref) function in the standard library, not to any `time` variable the user might have (the same applies to `println`). Imagine the problems that could occur if the user expression `ex` also contained assignments to a variable called `t0`, or defined its own `time` variable. We might get errors, or mysteriously incorrect behavior. @@ -678,7 +678,7 @@ variable. We might get errors, or mysteriously incorrect behavior. Julia's macro expander solves these problems in the following way. First, variables within a macro result are classified as either local or global. A variable is considered local if it is assigned to (and not declared global), declared local, or used as a function argument name. Otherwise, -it is considered global. Local variables are then renamed to be unique (using the [`gensym()`](@ref) +it is considered global. Local variables are then renamed to be unique (using the [`gensym`](@ref) function, which generates new symbols), and global variables are resolved within the macro definition environment. Therefore both of the above concerns are handled; the macro's locals will not conflict with any user variables, and `time` and `println` will refer to the standard library definitions. @@ -697,7 +697,7 @@ end Here the user expression `ex` is a call to `time`, but not the same `time` function that the macro uses. It clearly refers to `MyModule.time`. Therefore we must arrange for the code in `ex` to -be resolved in the macro call environment. This is done by "escaping" the expression with [`esc()`](@ref): +be resolved in the macro call environment. This is done by "escaping" the expression with [`esc`](@ref): ```julia macro time(ex) @@ -763,7 +763,7 @@ while we want `@time` to be usable with minimum impact on the wrapped code. When a significant amount of repetitive boilerplate code is required, it is common to generate it programmatically to avoid redundancy. In most languages, this requires an extra build step, and a separate program to generate the repetitive code. In Julia, expression interpolation and -[`eval()`](@ref) allow such code generation to take place in the normal course of program execution. +[`eval`](@ref) allow such code generation to take place in the normal course of program execution. For example, the following code defines a series of operators on three arguments in terms of their 2-argument forms: @@ -984,9 +984,9 @@ performance optimization, so it is invalid to depend too closely on this behavio The number of times a generated function is generated *might* be only once, but it *might* also be more often, or appear to not happen at all. As a consequence, you should *never* write a generated function with side effects - when, and how often, the side effects occur is undefined. (This is -true for macros too - and just like for macros, the use of [`eval()`](@ref) in a generated function +true for macros too - and just like for macros, the use of [`eval`](@ref) in a generated function is a sign that you're doing something the wrong way.) However, unlike macros, the runtime system -cannot correctly handle a call to [`eval()`](@ref), so it is disallowed. +cannot correctly handle a call to [`eval`](@ref), so it is disallowed. It is also important to see how `@generated` functions interact with method redefinition. Following the principle that a correct `@generated` function must not observe any @@ -1119,7 +1119,7 @@ to build some more advanced (and valid) functionality... ### An advanced example -Julia's base library has a [`sub2ind()`](@ref) function to calculate a linear index into an n-dimensional +Julia's base library has a [`sub2ind`](@ref) function to calculate a linear index into an n-dimensional array, based on a set of n multilinear indices - in other words, to calculate the index `i` that can be used to index into an array `A` using `A[i]`, instead of `A[x,y,z,...]`. One possible implementation is the following: diff --git a/doc/src/manual/methods.md b/doc/src/manual/methods.md index 89f9be82bf159..3b1524c84c5fd 100644 --- a/doc/src/manual/methods.md +++ b/doc/src/manual/methods.md @@ -163,7 +163,7 @@ f (generic function with 2 methods) ``` This output tells us that `f` is a function object with two methods. To find out what the signatures -of those methods are, use the [`methods()`](@ref) function: +of those methods are, use the [`methods`](@ref) function: ```julia-repl julia> methods(f) @@ -453,7 +453,7 @@ This monotonically increasing value tracks each method definition operation. This allows describing "the set of method definitions visible to a given runtime environment" as a single number, or "world age". It also allows comparing the methods available in two worlds just by comparing their ordinal value. -In the example above, we see that the "current world" (in which the method `newfun()` exists), +In the example above, we see that the "current world" (in which the method `newfun` exists), is one greater than the task-local "runtime world" that was fixed when the execution of `tryeval` started. Sometimes it is necessary to get around this (for example, if you are implementing the above REPL). @@ -515,6 +515,250 @@ julia> wait(schedule(t, 1)) "definition for Int" ``` +## Design Patterns with Parametric Methods + + +While complex dispatch logic is not required for performance or usability, +sometimes it can be the best way to express some algorithm. +Here are a few common design patterns that come up sometimes when using dispatch in this way. + +### Extracting the type parameter from a super-type + + +Here is the correct code template for returning the element-type `T` +of any arbitrary subtype of `AbstractArray`: + +```julia +abstract type AbstractArray{T, N} end +eltype(::Type{<:AbstractArray{T}}) where {T} = T +``` +using so-called triangular dispatch. Note that if `T` is a `UnionAll` +type, as e.g. `eltype(Array{T} where T <: Integer)`, then `Any` is +returned (as does the the version of `eltype` in `Base`). + +Another way, which used to be the only correct way before the advent of +triangular dispatch in Julia v0.6, is: + +```julia +abstract type AbstractArray{T, N} end +eltype(::Type{AbstractArray}) = Any +eltype(::Type{AbstractArray{T}}) where {T} = T +eltype(::Type{AbstractArray{T, N}}) where {T, N} = T +eltype(::Type{A}) where {A<:AbstractArray} = eltype(supertype(A)) +``` + +Another possibility is the following, which could useful to adapt +to cases where the parameter `T` would need to be matched more +narrowly: +```julia +eltype(::Type{AbstractArray{T, N} where {T<:S, N<:M}}) where {M, S} = Any +eltype(::Type{AbstractArray{T, N} where {T<:S}}) where {N, S} = Any +eltype(::Type{AbstractArray{T, N} where {N<:M}}) where {M, T} = T +eltype(::Type{AbstractArray{T, N}}) where {T, N} = T +eltype(::Type{A}) where {A <: AbstractArray} = eltype(supertype(A)) +``` + + +One common mistake is to try and get the element-type by using introspection: + +```julia +eltype_wrong(::Type{A}) where {A<:AbstractArray} = A.parameters[1] +``` + +However, it is not hard to construct cases where this will fail: + +```julia +struct BitVector <: AbstractArray{Bool, 1}; end +``` + +Here we have created a type `BitVector` which has no parameters, +but where the element-type is still fully specified, with `T` equal to `Bool`! + + +### Building a similar type with a different type parameter + +When building generic code, there is often a need for constructing a similar +object with some change made to the layout of the type, also +necessitating a change of the type parameters. +For instance, you might have some sort of abstract array with an arbitrary element type +and want to write your computation on it with a specific element type. +We must implement a method for each `AbstractArray{T}` subtype that describes how to compute this type transform. +There is no general transform of one subtype into another subtype with a different parameter. +(Quick review: do you see why this is?) + +The subtypes of `AbstractArray` typically implement two methods to +achieve this: +A method to convert the input array to a subtype of a specific `AbstractArray{T, N}` abstract type; +and a method to make a new uninitialized array with a specific element type. +Sample implementations of these can be found in the standard library. +Here is a basic example usage of them, guaranteeing that `input` and +`output` are of the same type: + +```julia +input = convert(AbstractArray{Eltype}, input) +output = similar(input, Eltype) +``` + +As an extension of this, in cases where the algorithm needs a copy of +the input array, +[`convert`](@ref) is insufficient as the return value may alias the original input. +Combining [`similar`](@ref) (to make the output array) and [`copy!`](@ref) (to fill it with the input data) +is a generic way to express the requirement for a mutable copy of the input argument: + +```julia +copy_with_eltype(input, Eltype) = copy!(similar(input, Eltype), input) +``` + +### Iterated dispatch + +In order to dispatch a multi-level parametric argument list, +often it is best to separate each level of dispatch into distinct functions. +This may sound similar in approach to single-dispatch, but as we shall see below, it is still more flexible. + +For example, trying to dispatch on the element-type of an array will often run into ambiguous situations. +Instead, commonly code will dispatch first on the container type, +then recurse down to a more specific method based on eltype. +In most cases, the algorithms lend themselves conveniently to this hierarchical approach, +while in other cases, this rigor must be resolved manually. +This dispatching branching can be observed, for example, in the logic to sum two matrices: + +```julia +# First dispatch selects the map algorithm for element-wise summation. ++(a::Matrix, b::Matrix) = map(+, a, b) +# Then dispatch handles each element and selects the appropriate +# common element type for the computation. ++(a, b) = +(promote(a, b)...) +# Once the elements have the same type, they can be added. +# For example, via primitive operations exposed by the processor. ++(a::Float64, b::Float64) = Core.add(a, b) +``` + +### Trait-based dispatch + +A natural extension to the iterated dispatch above is to add a layer to +method selection that allows to dispatch on sets of types which are +independent from the sets defined by the type hierarchy. +We could construct such a set by writing out a `Union` of the types in question, +but then this set would not be extensible as `Union`-types cannot be +altered after creation. +However, such an extensible set can be programmed with a design pattern +often referred to as a +["Holy-trait"](https://github.com/JuliaLang/julia/issues/2345#issuecomment-54537633). + +This pattern is implemented by defining a generic function which +computes a different singleton value (or type) for each trait-set to which the +function arguments may belong to. If this function is pure there is +no impact on performance compared to normal dispatch. + +The example in the previous section glossed over the implementation details of +[`map`](@ref) and [`promote`](@ref), which both operate in terms of these traits. +When iterating over a matrix, such as in the implementation of `map`, +one important question is what order to use to traverse the data. +When `AbstractArray` subtypes implement the [`Base.IndexStyle`](@ref) trait, +other functions such as `map` can dispatch on this information to pick +the best algorithm (see [Abstract Array Interface](@ref man-interface-array)). +This means that each subtype does not need to implement a custom version of `map`, +since the generic definitions + trait classes will enable the system to select the fastest version. +Here a toy implementation of `map` illustrating the trait-based dispatch: + +```julia +map(f, a::AbstractArray, b::AbstractArray) = map(Base.IndexStyle(a, b), f, a, b) +# generic implementation: +map(::Base.IndexCartesian, f, a::AbstractArray, b::AbstractArray) = ... +# linear-indexing implementation (faster) +map(::Base.IndexLinear, f, a::AbstractArray, b::AbstractArray) = ... +``` + +This trait-based approach is also present in the [`promote`](@ref) +mechanism employed by the scalar `+`. +It uses [`promote_type`](@ref), which returns the optimal common type to +compute the operation given the two types of the operands. +This makes it possible to reduce the problem of implementing every function for every pair of possible type arguments, +to the much smaller problem of implementing a conversion operation from each type to a common type, +plus a table of preferred pair-wise promotion rules. + + +### Output-type computation + +The discussion of trait-based promotion provides a transition into our next design pattern: +computing the output element type for a matrix operation. + +For implementing primitive operations, such as addition, +we use the [`promote_type`](@ref) function to compute the desired output type. +(As before, we saw this at work in the `promote` call in the call to `+`). + +For more complex functions on matrices, it may be necessary to compute the expected return +type for a more complex sequence of operations. +This is often performed by the following steps: + +1. Write a small function `op` that expresses the set of operations performed by the kernel of the algorithm. +2. Compute the element type `R` of the result matrix as `promote_op(op, argument_types...)`, + where `argument_types` is computed from `eltype` applied to each input array. +3. Build the output matrix as `similar(R, dims)`, where `dims` are the desired dimensions of the output array. + +For a more specific example, a generic square-matrix multiply pseudo-code might look like: + +```julia +function matmul(a::AbstractMatrix, b::AbstractMatrix) + op = (ai, bi) -> ai * bi + ai * bi + + ## this is insufficient because it assumes `one(eltype(a))` is constructable: + # R = typeof(op(one(eltype(a)), one(eltype(b)))) + + ## this fails because it assumes `a[1]` exists and is representative of all elements of the array + # R = typeof(op(a[1], b[1])) + + ## this is incorrect because it assumes that `+` calls `promote_type` + ## but this is not true for some types, such as Bool: + # R = promote_type(ai, bi) + + # this is wrong, since depending on the return value + # of type-inference is very brittle (as well as not being optimizable): + # R = return_types(op, (eltype(a), eltype(b))) + + ## but, finally, this works: + R = promote_op(op, eltype(a), eltype(b)) + ## although sometimes it may give a larger type than desired + ## it will always give a correct type + + output = similar(b, R, (size(a, 1), size(b, 2))) + if size(a, 2) > 0 + for j in 1:size(b, 2) + for i in 1:size(b, 1) + ## here we don't use `ab = zero(R)`, + ## since `R` might be `Any` and `zero(Any)` is not defined + ## we also must declare `ab::R` to make the type of `ab` constant in the loop, + ## since it is possible that typeof(a * b) != typeof(a * b + a * b) == R + ab::R = a[i, 1] * b[1, j] + for k in 2:size(a, 2) + ab += a[i, k] * b[k, j] + end + output[i, j] = ab + end + end + end + return output +end +``` + +### Separate convert and kernel logic + +One way to significantly cut down on compile-times and testing complexity is to isolate +the logic for converting to the desired type and the computation. +This lets the compiler specialize and inline the conversion logic independent +from the rest of the body of the larger kernel. + +This is a common pattern seen when converting from a larger class of types +to the one specific argument type that is actually supported by the algorithm: + +```julia +complexfunction(arg::Int) = ... +complexfunction(arg::Any) = complexfunction(convert(Int, arg)) + +matmul(a::T, b::T) = ... +matmul(a, b) = matmul(promote(a, b)...) +``` + ## Parametrically-constrained Varargs methods Function parameters can also be used to constrain the number of arguments that may be supplied diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index 921f195c7a317..ee15f40288e50 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -384,7 +384,7 @@ A few other points to be aware of: It is sometimes helpful during module development to turn off incremental precompilation. The command line flag `--compilecache={yes|no}` enables you to toggle module precompilation on and off. When Julia is started with `--compilecache=no` the serialized modules in the compile cache -are ignored when loading modules and module dependencies. `Base.compilecache()` can still be called +are ignored when loading modules and module dependencies. `Base.compilecache` can still be called manually and it will respect `__precompile__()` directives for the module. The state of this command -line flag is passed to [`Pkg.build()`](@ref) to disable automatic precompilation triggering when installing, +line flag is passed to [`Pkg.build`](@ref) to disable automatic precompilation triggering when installing, updating, and explicitly building packages. diff --git a/doc/src/manual/networking-and-streams.md b/doc/src/manual/networking-and-streams.md index d2708a16bba69..2e5916c8f176f 100644 --- a/doc/src/manual/networking-and-streams.md +++ b/doc/src/manual/networking-and-streams.md @@ -8,7 +8,7 @@ functionality. ## Basic Stream I/O -All Julia streams expose at least a [`read()`](@ref) and a [`write()`](@ref) method, taking the +All Julia streams expose at least a [`read`](@ref) and a [`write`](@ref) method, taking the stream as their first argument, e.g.: ```julia-repl @@ -19,11 +19,11 @@ julia> read(STDIN,Char) '\n': ASCII/Unicode U+000a (category Cc: Other, control) ``` -Note that [`write()`](@ref) returns 11, the number of bytes (in `"Hello World"`) written to [`STDOUT`](@ref), +Note that [`write`](@ref) returns 11, the number of bytes (in `"Hello World"`) written to [`STDOUT`](@ref), but this return value is suppressed with the `;`. Here Enter was pressed again so that Julia would read the newline. Now, as you can see from this -example, [`write()`](@ref) takes the data to write as its second argument, while [`read()`](@ref) +example, [`write`](@ref) takes the data to write as its second argument, while [`read`](@ref) takes the type of the data to be read as the second argument. For example, to read a simple byte array, we could do: @@ -69,7 +69,7 @@ abcd Note that depending on your terminal settings, your TTY may be line buffered and might thus require an additional enter before the data is sent to Julia. -To read every line from [`STDIN`](@ref) you can use [`eachline()`](@ref): +To read every line from [`STDIN`](@ref) you can use [`eachline`](@ref): ```julia for line in eachline(STDIN) @@ -77,7 +77,7 @@ for line in eachline(STDIN) end ``` -or [`read()`](@ref) if you wanted to read by character instead: +or [`read`](@ref) if you wanted to read by character instead: ```julia while !eof(STDIN) @@ -88,7 +88,7 @@ end ## Text I/O -Note that the [`write()`](@ref) method mentioned above operates on binary streams. In particular, +Note that the [`write`](@ref) method mentioned above operates on binary streams. In particular, values do not get converted to any canonical text representation but are written out as is: ```jldoctest @@ -96,10 +96,10 @@ julia> write(STDOUT,0x61); # suppress return value 1 with ; a ``` -Note that `a` is written to [`STDOUT`](@ref) by the [`write()`](@ref) function and that the returned +Note that `a` is written to [`STDOUT`](@ref) by the [`write`](@ref) function and that the returned value is `1` (since `0x61` is one byte). -For text I/O, use the [`print()`](@ref) or [`show()`](@ref) methods, depending on your needs (see +For text I/O, use the [`print`](@ref) or [`show`](@ref) methods, depending on your needs (see the standard library reference for a detailed discussion of the difference between the two): ```jldoctest @@ -116,7 +116,7 @@ should print a shorter output (if applicable). ## Working with Files -Like many other environments, Julia has an [`open()`](@ref) function, which takes a filename and +Like many other environments, Julia has an [`open`](@ref) function, which takes a filename and returns an `IOStream` object that you can use to read and write things from the file. For example if we have a file, `hello.txt`, whose contents are `Hello, World!`: @@ -150,7 +150,7 @@ julia> close(f) Examining `hello.txt` again will show its contents have been changed. Opening a file, doing something to its contents, and closing it again is a very common pattern. -To make this easier, there exists another invocation of [`open()`](@ref) which takes a function +To make this easier, there exists another invocation of [`open`](@ref) which takes a function as its first argument and filename as its second, opens the file, calls the function with the file as an argument, and then closes it again. For example, given a function: @@ -196,7 +196,7 @@ Task (runnable) @0x00007fd31dc11ae0 ``` To those familiar with the Unix socket API, the method names will feel familiar, though their -usage is somewhat simpler than the raw Unix socket API. The first call to [`listen()`](@ref) will +usage is somewhat simpler than the raw Unix socket API. The first call to [`listen`](@ref) will create a server waiting for incoming connections on the specified port (2000) in this case. The same function may also be used to create various other kinds of servers: @@ -226,14 +226,14 @@ Base.PipeServer(active) Note that the return type of the last invocation is different. This is because this server does not listen on TCP, but rather on a named pipe (Windows) or UNIX domain socket. Also note that Windows named pipe format has to be a specific pattern such that the name prefix (`\\.\pipe\`) uniquely -identifies the [file type](https://msdn.microsoft.com/en- -us/library/windows/desktop/aa365783(v=vs.85).aspx). The difference between TCP and named pipes or -UNIX domain sockets is subtle and has to do with the [`accept()`](@ref) and [`connect()`](@ref) -methods. The [`accept()`](@ref) method retrieves a connection to the client that is connecting on -the server we just created, while the [`connect()`](@ref) function connects to a server using the -specified method. The [`connect()`](@ref) function takes the same arguments as [`listen()`](@ref), +identifies the [file type](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx). +The difference between TCP and named pipes or +UNIX domain sockets is subtle and has to do with the [`accept`](@ref) and [`connect`](@ref) +methods. The [`accept`](@ref) method retrieves a connection to the client that is connecting on +the server we just created, while the [`connect`](@ref) function connects to a server using the +specified method. The [`connect`](@ref) function takes the same arguments as [`listen`](@ref), so, assuming the environment (i.e. host, cwd, etc.) is the same you should be able to pass the same -arguments to [`connect()`](@ref) as you did to listen to establish the connection. So let's try that +arguments to [`connect`](@ref) as you did to listen to establish the connection. So let's try that out (after having created the server above): ```julia-repl @@ -244,13 +244,13 @@ julia> Hello World ``` As expected we saw "Hello World" printed. So, let's actually analyze what happened behind the -scenes. When we called [`connect()`](@ref), we connect to the server we had just created. Meanwhile, +scenes. When we called [`connect`](@ref), we connect to the server we had just created. Meanwhile, the accept function returns a server-side connection to the newly created socket and prints "Hello World" to indicate that the connection was successful. A great strength of Julia is that since the API is exposed synchronously even though the I/O is actually happening asynchronously, we didn't have to worry callbacks or even making sure that -the server gets to run. When we called [`connect()`](@ref) the current task waited for the connection +the server gets to run. When we called [`connect`](@ref) the current task waited for the connection to be established and only continued executing after that was done. In this pause, the server task resumed execution (because a connection request was now available), accepted the connection, printed the message and waited for the next client. Reading and writing works in the same way. @@ -280,7 +280,7 @@ julia> println(clientside,"Hello World from the Echo Server") Hello World from the Echo Server ``` -As with other streams, use [`close()`](@ref) to disconnect the socket: +As with other streams, use [`close`](@ref) to disconnect the socket: ```julia-repl julia> close(clientside) @@ -288,7 +288,7 @@ julia> close(clientside) ## Resolving IP Addresses -One of the [`connect()`](@ref) methods that does not follow the [`listen()`](@ref) methods is +One of the [`connect`](@ref) methods that does not follow the [`listen`](@ref) methods is `connect(host::String,port)`, which will attempt to connect to the host given by the `host` parameter on the port given by the port parameter. It allows you to do things like: @@ -297,7 +297,7 @@ julia> connect("google.com",80) TCPSocket(RawFD(30) open, 0 bytes waiting) ``` -At the base of this functionality is [`getaddrinfo()`](@ref), which will do the appropriate address +At the base of this functionality is [`getaddrinfo`](@ref), which will do the appropriate address resolution: ```julia-repl diff --git a/doc/src/manual/noteworthy-differences.md b/doc/src/manual/noteworthy-differences.md index c94e231252499..202dbf12224cd 100644 --- a/doc/src/manual/noteworthy-differences.md +++ b/doc/src/manual/noteworthy-differences.md @@ -14,7 +14,7 @@ may trip up Julia users accustomed to MATLAB: * Julia does not automatically grow arrays in an assignment statement. Whereas in MATLAB `a(4) = 3.2` can create the array `a = [0 0 0 3.2]` and `a(5) = 7` can grow it into `a = [0 0 0 3.2 7]`, the corresponding Julia statement `a[5] = 7` throws an error if the length of `a` is less than 5 or - if this statement is the first use of the identifier `a`. Julia has [`push!()`](@ref) and [`append!()`](@ref), + if this statement is the first use of the identifier `a`. Julia has [`push!`](@ref) and [`append!`](@ref), which grow `Vector`s much more efficiently than MATLAB's `a(end+1) = val`. * The imaginary unit `sqrt(-1)` is represented in Julia as [`im`](@ref), not `i` or `j` as in MATLAB. * In Julia, literal numbers without a decimal point (such as `42`) create integers instead of floating @@ -32,7 +32,7 @@ may trip up Julia users accustomed to MATLAB: with semicolons (`[x; y; z]`). - To concatenate in the second ("horizontal") dimension use either [`hcat(x,y,z)`](@ref) or separate with spaces (`[x y z]`). - - To construct block matrices (concatenating in the first two dimensions), use either [`hvcat()`](@ref) + - To construct block matrices (concatenating in the first two dimensions), use either [`hvcat`](@ref) or combine spaces and semicolons (`[a b; c d]`). * In Julia, `a:b` and `a:b:c` construct `Range` objects. To construct a full vector like in MATLAB, use [`collect(a:b)`](@ref). Generally, there is no need to call `collect` though. `Range` will @@ -46,17 +46,17 @@ may trip up Julia users accustomed to MATLAB: * A Julia script may contain any number of functions, and all definitions will be externally visible when the file is loaded. Function definitions can be loaded from files outside the current working directory. - * In Julia, reductions such as [`sum()`](@ref), [`prod()`](@ref), and [`max()`](@ref) are performed + * In Julia, reductions such as [`sum`](@ref), [`prod`](@ref), and [`max`](@ref) are performed over every element of an array when called with a single argument, as in `sum(A)`, even if `A` has more than one dimension. - * In Julia, functions such as [`sort()`](@ref) that operate column-wise by default (`sort(A)` is + * In Julia, functions such as [`sort`](@ref) that operate column-wise by default (`sort(A)` is equivalent to `sort(A,1)`) do not have special behavior for `1xN` arrays; the argument is returned unmodified since it still performs `sort(A,1)`. To sort a `1xN` matrix like a vector, use `sort(A,2)`. * In Julia, parentheses must be used to call a function with zero arguments, like in [`tic()`](@ref) and [`toc()`](@ref). * Julia discourages the used of semicolons to end statements. The results of statements are not automatically printed (except at the interactive prompt), and lines of code do not need to end - with semicolons. [`println()`](@ref) or [`@printf()`](@ref) can be used to print specific output. + with semicolons. [`println`](@ref) or [`@printf`](@ref) can be used to print specific output. * In Julia, if `A` and `B` are arrays, logical comparison operations like `A == B` do not return an array of booleans. Instead, use `A .== B`, and similarly for the other boolean operators like [`<`](@ref), [`>`](@ref) and `=`. @@ -67,7 +67,7 @@ may trip up Julia users accustomed to MATLAB: parentheses may be required (e.g., to select elements of `A` equal to 1 or 2 use `(A .== 1) | (A .== 2)`). * In Julia, the elements of a collection can be passed as arguments to a function using the splat operator `...`, as in `xs=[1,2]; f(xs...)`. - * Julia's [`svd()`](@ref) returns singular values as a vector instead of as a dense diagonal matrix. + * Julia's [`svd`](@ref) returns singular values as a vector instead of as a dense diagonal matrix. * In Julia, `...` is not used to continue lines of code. Instead, incomplete expressions automatically continue onto the next line. * In both Julia and MATLAB, the variable `ans` is set to the value of the last expression issued @@ -79,9 +79,9 @@ may trip up Julia users accustomed to MATLAB: scope. * In MATLAB, an idiomatic way to remove unwanted values is to use logical indexing, like in the expression `x(x>3)` or in the statement `x(x>3) = []` to modify `x` in-place. In contrast, Julia - provides the higher order functions [`filter()`](@ref) and [`filter!()`](@ref), allowing users + provides the higher order functions [`filter`](@ref) and [`filter!`](@ref), allowing users to write `filter(z->z>3, x)` and `filter!(z->z>3, x)` as alternatives to the corresponding transliterations - `x[x.>3]` and `x = x[x.>3]`. Using [`filter!()`](@ref) reduces the use of temporary arrays. + `x[x.>3]` and `x = x[x.>3]`. Using [`filter!`](@ref) reduces the use of temporary arrays. * The analogue of extracting (or "dereferencing") all elements of a cell array, e.g. in `vertcat(A{:})` in MATLAB, is written using the splat operator in Julia, e.g. as `vcat(A...)`. @@ -108,8 +108,8 @@ For users coming to Julia from R, these are some noteworthy differences: * Like many languages, Julia does not always allow operations on vectors of different lengths, unlike R where the vectors only need to share a common index range. For example, `c(1, 2, 3, 4) + c(1, 2)` is valid R but the equivalent `[1, 2, 3, 4] + [1, 2]` will throw an error in Julia. - * Julia's [`map()`](@ref) takes the function first, then its arguments, unlike `lapply(, function, ...)` - in R. Similarly Julia's equivalent of `apply(X, MARGIN, FUN, ...)` in R is [`mapslices()`](@ref) + * Julia's [`map`](@ref) takes the function first, then its arguments, unlike `lapply(, function, ...)` + in R. Similarly Julia's equivalent of `apply(X, MARGIN, FUN, ...)` in R is [`mapslices`](@ref) where the function is the first argument. * Multivariate apply in R, e.g. `mapply(choose, 11:13, 1:3)`, can be written as `broadcast(binomial, 11:13, 1:3)` in Julia. Equivalently Julia offers a shorter dot syntax for vectorizing functions `binomial.(11:13, 1:3)`. @@ -136,11 +136,11 @@ For users coming to Julia from R, these are some noteworthy differences: * Julia is careful to distinguish scalars, vectors and matrices. In R, `1` and `c(1)` are the same. In Julia, they can not be used interchangeably. One potentially confusing result of this is that `x' * y` for vectors `x` and `y` is a 1-element vector, not a scalar. To get a scalar, use [`dot(x, y)`](@ref). - * Julia's [`diag()`](@ref) and [`diagm()`](@ref) are not like R's. + * Julia's [`diag`](@ref) and [`diagm`](@ref) are not like R's. * Julia cannot assign to the results of function calls on the left hand side of an assignment operation: you cannot write `diag(M) = ones(n)`. * Julia discourages populating the main namespace with functions. Most statistical functionality - for Julia is found in [packages](http://pkg.julialang.org/) under the [JuliaStats organization](https://github.com/JuliaStats). + for Julia is found in [packages](https://pkg.julialang.org/) under the [JuliaStats organization](https://github.com/JuliaStats). For example: * Functions pertaining to probability distributions are provided by the [Distributions package](https://github.com/JuliaStats/Distributions.jl). @@ -154,15 +154,15 @@ For users coming to Julia from R, these are some noteworthy differences: * In Julia, values are passed and assigned by reference. If a function modifies an array, the changes will be visible in the caller. This is very different from R and allows new functions to operate on large data structures much more efficiently. - * In Julia, vectors and matrices are concatenated using [`hcat()`](@ref), [`vcat()`](@ref) and - [`hvcat()`](@ref), not `c`, `rbind` and `cbind` like in R. + * In Julia, vectors and matrices are concatenated using [`hcat`](@ref), [`vcat`](@ref) and + [`hvcat`](@ref), not `c`, `rbind` and `cbind` like in R. * In Julia, a range like `a:b` is not shorthand for a vector like in R, but is a specialized `Range` that is used for iteration without high memory overhead. To convert a range into a vector, use [`collect(a:b)`](@ref). - * Julia's [`max()`](@ref) and [`min()`](@ref) are the equivalent of `pmax` and `pmin` respectively - in R, but both arguments need to have the same dimensions. While [`maximum()`](@ref) and [`minimum()`](@ref) + * Julia's [`max`](@ref) and [`min`](@ref) are the equivalent of `pmax` and `pmin` respectively + in R, but both arguments need to have the same dimensions. While [`maximum`](@ref) and [`minimum`](@ref) replace `max` and `min` in R, there are important differences. - * Julia's [`sum()`](@ref), [`prod()`](@ref), [`maximum()`](@ref), and [`minimum()`](@ref) are different + * Julia's [`sum`](@ref), [`prod`](@ref), [`maximum`](@ref), and [`minimum`](@ref) are different from their counterparts in R. They all accept one or two arguments. The first argument is an iterable collection such as an array. If there is a second argument, then this argument indicates the dimensions, over which the operation is carried out. For instance, let `A=[[1 2],[3 4]]` in Julia @@ -172,8 +172,8 @@ For users coming to Julia from R, these are some noteworthy differences: `sum(B,1)=11` and `sum(B,2)=12`. If the second argument is a vector, then it specifies all the dimensions over which the sum is performed, e.g., `sum(A,[1,2])=10`. It should be noted that there is no error checking regarding the second argument. - * Julia has several functions that can mutate their arguments. For example, it has both [`sort()`](@ref) - and [`sort!()`](@ref). + * Julia has several functions that can mutate their arguments. For example, it has both [`sort`](@ref) + and [`sort!`](@ref). * In R, performance requires vectorization. In Julia, almost the opposite is true: the best performing code is often achieved by using devectorized loops. * Julia is eagerly evaluated and does not support R-style lazy evaluation. For most users, this @@ -183,9 +183,9 @@ For users coming to Julia from R, these are some noteworthy differences: * In Julia, `return` does not require parentheses. * In R, an idiomatic way to remove unwanted values is to use logical indexing, like in the expression `x[x>3]` or in the statement `x = x[x>3]` to modify `x` in-place. In contrast, Julia provides - the higher order functions [`filter()`](@ref) and [`filter!()`](@ref), allowing users to write + the higher order functions [`filter`](@ref) and [`filter!`](@ref), allowing users to write `filter(z->z>3, x)` and `filter!(z->z>3, x)` as alternatives to the corresponding transliterations - `x[x.>3]` and `x = x[x.>3]`. Using [`filter!()`](@ref) reduces the use of temporary arrays. + `x[x.>3]` and `x = x[x.>3]`. Using [`filter!`](@ref) reduces the use of temporary arrays. ## Noteworthy differences from Python @@ -257,7 +257,7 @@ For users coming to Julia from R, these are some noteworthy differences: C/C++ (i.e. `a = myfunction(&b)`. * Julia does not require the use of semicolons to end statements. The results of expressions are not automatically printed (except at the interactive prompt, i.e. the REPL), and lines of code - do not need to end with semicolons. [`println()`](@ref) or [`@printf()`](@ref) can be used to + do not need to end with semicolons. [`println`](@ref) or [`@printf`](@ref) can be used to print specific output. In the REPL, `;` can be used to suppress output. `;` also has a different meaning within `[ ]`, something to watch out for. `;` can be used to separate expressions on a single line, but are not strictly necessary in many cases, and are more an aid to readability. diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 0d31a561f149e..90e8c0b465b91 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -26,8 +26,8 @@ Remote references come in two flavors: [`Future`](@ref) and [`RemoteChannel`](@r A remote call returns a [`Future`](@ref) to its result. Remote calls return immediately; the process that made the call proceeds to its next operation while the remote call happens somewhere else. -You can wait for a remote call to finish by calling [`wait()`](@ref) on the returned [`Future`](@ref), -and you can obtain the full value of the result using [`fetch()`](@ref). +You can wait for a remote call to finish by calling [`wait`](@ref) on the returned [`Future`](@ref), +and you can obtain the full value of the result using [`fetch`](@ref). On the other hand, [`RemoteChannel`](@ref) s are rewritable. For example, multiple processes can co-ordinate their processing by referencing the same remote `Channel`. @@ -55,9 +55,9 @@ julia> fetch(s) 1.16296 1.60607 ``` -The first argument to [`remotecall()`](@ref) is the function to call. Most parallel programming -in Julia does not reference specific processes or the number of processes available, but [`remotecall()`](@ref) -is considered a low-level interface providing finer control. The second argument to [`remotecall()`](@ref) +The first argument to [`remotecall`](@ref) is the function to call. Most parallel programming +in Julia does not reference specific processes or the number of processes available, but [`remotecall`](@ref) +is considered a low-level interface providing finer control. The second argument to [`remotecall`](@ref) is the `id` of the process that will do the work, and the remaining arguments will be passed to the function being called. @@ -68,7 +68,7 @@ argument on the process specified by the first argument. Occasionally you might want a remotely-computed value immediately. This typically happens when you read from a remote object to obtain data needed by the next local operation. The function -[`remotecall_fetch()`](@ref) exists for this purpose. It is equivalent to `fetch(remotecall(...))` +[`remotecall_fetch`](@ref) exists for this purpose. It is equivalent to `fetch(remotecall(...))` but is more efficient. ```julia-repl @@ -79,7 +79,7 @@ julia> remotecall_fetch(getindex, 2, r, 1, 1) Remember that [`getindex(r,1,1)`](@ref) is [equivalent](@ref man-array-indexing) to `r[1,1]`, so this call fetches the first element of the future `r`. -The syntax of [`remotecall()`](@ref) is not especially convenient. The macro [`@spawn`](@ref) +The syntax of [`remotecall`](@ref) is not especially convenient. The macro [`@spawn`](@ref) makes things easier. It operates on an expression rather than a function, and picks where to do the operation for you: @@ -97,15 +97,15 @@ julia> fetch(s) ``` Note that we used `1 .+ fetch(r)` instead of `1 .+ r`. This is because we do not know where the -code will run, so in general a [`fetch()`](@ref) might be required to move `r` to the process +code will run, so in general a [`fetch`](@ref) might be required to move `r` to the process doing the addition. In this case, [`@spawn`](@ref) is smart enough to perform the computation -on the process that owns `r`, so the [`fetch()`](@ref) will be a no-op (no work is done). +on the process that owns `r`, so the [`fetch`](@ref) will be a no-op (no work is done). (It is worth noting that [`@spawn`](@ref) is not built-in but defined in Julia as a [macro](@ref man-macros). It is possible to define your own such constructs.) An important thing to remember is that, once fetched, a [`Future`](@ref) will cache its value -locally. Further [`fetch()`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref)s +locally. Further [`fetch`](@ref) calls do not entail a network hop. Once all referencing [`Future`](@ref)s have fetched, the remote stored value is deleted. ## Code Availability and Loading Packages @@ -192,7 +192,7 @@ The base Julia installation has in-built support for two types of clusters: * A cluster spanning machines using the `--machinefile` option. This uses a passwordless `ssh` login to start Julia worker processes (from the same path as the current host) on the specified machines. -Functions [`addprocs()`](@ref), [`rmprocs()`](@ref), [`workers()`](@ref), and others are available +Functions [`addprocs`](@ref), [`rmprocs`](@ref), [`workers`](@ref), and others are available as a programmatic means of adding, removing and querying the processes in a cluster. Note that workers do not run a `.juliarc.jl` startup script, nor do they synchronize their global @@ -209,7 +209,7 @@ the number of messages and the amount of data sent is critical to achieving perf To this end, it is important to understand the data movement performed by Julia's various parallel programming constructs. -[`fetch()`](@ref) can be considered an explicit data movement operation, since it directly asks +[`fetch`](@ref) can be considered an explicit data movement operation, since it directly asks that an object be moved to the local machine. [`@spawn`](@ref) (and a few related constructs) also moves data, but this is not as obvious, hence it can be called an implicit data movement operation. Consider these two approaches to constructing and squaring a random matrix: @@ -420,12 +420,12 @@ Here each iteration applies `f` to a randomly-chosen sample from a vector `a` sh As you could see, the reduction operator can be omitted if it is not needed. In that case, the loop executes asynchronously, i.e. it spawns independent tasks on all available workers and returns an array of [`Future`](@ref) immediately without waiting for completion. The caller can wait for -the [`Future`](@ref) completions at a later point by calling [`fetch()`](@ref) on them, or wait +the [`Future`](@ref) completions at a later point by calling [`fetch`](@ref) on them, or wait for completion at the end of the loop by prefixing it with [`@sync`](@ref), like `@sync @parallel for`. In some cases no reduction operator is needed, and we merely wish to apply a function to all integers in some range (or, more generally, to all elements in some collection). This is another useful -operation called *parallel map*, implemented in Julia as the [`pmap()`](@ref) function. For example, +operation called *parallel map*, implemented in Julia as the [`pmap`](@ref) function. For example, we could compute the singular values of several large random matrices in parallel as follows: ```julia-repl @@ -434,9 +434,9 @@ julia> M = Matrix{Float64}[rand(1000,1000) for i = 1:10]; julia> pmap(svd, M); ``` -Julia's [`pmap()`](@ref) is designed for the case where each function call does a large amount +Julia's [`pmap`](@ref) is designed for the case where each function call does a large amount of work. In contrast, `@parallel for` can handle situations where each iteration is tiny, perhaps -merely summing two numbers. Only worker processes are used by both [`pmap()`](@ref) and `@parallel for` +merely summing two numbers. Only worker processes are used by both [`pmap`](@ref) and `@parallel for` for the parallel computation. In case of `@parallel for`, the final reduction is done on the calling process. @@ -445,7 +445,7 @@ process. ## Scheduling Julia's parallel programming platform uses [Tasks (aka Coroutines)](@ref man-tasks) to switch among multiple -computations. Whenever code performs a communication operation like [`fetch()`](@ref) or [`wait()`](@ref), +computations. Whenever code performs a communication operation like [`fetch`](@ref) or [`wait`](@ref), the current task is suspended and a scheduler picks another task to run. A task is restarted when the event it is waiting for completes. @@ -465,7 +465,7 @@ julia> pmap(svd, M); If one process handles both 800×800 matrices and another handles both 600×600 matrices, we will not get as much scalability as we could. The solution is to make a local task to "feed" work to -each process when it completes its current task. For example, consider a simple [`pmap()`](@ref) +each process when it completes its current task. For example, consider a simple [`pmap`](@ref) implementation: ```julia @@ -501,10 +501,10 @@ use it to create a "feeder" task for each process. Each task picks the next inde be computed, then waits for its process to finish, then repeats until we run out of indexes. Note that the feeder tasks do not begin to execute until the main task reaches the end of the [`@sync`](@ref) block, at which point it surrenders control and waits for all the local tasks to complete before -returning from the function. The feeder tasks are able to share state via `nextidx()` because +returning from the function. The feeder tasks are able to share state via `nextidx` because they all run on the same process. No locking is required, since the threads are scheduled cooperatively and not preemptively. This means context switches only occur at well-defined points: in this case, -when [`remotecall_fetch()`](@ref) is called. +when [`remotecall_fetch`](@ref) is called. ## Channels @@ -519,9 +519,9 @@ to complete. A channel can be visualized as a pipe, i.e., it has a write end and read end. - * Multiple writers in different tasks can write to the same channel concurrently via [`put!()`](@ref) + * Multiple writers in different tasks can write to the same channel concurrently via [`put!`](@ref) calls. - * Multiple readers in different tasks can read data concurrently via [`take!()`](@ref) calls. + * Multiple readers in different tasks can read data concurrently via [`take!`](@ref) calls. * As an example: ```julia @@ -529,7 +529,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. c1 = Channel(32) c2 = Channel(32) - # and a function `foo()` which reads items from from c1, processes the item read + # and a function `foo` which reads items from from c1, processes the item read # and writes a result to c2, function foo() while true @@ -539,7 +539,7 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. end end - # we can schedule `n` instances of `foo()` to be active concurrently. + # we can schedule `n` instances of `foo` to be active concurrently. for _ in 1:n @schedule foo() end @@ -549,13 +549,13 @@ A channel can be visualized as a pipe, i.e., it has a write end and read end. to the maximum number of elements that can be held in the channel at any time. For example, `Channel(32)` creates a channel that can hold a maximum of 32 objects of any type. A `Channel{MyType}(64)` can hold up to 64 objects of `MyType` at any time. - * If a [`Channel`](@ref) is empty, readers (on a [`take!()`](@ref) call) will block until data is available. - * If a [`Channel`](@ref) is full, writers (on a [`put!()`](@ref) call) will block until space becomes available. - * [`isready()`](@ref) tests for the presence of any object in the channel, while [`wait()`](@ref) + * If a [`Channel`](@ref) is empty, readers (on a [`take!`](@ref) call) will block until data is available. + * If a [`Channel`](@ref) is full, writers (on a [`put!`](@ref) call) will block until space becomes available. + * [`isready`](@ref) tests for the presence of any object in the channel, while [`wait`](@ref) waits for an object to become available. * A [`Channel`](@ref) is in an open state initially. This means that it can be read from and written to - freely via [`take!()`](@ref) and [`put!()`](@ref) calls. [`close()`](@ref) closes a [`Channel`](@ref). - On a closed [`Channel`](@ref), [`put!()`](@ref) will fail. For example: + freely via [`take!`](@ref) and [`put!`](@ref) calls. [`close`](@ref) closes a [`Channel`](@ref). + On a closed [`Channel`](@ref), [`put!`](@ref) will fail. For example: ```julia-repl julia> c = Channel(2); @@ -570,7 +570,7 @@ ERROR: InvalidStateException("Channel is closed.",:closed) [...] ``` - * [`take!()`](@ref) and [`fetch()`](@ref) (which retrieves but does not remove the value) on a closed + * [`take!`](@ref) and [`fetch`](@ref) (which retrieves but does not remove the value) on a closed channel successfully return any existing values until it is emptied. Continuing the above example: ```julia-repl @@ -683,7 +683,7 @@ too. Remote references always refer to an implementation of an `AbstractChannel`. A concrete implementation of an `AbstractChannel` (like `Channel`), is required to implement -[`put!()`](@ref), [`take!()`](@ref), [`fetch()`](@ref), [`isready()`](@ref) and [`wait()`](@ref). +[`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) and [`wait`](@ref). The remote object referred to by a [`Future`](@ref) is stored in a `Channel{Any}(1)`, i.e., a `Channel` of size 1 capable of holding objects of `Any` type. @@ -691,13 +691,13 @@ The remote object referred to by a [`Future`](@ref) is stored in a `Channel{Any} other implementation of an `AbstractChannel`. The constructor `RemoteChannel(f::Function, pid)()` allows us to construct references to channels -holding more than one value of a specific type. `f()` is a function executed on `pid` and it must +holding more than one value of a specific type. `f` is a function executed on `pid` and it must return an `AbstractChannel`. For example, `RemoteChannel(()->Channel{Int}(10), pid)`, will return a reference to a channel of type `Int` and size 10. The channel exists on worker `pid`. -Methods [`put!()`](@ref), [`take!()`](@ref), [`fetch()`](@ref), [`isready()`](@ref) and [`wait()`](@ref) +Methods [`put!`](@ref), [`take!`](@ref), [`fetch`](@ref), [`isready`](@ref) and [`wait`](@ref) on a [`RemoteChannel`](@ref) are proxied onto the backing store on the remote process. [`RemoteChannel`](@ref) can thus be used to refer to user implemented `AbstractChannel` objects. @@ -796,7 +796,7 @@ The notifications are done via sending of "tracking" messages--an "add reference a reference is serialized to a different process and a "delete reference" message when a reference is locally garbage collected. -Since [`Future`](@ref)s are write-once and cached locally, the act of [`fetch()`](@ref)ing a +Since [`Future`](@ref)s are write-once and cached locally, the act of [`fetch`](@ref)ing a [`Future`](@ref) also updates reference tracking information on the node owning the value. The node which owns the value frees it once all references to it are cleared. @@ -809,10 +809,10 @@ of the object and the current memory pressure in the system. In case of remote references, the size of the local reference object is quite small, while the value stored on the remote node may be quite large. Since the local object may not be collected -immediately, it is a good practice to explicitly call [`finalize()`](@ref) on local instances -of a [`RemoteChannel`](@ref), or on unfetched [`Future`](@ref)s. Since calling [`fetch()`](@ref) +immediately, it is a good practice to explicitly call [`finalize`](@ref) on local instances +of a [`RemoteChannel`](@ref), or on unfetched [`Future`](@ref)s. Since calling [`fetch`](@ref) on a [`Future`](@ref) also removes its reference from the remote store, this is not required on -fetched [`Future`](@ref)s. Explicitly calling [`finalize()`](@ref) results in an immediate message +fetched [`Future`](@ref)s. Explicitly calling [`finalize`](@ref) results in an immediate message sent to the remote node to go ahead and remove its reference to the value. Once finalized, a reference becomes invalid and cannot be used in any further calls. @@ -831,8 +831,8 @@ data jointly accessible to two or more processes on the same machine. and is efficient because the underlying memory is available to the local process. Therefore, most algorithms work naturally on [`SharedArray`](@ref)s, albeit in single-process mode. In cases where an algorithm insists on an [`Array`](@ref) input, the underlying array can be retrieved -from a [`SharedArray`](@ref) by calling [`sdata()`](@ref). For other `AbstractArray` types, [`sdata()`](@ref) -just returns the object itself, so it's safe to use [`sdata()`](@ref) on any `Array`-type object. +from a [`SharedArray`](@ref) by calling [`sdata`](@ref). For other `AbstractArray` types, [`sdata`](@ref) +just returns the object itself, so it's safe to use [`sdata`](@ref) on any `Array`-type object. The constructor for a shared array is of the form: @@ -874,7 +874,7 @@ julia> S 2 7 4 4 ``` -[`Base.localindexes()`](@ref) provides disjoint one-dimensional ranges of indexes, and is sometimes +[`Base.localindexes`](@ref) provides disjoint one-dimensional ranges of indexes, and is sometimes convenient for splitting up tasks among processes. You can, of course, divide the work any way you wish: @@ -1035,8 +1035,8 @@ A Julia cluster has the following characteristics: Connections between workers (using the in-built TCP/IP transport) is established in the following manner: - * [`addprocs()`](@ref) is called on the master process with a `ClusterManager` object. - * [`addprocs()`](@ref) calls the appropriate [`launch()`](@ref) method which spawns required number + * [`addprocs`](@ref) is called on the master process with a `ClusterManager` object. + * [`addprocs`](@ref) calls the appropriate [`launch`](@ref) method which spawns required number of worker processes on appropriate machines. * Each worker starts listening on a free port and writes out its host and port information to [`STDOUT`](@ref). * The cluster manager captures the [`STDOUT`](@ref) of each worker and makes it available to the @@ -1061,8 +1061,8 @@ and multi-processor hardware. Thus, a minimal cluster manager would need to: * be a subtype of the abstract `ClusterManager` - * implement [`launch()`](@ref), a method responsible for launching new workers - * implement [`manage()`](@ref), which is called at various events during a worker's lifetime (for + * implement [`launch`](@ref), a method responsible for launching new workers + * implement [`manage`](@ref), which is called at various events during a worker's lifetime (for example, sending an interrupt signal) [`addprocs(manager::FooManager)`](@ref addprocs) requires `FooManager` to implement: @@ -1094,15 +1094,15 @@ function manage(manager::LocalManager, id::Integer, config::WorkerConfig, op::Sy end ``` -The [`launch()`](@ref) method takes the following arguments: +The [`launch`](@ref) method takes the following arguments: - * `manager::ClusterManager`: the cluster manager that [`addprocs()`](@ref) is called with - * `params::Dict`: all the keyword arguments passed to [`addprocs()`](@ref) + * `manager::ClusterManager`: the cluster manager that [`addprocs`](@ref) is called with + * `params::Dict`: all the keyword arguments passed to [`addprocs`](@ref) * `launched::Array`: the array to append one or more `WorkerConfig` objects to * `c::Condition`: the condition variable to be notified as and when workers are launched -The [`launch()`](@ref) method is called asynchronously in a separate task. The termination of -this task signals that all requested workers have been launched. Hence the [`launch()`](@ref) +The [`launch`](@ref) method is called asynchronously in a separate task. The termination of +this task signals that all requested workers have been launched. Hence the [`launch`](@ref) function MUST exit as soon as all the requested workers have been launched. Newly launched workers are connected to each other and the master process in an all-to-all manner. @@ -1122,7 +1122,7 @@ As an example of a non-TCP/IP transport, an implementation may choose to use MPI `--worker` must NOT be specified. Instead, newly launched workers should call `init_worker(cookie)` before using any of the parallel constructs. -For every worker launched, the [`launch()`](@ref) method must add a `WorkerConfig` object (with +For every worker launched, the [`launch`](@ref) method must add a `WorkerConfig` object (with appropriate fields initialized) to `launched` ```julia @@ -1225,8 +1225,8 @@ When using custom transports: must be called. This launches a new task that handles reading and writing of messages from/to the worker represented by the `IO` objects. * `init_worker(cookie, manager::FooManager)` MUST be called as part of worker process initialization. - * Field `connect_at::Any` in `WorkerConfig` can be set by the cluster manager when [`launch()`](@ref) - is called. The value of this field is passed in in all [`connect()`](@ref) callbacks. Typically, + * Field `connect_at::Any` in `WorkerConfig` can be set by the cluster manager when [`launch`](@ref) + is called. The value of this field is passed in in all [`connect`](@ref) callbacks. Typically, it carries information on *how to connect* to a worker. For example, the TCP/IP socket transport uses this field to specify the `(host, port)` tuple at which to connect to a worker. @@ -1344,7 +1344,7 @@ julia> Threads.nthreads() 4 ``` -But we are currently on the master thread. To check, we use the command [`Threads.threadid()`](@ref) +But we are currently on the master thread. To check, we use the function [`Threads.threadid`](@ref) ```julia-repl julia> Threads.threadid() @@ -1408,12 +1408,12 @@ All I/O tasks, timers, REPL commands, etc are multiplexed onto a single OS threa loop. A patched version of libuv ([http://docs.libuv.org/en/v1.x/](http://docs.libuv.org/en/v1.x/)) provides this functionality. Yield points provide for co-operatively scheduling multiple tasks onto the same OS thread. I/O tasks and timers yield implicitly while waiting for the event to -occur. Calling [`yield()`](@ref) explicitly allows for other tasks to be scheduled. +occur. Calling [`yield`](@ref) explicitly allows for other tasks to be scheduled. Thus, a task executing a [`ccall`](@ref) effectively prevents the Julia scheduler from executing any other tasks till the call returns. This is true for all calls into external libraries. Exceptions are calls into custom C code that call back into Julia (which may then yield) or C code that calls -`jl_yield()` (C equivalent of [`yield()`](@ref)). +`jl_yield()` (C equivalent of [`yield`](@ref)). Note that while Julia code runs on a single thread (by default), libraries used by Julia may launch their own internal threads. For example, the BLAS library may start as many threads as there are diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index b26a874215db6..6fa1c9a509ee2 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -74,7 +74,7 @@ On the first call (`@time f(1)`), `f` gets compiled. (If you've not yet used [` in this session, it will also compile functions needed for timing.) You should not take the results of this run seriously. For the second run, note that in addition to reporting the time, it also indicated that a large amount of memory was allocated. This is the single biggest advantage of -[`@time`](@ref) vs. functions like [`tic()`](@ref) and [`toc()`](@ref), which only report time. +[`@time`](@ref) vs. functions like [`tic`](@ref) and [`toc`](@ref), which only report time. Unexpected memory allocation is almost always a sign of some problem with your code, usually a problem with type-stability. Consequently, in addition to the allocation itself, it's very likely @@ -485,6 +485,34 @@ annotation in this context in order to achieve type stability. This is because t cannot deduce the type of the return value of a function, even `convert`, unless the types of all the function's arguments are known. +Type annotation will not enhance (and can actually hinder) performance if the type is constructed +at run-time. This is because the compiler cannot use the annotation to specialize the subsequent +code, and the type-check itself takes time. For example, in the code: + +```julia +function nr(a, prec) + ctype = prec == 32 ? Float32 : Float64 + b = Complex{ctype}(a) + c = (b + 1.0f0)::Complex{ctype} + abs(c) +end +``` + +the annotation of `c` harms performance. To write performant code involving types constructed at +run-time, use the [function-barrier technique](@ref kernal-functions) discussed below, and ensure +that the constructed type appears among the argument types of the kernel function so that the kernel +operations are properly specialized by the compiler. For example, in the above snippet, as soon as +`b` is constructed, it can be passed to another function `k`, the kernel. If, for example, function +`k` declares `b` as an argument of type `Complex{T}`, where `T` is a type parameter, then a type annotation +appearing in an assignment statement within `k` of the form: + +```julia +c = (b + 1.0f0)::Complex{T} +``` + +does not hinder performance (but does not help either) since the compiler can determine the type of `c` +at the time `k` is compiled. + ### Declare types of keyword arguments Keyword arguments can have declared types: @@ -549,7 +577,7 @@ easily be fixed as follows: pos(x) = x < 0 ? zero(x) : x ``` -There is also a [`one()`](@ref) function, and a more general [`oftype(x, y)`](@ref) function, which +There is also a [`one`](@ref) function, and a more general [`oftype(x, y)`](@ref) function, which returns `y` converted to the type of `x`. ## Avoid changing the type of a variable @@ -810,7 +838,7 @@ Consider the following contrived example. Imagine we wanted to write a function [`Vector`](@ref) and returns a square [`Matrix`](@ref) with either the rows or the columns filled with copies of the input vector. Assume that it is not important whether rows or columns are filled with these copies (perhaps the rest of the code can be easily adapted accordingly). We could conceivably -do this in at least four ways (in addition to the recommended call to the built-in [`repmat()`](@ref)): +do this in at least four ways (in addition to the recommended call to the built-in [`repmat`](@ref)): ```julia function copy_cols(x::Vector{T}) where T @@ -997,7 +1025,7 @@ An alternative is to create a "view" of the array, which is an array object (a `SubArray`) that actually references the data of the original array in-place, without making a copy. (If you write to a view, it modifies the original array's data as well.) -This can be done for individual slices by calling [`view()`](@ref), +This can be done for individual slices by calling [`view`](@ref), or more simply for a whole expression or block of code by putting [`@views`](@ref) in front of that expression. For example: @@ -1125,7 +1153,7 @@ These are some minor points that might help in tight inner loops. * Avoid unnecessary arrays. For example, instead of [`sum([x,y,z])`](@ref) use `x+y+z`. * Use [`abs2(z)`](@ref) instead of [`abs(z)^2`](@ref) for complex `z`. In general, try to rewrite - code to use [`abs2()`](@ref) instead of [`abs()`](@ref) for complex arguments. + code to use [`abs2`](@ref) instead of [`abs`](@ref) for complex arguments. * Use [`div(x,y)`](@ref) for truncating division of integers instead of [`trunc(x/y)`](@ref), [`fld(x,y)`](@ref) instead of [`floor(x/y)`](@ref), and [`cld(x,y)`](@ref) instead of [`ceil(x/y)`](@ref). @@ -1208,8 +1236,8 @@ following additional properties: * The loop must be an innermost loop. * The loop body must be straight-line code. This is why `@inbounds` 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 [`ifelse()`](@ref) - instead of `?:` in the loop if it is safe to do so. + 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. @@ -1295,7 +1323,7 @@ on this particular computer), the main difference is that the expression `1 / (2 `idx = 1 / (2*dx)`. In the loop, the expression `... / (2*dx)` then becomes `... * idx`, which is much faster to evaluate. Of course, both the actual optimization that is applied by the compiler as well as the resulting speedup depend very much on the hardware. You can examine the change -in generated code by using Julia's [`code_native()`](@ref) function. +in generated code by using Julia's [`code_native`](@ref) function. ## Treat Subnormal Numbers as Zeros @@ -1359,7 +1387,7 @@ a = rand(Float32,1000) * 1.f-9 ## [[`@code_warntype`](@ref)](@id man-code-warntype) -The macro [`@code_warntype`](@ref) (or its function variant [`code_warntype()`](@ref)) can sometimes +The macro [`@code_warntype`](@ref) (or its function variant [`code_warntype`](@ref)) can sometimes be helpful in diagnosing type-related problems. Here's an example: ```julia diff --git a/doc/src/manual/profile.md b/doc/src/manual/profile.md index 863c41de36c9c..8e9d530156a35 100644 --- a/doc/src/manual/profile.md +++ b/doc/src/manual/profile.md @@ -205,7 +205,7 @@ be very useful, but sometimes you want to start fresh; you can do so with [`Prof ## Options for controlling the display of profile results -[`Profile.print()`](@ref) has more options than we've described so far. Let's see the full declaration: +[`Profile.print`](@ref) has more options than we've described so far. Let's see the full declaration: ```julia function print(io::IO = STDOUT, data = fetch(); kwargs...) diff --git a/doc/src/manual/running-external-programs.md b/doc/src/manual/running-external-programs.md index e1f96a9bad903..82ca983b729cc 100644 --- a/doc/src/manual/running-external-programs.md +++ b/doc/src/manual/running-external-programs.md @@ -47,7 +47,7 @@ julia> chomp(a) == "hello" true ``` -More generally, you can use [`open()`](@ref) to read from or write to an external command. +More generally, you can use [`open`](@ref) to read from or write to an external command. ```jldoctest julia> open(`less`, "w", STDOUT) do io @@ -245,7 +245,7 @@ hello | sort This expression invokes the `echo` command with three words as arguments: `hello`, `|`, and `sort`. The result is that a single line is printed: `hello | sort`. How, then, does one construct a -pipeline? Instead of using `'|'` inside of backticks, one uses [`pipeline()`](@ref): +pipeline? Instead of using `'|'` inside of backticks, one uses [`pipeline`](@ref): ```jldoctest julia> run(pipeline(`echo hello`, `sort`)) diff --git a/doc/src/manual/stacktraces.md b/doc/src/manual/stacktraces.md index 82f0ad70f5070..6d2a06b06ccda 100644 --- a/doc/src/manual/stacktraces.md +++ b/doc/src/manual/stacktraces.md @@ -5,7 +5,7 @@ easy to use programmatically. ## Viewing a stack trace -The primary function used to obtain a stack trace is [`stacktrace()`](@ref): +The primary function used to obtain a stack trace is [`stacktrace`](@ref): ```julia-repl julia> stacktrace() @@ -69,7 +69,7 @@ julia> example() Each [`StackFrame`](@ref) contains the function name, file name, line number, lambda info, a flag indicating whether the frame has been inlined, a flag indicating whether it is a C function (by default C functions do not appear in the stack trace), and an integer representation of the pointer -returned by [`backtrace()`](@ref): +returned by [`backtrace`](@ref): ```julia-repl julia> top_frame = stacktrace()[1] @@ -126,13 +126,13 @@ julia> example() ``` You may notice that in the example above the first stack frame points points at line 4, where -[`stacktrace()`](@ref) is called, rather than line 2, where *bad_function* is called, and `bad_function`'s -frame is missing entirely. This is understandable, given that [`stacktrace()`](@ref) is called +[`stacktrace`](@ref) is called, rather than line 2, where *bad_function* is called, and `bad_function`'s +frame is missing entirely. This is understandable, given that [`stacktrace`](@ref) is called from the context of the *catch*. While in this example it's fairly easy to find the actual source of the error, in complex cases tracking down the source of the error becomes nontrivial. -This can be remedied by calling [`catch_stacktrace()`](@ref) instead of [`stacktrace()`](@ref). -Instead of returning callstack information for the current context, [`catch_stacktrace()`](@ref) +This can be remedied by calling [`catch_stacktrace`](@ref) instead of [`stacktrace`](@ref). +Instead of returning callstack information for the current context, [`catch_stacktrace`](@ref) returns stack information for the context of the most recent exception: ```julia-repl @@ -181,10 +181,10 @@ ERROR: Whoops! [...] ``` -## Comparison with [`backtrace()`](@ref) +## Comparison with [`backtrace`](@ref) -A call to [`backtrace()`](@ref) returns a vector of `Ptr{Void}`, which may then be passed into -[`stacktrace()`](@ref) for translation: +A call to [`backtrace`](@ref) returns a vector of `Ptr{Void}`, which may then be passed into +[`stacktrace`](@ref) for translation: ```julia-repl julia> trace = backtrace() @@ -220,8 +220,8 @@ julia> stacktrace(trace) (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at event.jl:73 ``` -Notice that the vector returned by [`backtrace()`](@ref) had 21 pointers, while the vector returned -by [`stacktrace()`](@ref) only has 5. This is because, by default, [`stacktrace()`](@ref) removes +Notice that the vector returned by [`backtrace`](@ref) had 21 pointers, while the vector returned +by [`stacktrace`](@ref) only has 5. This is because, by default, [`stacktrace`](@ref) removes any lower-level C functions from the stack. If you want to include stack frames from C calls, you can do it like this: @@ -257,8 +257,8 @@ julia> stacktrace(trace, true) ip:0xffffffffffffffff ``` -Individual pointers returned by [`backtrace()`](@ref) can be translated into [`StackFrame`](@ref) -s by passing them into [`StackTraces.lookup()`](@ref): +Individual pointers returned by [`backtrace`](@ref) can be translated into [`StackFrame`](@ref) +s by passing them into [`StackTraces.lookup`](@ref): ```julia-repl julia> pointer = backtrace()[1]; diff --git a/doc/src/manual/strings.md b/doc/src/manual/strings.md index 2eb91e53ae111..2508e28b61177 100644 --- a/doc/src/manual/strings.md +++ b/doc/src/manual/strings.md @@ -22,7 +22,7 @@ There are a few noteworthy high-level features about Julia's strings: * The built-in concrete type used for strings (and string literals) in Julia is [`String`](@ref). This supports the full range of [Unicode](https://en.wikipedia.org/wiki/Unicode) characters via - the [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. (A [`transcode()`](@ref) function is + the [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoding. (A [`transcode`](@ref) function is provided to convert to/from other Unicode encodings.) * All string types are subtypes of the abstract type `AbstractString`, and external packages define additional `AbstractString` subtypes (e.g. for other encodings). If you define a function expecting @@ -72,9 +72,9 @@ julia> Char(120) 'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase) ``` -Not all integer values are valid Unicode code points, but for performance, the `Char()` conversion +Not all integer values are valid Unicode code points, but for performance, the `Char` conversion does not check that every character value is valid. If you want to check that each converted value -is a valid code point, use the [`isvalid()`](@ref) function: +is a valid code point, use the [`isvalid`](@ref) function: ```jldoctest julia> Char(0x110000) @@ -314,7 +314,7 @@ For example, the [LegacyStrings.jl](https://github.com/JuliaArchive/LegacyString implements `UTF16String` and `UTF32String` types. Additional discussion of other encodings and how to implement support for them is beyond the scope of this document for the time being. For further discussion of UTF-8 encoding issues, see the section below on [byte array literals](@ref man-byte-array-literals). -The [`transcode()`](@ref) function is provided to convert data between the various UTF-xx encodings, +The [`transcode`](@ref) function is provided to convert data between the various UTF-xx encodings, primarily for working with external data and libraries. ## Concatenation @@ -359,7 +359,7 @@ implies commutativity. ## [Interpolation](@id string-interpolation) Constructing strings using concatenation can become a bit cumbersome, however. To reduce the need for these -verbose calls to [`string()`](@ref) or repeated multiplications, Julia allows interpolation into string literals +verbose calls to [`string`](@ref) or repeated multiplications, Julia allows interpolation into string literals using `$`, as in Perl: ```jldoctest stringconcat @@ -378,7 +378,7 @@ julia> "1 + 2 = $(1 + 2)" "1 + 2 = 3" ``` -Both concatenation and string interpolation call [`string()`](@ref) to convert objects into string +Both concatenation and string interpolation call [`string`](@ref) to convert objects into string form. Most non-`AbstractString` objects are converted to strings closely corresponding to how they are entered as literal expressions: @@ -393,7 +393,7 @@ julia> "v: $v" "v: [1, 2, 3]" ``` -[`string()`](@ref) is the identity for `AbstractString` and `Char` values, so these are interpolated +[`string`](@ref) is the identity for `AbstractString` and `Char` values, so these are interpolated into strings as themselves, unquoted and unescaped: ```jldoctest @@ -474,7 +474,7 @@ julia> "1 + 2 = 3" == "1 + 2 = $(1 + 2)" true ``` -You can search for the index of a particular character using the [`search()`](@ref) function: +You can search for the index of a particular character using the [`search`](@ref) function: ```jldoctest julia> search("xylophone", 'x') @@ -500,7 +500,7 @@ julia> search("xylophone", 'o', 8) 0 ``` -You can use the [`contains()`](@ref) function to check if a substring is contained in a string: +You can use the [`contains`](@ref) function to check if a substring is contained in a string: ```jldoctest julia> contains("Hello, world.", "world") @@ -516,9 +516,9 @@ julia> contains("Xylophon", 'o') true ``` -The last example shows that [`contains()`](@ref) can also look for a character literal. +The last example shows that [`contains`](@ref) can also look for a character literal. -Two other handy string functions are [`repeat()`](@ref) and [`join()`](@ref): +Two other handy string functions are [`repeat`](@ref) and [`join`](@ref): ```jldoctest julia> repeat(".:Z:.", 10) @@ -535,7 +535,7 @@ Some other useful functions include: * [`i = start(str)`](@ref start) gives the first valid index at which a character can be found in `str` (typically 1). * [`c, j = next(str,i)`](@ref next) returns next character at or after the index `i` and the next valid - character index following that. With [`start()`](@ref) and [`endof()`](@ref), can be used to iterate + character index following that. With [`start`](@ref) and [`endof`](@ref), can be used to iterate through the characters in `str`. * [`ind2chr(str,i)`](@ref) gives the number of characters in `str` up to and including any at index `i`. @@ -569,7 +569,7 @@ julia> typeof(ans) Regex ``` -To check if a regex matches a string, use [`ismatch()`](@ref): +To check if a regex matches a string, use [`ismatch`](@ref): ```jldoctest julia> ismatch(r"^\s*(?:#|$)", "not a comment") @@ -579,10 +579,10 @@ julia> ismatch(r"^\s*(?:#|$)", "# a comment") true ``` -As one can see here, [`ismatch()`](@ref) simply returns true or false, indicating whether the +As one can see here, [`ismatch`](@ref) simply returns true or false, indicating whether the given regex matches the string or not. Commonly, however, one wants to know not just whether a string matched, but also *how* it matched. To capture this information about a match, use the -[`match()`](@ref) function instead: +[`match`](@ref) function instead: ```jldoctest julia> match(r"^\s*(?:#|$)", "not a comment") @@ -591,7 +591,7 @@ julia> match(r"^\s*(?:#|$)", "# a comment") RegexMatch("#") ``` -If the regular expression does not match the given string, [`match()`](@ref) returns `nothing` +If the regular expression does not match the given string, [`match`](@ref) returns `nothing` -- a special value that does not print anything at the interactive prompt. Other than not printing, it is a completely normal value and you can test for it programmatically: @@ -604,7 +604,7 @@ else end ``` -If a regular expression does match, the value returned by [`match()`](@ref) is a `RegexMatch` +If a regular expression does match, the value returned by [`match`](@ref) is a `RegexMatch` object. These objects record how the expression matches, including the substring that the pattern matches and any captured substrings, if there are any. This example only captures the portion of the substring that matches, but perhaps we want to capture any non-blank text after the comment @@ -615,7 +615,7 @@ julia> m = match(r"^\s*(?:#\s*(.*?)\s*$|$)", "# a comment ") RegexMatch("# a comment ", 1="a comment") ``` -When calling [`match()`](@ref), you have the option to specify an index at which to start the +When calling [`match`](@ref), you have the option to specify an index at which to start the search. For example: ```jldoctest @@ -648,7 +648,7 @@ julia> m.match "acd" julia> m.captures -3-element Array{Union{SubString{String}, Void},1}: +3-element Array{Union{Void, SubString{String}},1}: "a" "c" "d" @@ -669,7 +669,7 @@ julia> m.match "ad" julia> m.captures -3-element Array{Union{SubString{String}, Void},1}: +3-element Array{Union{Void, SubString{String}},1}: "a" nothing "d" @@ -706,7 +706,7 @@ julia> m[2] "45" ``` -Captures can be referenced in a substitution string when using [`replace()`](@ref) by using `\n` +Captures can be referenced in a substitution string when using [`replace`](@ref) by using `\n` to refer to the nth capture group and prefixing the substitution string with `s`. Capture group 0 refers to the entire match object. Named capture groups can be referenced in the substitution with `g`. For example: diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index 458cd5c37b5d0..07defaea4df67 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -111,8 +111,8 @@ end ``` The Julia standard library uses this convention throughout and contains examples of functions -with both copying and modifying forms (e.g., [`sort()`](@ref) and [`sort!()`](@ref)), and others -which are just modifying (e.g., [`push!()`](@ref), [`pop!()`](@ref), [`splice!()`](@ref)). It +with both copying and modifying forms (e.g., [`sort`](@ref) and [`sort!`](@ref)), and others +which are just modifying (e.g., [`push!`](@ref), [`pop!`](@ref), [`splice!`](@ref)). It is typical for such functions to also return the modified array for convenience. ## Avoid strange type `Union`s @@ -156,11 +156,11 @@ uses (e.g. `a[i]::Int`) than to try to pack many alternatives into one type. ## Use naming conventions consistent with Julia's `base/` * modules and type names use capitalization and camel case: `module SparseArrays`, `struct UnitRange`. - * functions are lowercase ([`maximum()`](@ref), [`convert()`](@ref)) and, when readable, with multiple - words squashed together ([`isequal()`](@ref), [`haskey()`](@ref)). When necessary, use underscores - as word separators. Underscores are also used to indicate a combination of concepts ([`remotecall_fetch()`](@ref) - as a more efficient implementation of `fetch(remotecall(...))`) or as modifiers ([`sum_kbn()`](@ref)). - * conciseness is valued, but avoid abbreviation ([`indexin()`](@ref) rather than `indxin()`) as + * functions are lowercase ([`maximum`](@ref), [`convert`](@ref)) and, when readable, with multiple + words squashed together ([`isequal`](@ref), [`haskey`](@ref)). When necessary, use underscores + as word separators. Underscores are also used to indicate a combination of concepts ([`remotecall_fetch`](@ref) + as a more efficient implementation of `fetch(remotecall(...))`) or as modifiers ([`sum_kbn`](@ref)). + * conciseness is valued, but avoid abbreviation ([`indexin`](@ref) rather than `indxin`) as it becomes difficult to remember whether and how particular words are abbreviated. If a function name requires multiple words, consider whether it might represent more than one @@ -235,7 +235,7 @@ with the "values" as subtypes. Be aware of when a macro could really be a function instead. -Calling [`eval()`](@ref) inside a macro is a particularly dangerous warning sign; it means the +Calling [`eval`](@ref) inside a macro is a particularly dangerous warning sign; it means the macro will only work when called at the top level. If such a macro is written as a function instead, it will naturally have access to the run-time values it needs. @@ -307,7 +307,7 @@ higher-level, Julia-friendly API. ## Be careful with type equality -You generally want to use [`isa()`](@ref) and [`<:`](@ref) for testing types, +You generally want to use [`isa`](@ref) and [`<:`](@ref) for testing types, not `==`. Checking types for exact equality typically only makes sense when comparing to a known concrete type (e.g. `T == Float64`), or if you *really, really* know what you're doing. diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 7f19fcac08a67..893c53a1b54b7 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -41,7 +41,7 @@ up front are: where the combination of static compilation with polymorphism makes this distinction significant. * Only values, not variables, have types -- variables are simply names bound to values. * Both abstract and concrete types can be parameterized by other types. They can also be parameterized - by symbols, by values of any type for which [`isbits()`](@ref) returns true (essentially, things + by symbols, by values of any type for which [`isbits`](@ref) returns true (essentially, things like numbers and bools that are stored like C types or structs with no pointers to other objects), and also by tuples thereof. Type parameters may be omitted when they do not need to be referenced or restricted. @@ -69,7 +69,7 @@ an exception is thrown, otherwise, the left-hand value is returned: ```jldoctest julia> (1+2)::AbstractFloat -ERROR: TypeError: typeassert: expected AbstractFloat, got Int64 +ERROR: TypeError: in typeassert, expected AbstractFloat, got Int64 julia> (1+2)::Int 3 @@ -80,7 +80,7 @@ This allows a type assertion to be attached to any expression in-place. When appended to a variable on the left-hand side of an assignment, or as part of a `local` declaration, the `::` operator means something a bit different: it declares the variable to always have the specified type, like a type declaration in a statically-typed language such as C. Every value -assigned to the variable will be converted to the declared type using [`convert()`](@ref): +assigned to the variable will be converted to the declared type using [`convert`](@ref): ```jldoctest julia> function foo() @@ -334,7 +334,7 @@ Foo When a type is applied like a function it is called a *constructor*. Two constructors are generated automatically (these are called *default constructors*). One accepts any arguments and calls -[`convert()`](@ref) to convert them to the types of the fields, and the other accepts arguments +[`convert`](@ref) to convert them to the types of the fields, and the other accepts arguments that match the field types exactly. The reason both of these are generated is that this makes it easier to add new definitions without inadvertently replacing a default constructor. @@ -345,7 +345,7 @@ must be convertible to `Int`: julia> Foo((), 23.5, 1) ERROR: InexactError: convert(Int64, 23.5) Stacktrace: - [1] convert at ./float.jl:681 [inlined] + [1] convert at ./float.jl:701 [inlined] [2] Foo(::Tuple{}, ::Float64, ::Int64) at ./none:2 ``` @@ -483,7 +483,7 @@ argument types, constructed using the special `Union` function: ```jldoctest julia> IntOrString = Union{Int,AbstractString} -Union{AbstractString, Int64} +Union{Int64, AbstractString} julia> 1 :: IntOrString 1 @@ -492,7 +492,7 @@ julia> "Hello!" :: IntOrString "Hello!" julia> 1.0 :: IntOrString -ERROR: TypeError: typeassert: expected Union{AbstractString, Int64}, got Float64 +ERROR: TypeError: in typeassert, expected Union{Int64, AbstractString}, got Float64 ``` The compilers for many languages have an internal union construct for reasoning about types; Julia @@ -651,7 +651,7 @@ ERROR: MethodError: Cannot `convert` an object of type Float64 to an object of t This may have arisen from a call to the constructor Point{Float64}(...), since type constructors fall back to convert methods. Stacktrace: - [1] Point{Float64}(::Float64) at ./sysimg.jl:102 + [1] Point{Float64}(::Float64) at ./sysimg.jl:103 julia> Point{Float64}(1.0,2.0,3.0) ERROR: MethodError: no method matching Point{Float64}(::Float64, ::Float64, ::Float64) @@ -801,10 +801,10 @@ julia> Pointy{Real} Pointy{Real} julia> Pointy{AbstractString} -ERROR: TypeError: Pointy: in T, expected T<:Real, got Type{AbstractString} +ERROR: TypeError: in Pointy, in T, expected T<:Real, got Type{AbstractString} julia> Pointy{1} -ERROR: TypeError: Pointy: in T, expected T<:Real, got Int64 +ERROR: TypeError: in Pointy, in T, expected T<:Real, got Int64 ``` Type parameters for parametric composite types can be restricted in the same manner: @@ -1111,7 +1111,7 @@ julia> isa(1, AbstractFloat) false ``` -The [`typeof()`](@ref) function, already used throughout the manual in examples, returns the type +The [`typeof`](@ref) function, already used throughout the manual in examples, returns the type of its argument. Since, as noted above, types are objects, they also have types, and we can ask what their types are: @@ -1139,7 +1139,7 @@ DataType `DataType` is its own type. -Another operation that applies to some types is [`supertype()`](@ref), which reveals a type's +Another operation that applies to some types is [`supertype`](@ref), which reveals a type's supertype. Only declared types (`DataType`) have unambiguous supertypes: ```jldoctest @@ -1156,21 +1156,21 @@ julia> supertype(Any) Any ``` -If you apply [`supertype()`](@ref) to other type objects (or non-type objects), a [`MethodError`](@ref) +If you apply [`supertype`](@ref) to other type objects (or non-type objects), a [`MethodError`](@ref) is raised: ```jldoctest julia> supertype(Union{Float64,Int64}) ERROR: MethodError: no method matching supertype(::Type{Union{Float64, Int64}}) Closest candidates are: - supertype(!Matched::DataType) at operators.jl:41 - supertype(!Matched::UnionAll) at operators.jl:46 + supertype(!Matched::DataType) at operators.jl:42 + supertype(!Matched::UnionAll) at operators.jl:47 ``` ## Custom pretty-printing Often, one wants to customize how instances of a type are displayed. This is accomplished by -overloading the [`show()`](@ref) function. For example, suppose we define a type to represent +overloading the [`show`](@ref) function. For example, suppose we define a type to represent complex numbers in polar form: ```jldoctest polartype @@ -1202,7 +1202,7 @@ julia> Base.show(io::IO, z::Polar) = print(io, z.r, " * exp(", z.Θ, "im)") More fine-grained control over display of `Polar` objects is possible. In particular, sometimes one wants both a verbose multi-line printing format, used for displaying a single object in the REPL and other interactive environments, and also a more compact single-line format used for -[`print()`](@ref) or for displaying the object as part of another object (e.g. in an array). Although +[`print`](@ref) or for displaying the object as part of another object (e.g. in an array). Although by default the `show(io, z)` function is called in both cases, you can define a *different* multi-line format for displaying an object by overloading a three-argument form of `show` that takes the `text/plain` MIME type as its second argument (see [Multimedia I/O](@ref)), for example: @@ -1227,7 +1227,7 @@ julia> [Polar(3, 4.0), Polar(4.0,5.3)] where the single-line `show(io, z)` form is still used for an array of `Polar` values. Technically, the REPL calls `display(z)` to display the result of executing a line, which defaults to `show(STDOUT, MIME("text/plain"), z)`, -which in turn defaults to `show(STDOUT, z)`, but you should *not* define new [`display()`](@ref) +which in turn defaults to `show(STDOUT, z)`, but you should *not* define new [`display`](@ref) methods unless you are defining a new multimedia display handler (see [Multimedia I/O](@ref)). Moreover, you can also define `show` methods for other MIME types in order to enable richer display @@ -1405,7 +1405,7 @@ a single value of type `T` as an argument. ### Checking if a `Nullable` object has a value -You can check if a `Nullable` object has any value using [`isnull()`](@ref): +You can check if a `Nullable` object has any value using [`isnull`](@ref): ```jldoctest julia> isnull(Nullable{Float64}()) @@ -1417,25 +1417,25 @@ false ### Safely accessing the value of a `Nullable` object -You can safely access the value of a `Nullable` object using [`get()`](@ref): +You can safely access the value of a `Nullable` object using [`get`](@ref): ```jldoctest julia> get(Nullable{Float64}()) ERROR: NullException() Stacktrace: - [1] get(::Nullable{Float64}) at ./nullable.jl:92 + [1] get(::Nullable{Float64}) at ./nullable.jl:118 julia> get(Nullable(1.0)) 1.0 ``` If the value is not present, as it would be for `Nullable{Float64}`, a [`NullException`](@ref) -error will be thrown. The error-throwing nature of the `get()` function ensures that any +error will be thrown. The error-throwing nature of the `get` function ensures that any attempt to access a missing value immediately fails. In cases for which a reasonable default value exists that could be used when a `Nullable` object's value turns out to be missing, you can provide this default value as a second argument -to `get()`: +to `get`: ```jldoctest julia> get(Nullable{Float64}(), 0.0) @@ -1446,15 +1446,15 @@ julia> get(Nullable(1.0), 0.0) ``` !!! tip - Make sure the type of the default value passed to `get()` and that of the `Nullable` - object match to avoid type instability, which could hurt performance. Use [`convert()`](@ref) + Make sure the type of the default value passed to `get` and that of the `Nullable` + object match to avoid type instability, which could hurt performance. Use [`convert`](@ref) manually if needed. ### Performing operations on `Nullable` objects `Nullable` objects represent values that are possibly missing, and it is possible to write all code using these objects by first testing to see if -the value is missing with [`isnull()`](@ref), and then doing an appropriate +the value is missing with [`isnull`](@ref), and then doing an appropriate action. However, there are some common use cases where the code could be more concise or clear by using a higher-order function. @@ -1503,7 +1503,7 @@ example, we will assume that the best solution is to propagate the missing values forward; that is, if any input is missing, we simply produce a missing output. -The `broadcast()` function makes this task easy; we can simply pass the +The `broadcast` function makes this task easy; we can simply pass the `root` function we wrote to `broadcast`: ```jldoctest nullableroot @@ -1518,9 +1518,9 @@ Nullable{Float64}() ``` If one or more of the inputs is missing, then the output of -`broadcast()` will be missing. +`broadcast` will be missing. -There exists special syntactic sugar for the `broadcast()` function +There exists special syntactic sugar for the `broadcast` function using a dot notation: ```jldoctest nullableroot @@ -1528,7 +1528,7 @@ julia> root.(Nullable(1), Nullable(-9), Nullable(20)) Nullable{Float64}(5.0) ``` -In particular, the regular arithmetic operators can be `broadcast()` +In particular, the regular arithmetic operators can be `broadcast` conveniently using `.`-prefixed operators: ```jldoctest diff --git a/doc/src/manual/variables-and-scoping.md b/doc/src/manual/variables-and-scoping.md index f1df11b7ed5a3..f56cd1c49d51d 100644 --- a/doc/src/manual/variables-and-scoping.md +++ b/doc/src/manual/variables-and-scoping.md @@ -304,8 +304,8 @@ julia> odd(3) true ``` -Julia provides built-in, efficient functions to test for oddness and evenness called [`iseven()`](@ref) -and [`isodd()`](@ref) so the above definitions should only be taken as examples. +Julia provides built-in, efficient functions to test for oddness and evenness called [`iseven`](@ref) +and [`isodd`](@ref) so the above definitions should only be taken as examples. ### Hard vs. Soft Local Scope @@ -416,10 +416,9 @@ outer local `x`. ### For Loops and Comprehensions -`for` loops and [Comprehensions](@ref) have the following behavior: any new variables introduced -in their body scopes are freshly allocated for each loop iteration. This is in contrast to `while` -loops which reuse the variables for all iterations. Therefore these constructs are similar to -`while` loops with `let` blocks inside: +`for` loops, `while` loops, and [Comprehensions](@ref) have the following behavior: any new variables +introduced in their body scopes are freshly allocated for each loop iteration, as if the loop body +were surrounded by a `let` block: ```jldoctest julia> Fs = Array{Any}(2); @@ -435,7 +434,7 @@ julia> Fs[2]() 2 ``` -`for` loops will reuse existing variables for its iteration variable: +A `for` loop or comprehension iteration variable is always a new variable: ```jldoctest julia> i = 0; @@ -444,18 +443,20 @@ julia> for i = 1:3 end julia> i -3 +0 ``` -However, comprehensions do not do this, and always freshly allocate their iteration variables: +However, it is occasionally useful to reuse an existing variable as the iteration variable. +This can be done conveniently by adding the keyword `outer`: ```jldoctest -julia> x = 0; +julia> i = 0; -julia> [ x for x = 1:3 ]; +julia> for outer i = 1:3 + end -julia> x -0 +julia> i +3 ``` ## Constants @@ -475,7 +476,8 @@ their values (or even their types) might change at almost any time. If a global not change, adding a `const` declaration solves this performance problem. Local constants are quite different. The compiler is able to determine automatically when a local -variable is constant, so local constant declarations are not necessary, and are currently just ignored. +variable is constant, so local constant declarations are not necessary, and in fact are currently +not supported. Special top-level assignments, such as those performed by the `function` and `struct` keywords, are constant by default. diff --git a/doc/src/manual/variables.md b/doc/src/manual/variables.md index 8683b3ace795b..7e2df62bd8f92 100644 --- a/doc/src/manual/variables.md +++ b/doc/src/manual/variables.md @@ -59,29 +59,37 @@ name `δ` can be entered by typing `\delta`-*tab*, or even `α̂₂` by `\alpha` that you don't know how to type, the REPL help will tell you: just type `?` and then paste the symbol.) -Julia will even let you redefine built-in constants and functions if needed: +Julia will even let you redefine built-in constants and functions if needed (although +this is not recommended to avoid potential confusions): ```jldoctest -julia> pi -π = 3.1415926535897... - julia> pi = 3 -WARNING: imported binding for pi overwritten in module Main 3 julia> pi 3 +julia> sqrt = 4 +4 +``` + +However, if you try to redefine a built-in constant or function already in use, Julia will give +you an error: + +```jldoctest +julia> pi +π = 3.1415926535897... + +julia> pi = 3 +ERROR: cannot assign variable MathConstants.pi from module Main + julia> sqrt(100) 10.0 julia> sqrt = 4 -WARNING: imported binding for sqrt overwritten in module Main -4 +ERROR: cannot assign variable Base.sqrt from module Main ``` -However, this is obviously not recommended to avoid potential confusion. - ## Allowed Variable Names Variable names must begin with a letter (A-Z or a-z), underscore, or a subset of Unicode code diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index aedb79e7f6a64..7c46de37df2b4 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -42,7 +42,6 @@ Base.length(::AbstractArray) Base.eachindex Base.linearindices Base.IndexStyle -Base.countnz Base.conj! Base.stride Base.strides @@ -179,10 +178,6 @@ and can be converted to/from the latter via `Array(bitarray)` and `BitArray(arra ```@docs Base.flipbits! -Base.rol! -Base.rol -Base.ror! -Base.ror ``` ## [Sparse Vectors and Matrices](@id stdlib-sparse-arrays) diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index 6ba310f9cb9bd..15587998cb32d 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -83,7 +83,7 @@ Base.widen Base.identity ``` -## Types +## Dealing with Types ```@docs Base.supertype @@ -109,11 +109,23 @@ Base.isbits Base.isleaftype Base.typejoin Base.typeintersect -Base.Val -Base.Enums.@enum Base.instances ``` +## Special Types + +```@docs +Core.Void +Core.Any +Base.Enums.@enum +Core.Union +Union{} +Core.UnionAll +Core.Tuple +Base.Val +Core.Vararg +``` + ## Generic Functions ```@docs @@ -134,14 +146,15 @@ Base.@eval Base.evalfile Base.esc Base.@inbounds +Base.@boundscheck Base.@inline Base.@noinline Base.@nospecialize Base.gensym Base.@gensym Base.@polly -Base.parse(::Any, ::Any) -Base.parse(::Any) +Base.parse(::AbstractString, ::Int) +Base.parse(::AbstractString) ``` ## Nullables diff --git a/doc/src/stdlib/c.md b/doc/src/stdlib/c.md index 42199b752c001..19ecaa72c0c22 100644 --- a/doc/src/stdlib/c.md +++ b/doc/src/stdlib/c.md @@ -9,9 +9,8 @@ Base.cconvert Base.unsafe_load Base.unsafe_store! Base.unsafe_copy!{T}(::Ptr{T}, ::Ptr{T}, ::Any) -Base.unsafe_copy!(::Array, ::Any, ::Array, ::Any, ::Any) -Base.copy!(::Any, ::Any) -Base.copy!(::Any, ::Any, ::Any, ::Any, ::Any) +Base.unsafe_copy!{T}(::Array{T}, ::Any, ::Array{T}, ::Any, ::Any) +Base.copy! Base.pointer Base.unsafe_wrap{T,N}(::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, ::Ptr{T}, ::NTuple{N,Int}) Base.pointer_from_objref diff --git a/doc/src/stdlib/collections.md b/doc/src/stdlib/collections.md index 9415ae0fc1ce6..e83303f7ab89a 100644 --- a/doc/src/stdlib/collections.md +++ b/doc/src/stdlib/collections.md @@ -2,7 +2,7 @@ ## [Iteration](@id lib-collections-iteration) -Sequential iteration is implemented by the methods [`start()`](@ref), [`done()`](@ref), and [`next()`](@ref). +Sequential iteration is implemented by the methods [`start`](@ref), [`done`](@ref), and [`next`](@ref). The general `for` loop: ```julia @@ -53,7 +53,7 @@ Fully implemented by: ```@docs Base.isempty Base.empty! -Base.length(::Any) +Base.length ``` Fully implemented by: @@ -137,8 +137,8 @@ Base.filter! ## Indexable Collections ```@docs -Base.getindex(::Any, ::Any...) -Base.setindex!(::Any, ::Any, ::Any...) +Base.getindex +Base.setindex! Base.endof ``` @@ -161,8 +161,8 @@ Partially implemented by: ## Associative Collections -[`Dict`](@ref) is the standard associative collection. Its implementation uses [`hash()`](@ref) -as the hashing function for the key, and [`isequal()`](@ref) to determine equality. Define these +[`Dict`](@ref) is the standard associative collection. Its implementation uses [`hash`](@ref) +as the hashing function for the key, and [`isequal`](@ref) to determine equality. Define these two functions for custom types to override how they are stored in a hash table. [`ObjectIdDict`](@ref) is a special hash table where the keys are always object identities. @@ -170,7 +170,7 @@ two functions for custom types to override how they are stored in a hash table. [`WeakKeyDict`](@ref) is a hash table implementation where the keys are weak references to objects, and thus may be garbage collected even when referenced in a hash table. -[`Dict`](@ref)s can be created by passing pair objects constructed with `=>()` to a [`Dict`](@ref) +[`Dict`](@ref)s can be created by passing pair objects constructed with `=>` to a [`Dict`](@ref) constructor: `Dict("A"=>1, "B"=>2)`. This call will attempt to infer type information from the keys and values (i.e. this example creates a `Dict{String, Int64}`). To explicitly specify types use the syntax `Dict{KeyType,ValueType}(...)`. For example, `Dict{String,Int32}("A"=>1, "B"=>2)`. @@ -196,6 +196,7 @@ Base.delete! Base.pop!(::Any, ::Any, ::Any) Base.keys Base.values +Base.pairs Base.merge Base.merge!(::Associative, ::Associative...) Base.merge!(::Function, ::Associative, ::Associative...) @@ -249,7 +250,7 @@ Partially implemented by: ```@docs Base.push! -Base.pop!(::Any) +Base.pop! Base.unshift! Base.shift! Base.insert! diff --git a/doc/src/stdlib/io-network.md b/doc/src/stdlib/io-network.md index 3c94efcc2e339..6b750b7cad0e5 100644 --- a/doc/src/stdlib/io-network.md +++ b/doc/src/stdlib/io-network.md @@ -148,8 +148,7 @@ Base.Multimedia.istextmime ```@docs Base.Mmap.Anonymous -Base.Mmap.mmap(::Any, ::Type, ::Any, ::Any) -Base.Mmap.mmap(::Any, ::BitArray, ::Any, ::Any) +Base.Mmap.mmap Base.Mmap.sync! ``` diff --git a/doc/src/stdlib/libdl.md b/doc/src/stdlib/libdl.md index 621eaaeaa793d..9956624ad7f95 100644 --- a/doc/src/stdlib/libdl.md +++ b/doc/src/stdlib/libdl.md @@ -1,6 +1,6 @@ # Dynamic Linker -The names in `Base.Libdl` are not exported and need to be called e.g. as `Libdl.dlopen()`. +The names in `Base.Libdl` are not exported and need to be called e.g. as `Libdl.dlopen`. ```@docs Base.Libdl.dlopen diff --git a/doc/src/stdlib/linalg.md b/doc/src/stdlib/linalg.md index e9421cb216857..126d74954d4d2 100644 --- a/doc/src/stdlib/linalg.md +++ b/doc/src/stdlib/linalg.md @@ -92,9 +92,6 @@ Base.repmat Base.kron Base.SparseArrays.blkdiag Base.LinAlg.linreg -Base.LinAlg.expm -Base.LinAlg.logm -Base.LinAlg.sqrtm Base.LinAlg.lyap Base.LinAlg.sylvester Base.LinAlg.issuccess @@ -109,8 +106,8 @@ Base.LinAlg.RowVector Base.LinAlg.ConjArray Base.transpose Base.transpose! -Base.ctranspose -Base.ctranspose! +Base.adjoint +Base.adjoint! Base.LinAlg.eigs(::Any) Base.LinAlg.eigs(::Any, ::Any) Base.LinAlg.svds diff --git a/doc/src/stdlib/numbers.md b/doc/src/stdlib/numbers.md index 91941c4a338fa..977bd50049ac8 100644 --- a/doc/src/stdlib/numbers.md +++ b/doc/src/stdlib/numbers.md @@ -59,6 +59,7 @@ Base.Math.exponent Base.complex(::Complex) Base.bswap Base.hex2bytes +Base.hex2bytes! Base.bytes2hex ``` @@ -68,12 +69,12 @@ Base.bytes2hex Base.one Base.oneunit Base.zero -Base.pi Base.im -Base.eu -Base.catalan -Base.eulergamma -Base.golden +Base.MathConstants.pi +Base.MathConstants.ℯ +Base.MathConstants.catalan +Base.MathConstants.eulergamma +Base.MathConstants.golden Base.Inf Base.Inf32 Base.Inf16 @@ -143,12 +144,12 @@ dimension specifications `dims...` (which can be given as a tuple) to generate a values. A `MersenneTwister` or `RandomDevice` RNG can generate random numbers of the following types: -[`Float16`](@ref), [`Float32`](@ref), [`Float64`](@ref), [`Bool`](@ref), [`Int8`](@ref), -[`UInt8`](@ref), [`Int16`](@ref), [`UInt16`](@ref), [`Int32`](@ref), [`UInt32`](@ref), -[`Int64`](@ref), [`UInt64`](@ref), [`Int128`](@ref), [`UInt128`](@ref), [`BigInt`](@ref) -(or complex numbers of those types). Random floating point numbers are generated uniformly -in ``[0, 1)``. As `BigInt` represents unbounded integers, the interval must be specified -(e.g. `rand(big(1:6))`). +[`Float16`](@ref), [`Float32`](@ref), [`Float64`](@ref), [`BigFloat`](@ref), [`Bool`](@ref), +[`Int8`](@ref), [`UInt8`](@ref), [`Int16`](@ref), [`UInt16`](@ref), [`Int32`](@ref), +[`UInt32`](@ref), [`Int64`](@ref), [`UInt64`](@ref), [`Int128`](@ref), [`UInt128`](@ref), +[`BigInt`](@ref) (or complex numbers of those types). +Random floating point numbers are generated uniformly in ``[0, 1)``. As `BigInt` represents +unbounded integers, the interval must be specified (e.g. `rand(big(1:6))`). ```@docs Base.Random.srand diff --git a/doc/src/stdlib/sort.md b/doc/src/stdlib/sort.md index 65bfd0e70bbc9..ffb4725b6bcf6 100644 --- a/doc/src/stdlib/sort.md +++ b/doc/src/stdlib/sort.md @@ -122,10 +122,10 @@ Base.issorted Base.Sort.searchsorted Base.Sort.searchsortedfirst Base.Sort.searchsortedlast -Base.Sort.select! -Base.Sort.select -Base.Sort.selectperm -Base.Sort.selectperm! +Base.Sort.partialsort! +Base.Sort.partialsort +Base.Sort.partialsortperm +Base.Sort.partialsortperm! ``` ## Sorting Algorithms diff --git a/doc/src/stdlib/test.md b/doc/src/stdlib/test.md index baf78ae1c16b6..5f8415ee79b09 100644 --- a/doc/src/stdlib/test.md +++ b/doc/src/stdlib/test.md @@ -23,7 +23,7 @@ see if your code is correct by checking that the results are what you expect. It to ensure your code still works after you make changes, and can be used when developing as a way of specifying the behaviors your code should have when complete. -Simple unit testing can be performed with the `@test()` and `@test_throws()` macros: +Simple unit testing can be performed with the `@test` and `@test_throws` macros: ```@docs Base.Test.@test @@ -60,7 +60,7 @@ ERROR: There was an error during testing ``` If the condition could not be evaluated because an exception was thrown, which occurs in this -case because `length()` is not defined for symbols, an `Error` object is returned and an exception +case because `length` is not defined for symbols, an `Error` object is returned and an exception is thrown: ```julia-repl @@ -79,7 +79,7 @@ Error During Test ERROR: There was an error during testing ``` -If we expect that evaluating an expression *should* throw an exception, then we can use `@test_throws()` +If we expect that evaluating an expression *should* throw an exception, then we can use `@test_throws` to check that this occurs: ```jldoctest testfoo @@ -95,7 +95,7 @@ of inputs. In the event a test fails, the default behavior is to throw an except However, it is normally preferable to run the rest of the tests first to get a better picture of how many errors there are in the code being tested. -The `@testset()` macro can be used to group tests into *sets*. All the tests in a test set will +The `@testset` macro can be used to group tests into *sets*. All the tests in a test set will be run, and at the end of the test set a summary will be printed. If any of the tests failed, or could not be evaluated due to an error, the test set will then throw a `TestSetException`. @@ -167,7 +167,7 @@ ERROR: Some tests did not pass: 3 passed, 1 failed, 0 errored, 0 broken. As calculations on floating-point values can be imprecise, you can perform approximate equality checks using either `@test a ≈ b` (where `≈`, typed via tab completion of `\approx`, is the -[`isapprox()`](@ref) function) or use [`isapprox()`](@ref) directly. +[`isapprox`](@ref) function) or use [`isapprox`](@ref) directly. ```jldoctest julia> @test 1 ≈ 0.999999999 @@ -188,7 +188,7 @@ Base.Test.@test_nowarn ## Broken Tests -If a test fails consistently it can be changed to use the `@test_broken()` macro. This will denote +If a test fails consistently it can be changed to use the `@test_broken` macro. This will denote the test as `Broken` if the test continues to fail and alerts the user via an `Error` if the test succeeds. @@ -196,7 +196,7 @@ succeeds. Base.Test.@test_broken ``` -`@test_skip()` is also available to skip a test without evaluation, but counting the skipped test +`@test_skip` is also available to skip a test without evaluation, but counting the skipped test in the test set reporting. The test will not run but gives a `Broken` `Result`. ```@docs diff --git a/examples/embedding/embedding.c b/examples/embedding/embedding.c index c820e5b3c02ad..712e46dd8b6b4 100644 --- a/examples/embedding/embedding.c +++ b/examples/embedding/embedding.c @@ -142,7 +142,7 @@ int main() " nothing\n" "end"); typedef void (*Func_VOID__VOID)(void); - jl_value_t *pbar = jl_eval_string("cfunction(bar_from_c, Void, ())"); + jl_value_t *pbar = jl_eval_string("cfunction(bar_from_c, Void, Tuple{})"); Func_VOID__VOID bar = (Func_VOID__VOID)jl_unbox_voidpointer(pbar); bar(); checked_eval_string("bar() = println(\"calling new bar\")"); diff --git a/src/APInt-C.cpp b/src/APInt-C.cpp index 38bbdbe96a2e8..ccf3a010708b7 100644 --- a/src/APInt-C.cpp +++ b/src/APInt-C.cpp @@ -6,10 +6,9 @@ #include #include -#include "fix_llvm_assert.h" - #include "APInt-C.h" #include "julia.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/Makefile b/src/Makefile index da00f0f652842..d3ff989009006 100644 --- a/src/Makefile +++ b/src/Makefile @@ -75,8 +75,8 @@ LLVM_LIBS := support endif # headers are used for dependency tracking, while public headers will be part of the dist -HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_threads.h tls.h locks.h atomics.h julia_internal.h options.h timing.h) -PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_threads.h tls.h locks.h atomics.h) +HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h tls.h locks.h atomics.h julia_internal.h options.h timing.h) +PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,julia.h julia_assert.h julia_threads.h tls.h locks.h atomics.h) ifeq ($(USE_SYSTEM_LIBUV),0) ifeq ($(OS),WINNT) HEADERS += $(build_includedir)/tree.h diff --git a/src/abi_x86.cpp b/src/abi_x86.cpp index 12870c00d816d..7a65de028e083 100644 --- a/src/abi_x86.cpp +++ b/src/abi_x86.cpp @@ -39,18 +39,22 @@ struct ABI_x86Layout : AbiLayout { +STATIC_INLINE bool is_complex_type(jl_datatype_t *dt) +{ + static jl_sym_t *Complex_sym = NULL; + if (Complex_sym == NULL) + Complex_sym = jl_symbol("Complex"); + return jl_is_datatype(dt) && dt->name->name == Complex_sym && dt->name->module == jl_base_module; +} + inline bool is_complex64(jl_datatype_t *dt) const { - return jl_complex_type != NULL && jl_is_datatype(dt) && - ((jl_datatype_t*)dt)->name == ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_complex_type))->name && - jl_tparam0(dt) == (jl_value_t*)jl_float32_type; + return is_complex_type(dt) && jl_tparam0(dt) == (jl_value_t*)jl_float32_type; } inline bool is_complex128(jl_datatype_t *dt) const { - return jl_complex_type != NULL && jl_is_datatype(dt) && - ((jl_datatype_t*)dt)->name == ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_complex_type))->name && - jl_tparam0(dt) == (jl_value_t*)jl_float64_type; + return is_complex_type(dt) && jl_tparam0(dt) == (jl_value_t*)jl_float64_type; } bool use_sret(jl_datatype_t *dt) override diff --git a/src/array.c b/src/array.c index 3e77a0e9cbc30..0810ab9348958 100644 --- a/src/array.c +++ b/src/array.c @@ -5,12 +5,12 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -22,16 +22,10 @@ extern "C" { // array constructors --------------------------------------------------------- -static inline int store_unboxed(jl_value_t *el_type) // jl_isbits +JL_DLLEXPORT int jl_array_store_unboxed(jl_value_t *eltype) { - return jl_is_leaf_type(el_type) && jl_is_immutable(el_type) && - ((jl_datatype_t*)el_type)->layout && - ((jl_datatype_t*)el_type)->layout->npointers == 0; -} - -int jl_array_store_unboxed(jl_value_t *el_type) -{ - return store_unboxed(el_type); + size_t fsz = 0, al = 0; + return jl_islayout_inline(eltype, &fsz, &al); } STATIC_INLINE jl_value_t *jl_array_owner(jl_array_t *a) @@ -67,16 +61,20 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, jl_error("invalid Array dimensions"); nel = prod; } - + int isunion = atype != NULL && jl_is_uniontype(jl_tparam0(atype)); if (isunboxed) { wideint_t prod = (wideint_t)elsz * (wideint_t)nel; if (prod > (wideint_t) MAXINTVAL) jl_error("invalid Array size"); tot = prod; - if (elsz == 1) { + if (elsz == 1 && !isunion) { // extra byte for all julia allocated byte arrays tot++; } + if (isunion) { + // an extra byte for each isbits union array element, stored directly after the last array element + tot += nel; + } } else { wideint_t prod = (wideint_t)sizeof(void*) * (wideint_t)nel; @@ -97,7 +95,7 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, // No allocation or safepoint allowed after this a->flags.how = 0; data = (char*)a + doffs; - if (tot > 0 && !isunboxed) + if ((tot > 0 && !isunboxed) || isunion) memset(data, 0, tot); } else { @@ -109,7 +107,8 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, // No allocation or safepoint allowed after this a->flags.how = 2; jl_gc_track_malloced_array(ptls, a); - if (!isunboxed) + if (!isunboxed || isunion) + // need to zero out isbits union array selector bytes to ensure a valid type index memset(data, 0, tot); } a->flags.pooled = tsz <= GC_MAX_SZCLASS; @@ -141,11 +140,14 @@ static jl_array_t *_new_array_(jl_value_t *atype, uint32_t ndims, size_t *dims, static inline jl_array_t *_new_array(jl_value_t *atype, uint32_t ndims, size_t *dims) { - int isunboxed=0, elsz=sizeof(void*); - jl_value_t *el_type = jl_tparam0(atype); - isunboxed = store_unboxed(el_type); - if (isunboxed) - elsz = jl_datatype_size(el_type); + jl_value_t *eltype = jl_tparam0(atype); + size_t elsz = 0, al = 0; + int isunboxed = jl_islayout_inline(eltype, &elsz, &al); + if (!isunboxed) { + elsz = sizeof(void*); + al = elsz; + } + return _new_array_(atype, ndims, dims, isunboxed, elsz); } @@ -155,7 +157,7 @@ jl_array_t *jl_new_array_for_deserialization(jl_value_t *atype, uint32_t ndims, return _new_array_(atype, ndims, dims, isunboxed, elsz); } -#ifndef NDEBUG +#ifndef JL_NDEBUG static inline int is_ntuple_long(jl_value_t *v) { if (!jl_is_tuple(v)) @@ -180,7 +182,7 @@ JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, size_t *dims = (size_t*)_dims; int ndimwords = jl_array_ndimwords(ndims); - int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords*sizeof(size_t) + sizeof(void*), JL_SMALL_BYTE_ALIGNMENT); + int tsz = JL_ARRAY_ALIGN(sizeof(jl_array_t) + ndimwords * sizeof(size_t) + sizeof(void*), JL_SMALL_BYTE_ALIGNMENT); a = (jl_array_t*)jl_gc_alloc(ptls, tsz, atype); // No allocation or safepoint allowed after this a->flags.pooled = tsz <= GC_MAX_SZCLASS; @@ -189,18 +191,24 @@ JL_DLLEXPORT jl_array_t *jl_reshape_array(jl_value_t *atype, jl_array_t *data, a->data = NULL; a->flags.isaligned = data->flags.isaligned; jl_array_t *owner = (jl_array_t*)jl_array_owner(data); - jl_value_t *el_type = jl_tparam0(atype); - assert(store_unboxed(el_type) == !data->flags.ptrarray); - if (!data->flags.ptrarray) { - a->elsize = jl_datatype_size(el_type); - unsigned align = jl_datatype_align(el_type); + jl_value_t *eltype = jl_tparam0(atype); + size_t elsz = 0, align = 0; + int isboxed = !jl_islayout_inline(eltype, &elsz, &align); + assert(isboxed == data->flags.ptrarray); + if (!isboxed) { + a->elsize = elsz; jl_value_t *ownerty = jl_typeof(owner); - unsigned oldalign = (ownerty == (jl_value_t*)jl_string_type ? 1 : - jl_datatype_align(jl_tparam0(ownerty))); + size_t oldelsz = 0, oldalign = 0; + if (ownerty == (jl_value_t*)jl_string_type) { + oldalign = 1; + } + else { + jl_islayout_inline(jl_tparam0(ownerty), &oldelsz, &oldalign); + } if (oldalign < align) jl_exceptionf(jl_argumenterror_type, - "reinterpret from alignment %u bytes to alignment %u bytes not allowed", - oldalign, align); + "reinterpret from alignment %d bytes to alignment %d bytes not allowed", + (int) oldalign, (int) align); a->flags.ptrarray = 0; } else { @@ -276,14 +284,17 @@ JL_DLLEXPORT jl_array_t *jl_ptr_to_array_1d(jl_value_t *atype, void *data, { jl_ptls_t ptls = jl_get_ptls_states(); jl_array_t *a; - jl_value_t *el_type = jl_tparam0(atype); + jl_value_t *eltype = jl_tparam0(atype); - int isunboxed = store_unboxed(el_type); + int isunboxed = jl_array_store_unboxed(eltype); size_t elsz; unsigned align; + if (isunboxed && jl_is_uniontype(eltype)) + jl_exceptionf(jl_argumenterror_type, + "unsafe_wrap: unspecified layout for union element type"); if (isunboxed) { - elsz = jl_datatype_size(el_type); - align = jl_datatype_align(el_type); + elsz = jl_datatype_size(eltype); + align = jl_datatype_align(eltype); } else { align = elsz = sizeof(void*); @@ -339,14 +350,17 @@ JL_DLLEXPORT jl_array_t *jl_ptr_to_array(jl_value_t *atype, void *data, } if (__unlikely(ndims == 1)) return jl_ptr_to_array_1d(atype, data, nel, own_buffer); - jl_value_t *el_type = jl_tparam0(atype); + jl_value_t *eltype = jl_tparam0(atype); - int isunboxed = store_unboxed(el_type); + int isunboxed = jl_array_store_unboxed(eltype); size_t elsz; unsigned align; + if (isunboxed && jl_is_uniontype(eltype)) + jl_exceptionf(jl_argumenterror_type, + "unsafe_wrap: unspecified layout for union element type"); if (isunboxed) { - elsz = jl_datatype_size(el_type); - align = jl_datatype_align(el_type); + elsz = jl_datatype_size(eltype); + align = jl_datatype_align(eltype); } else { align = elsz = sizeof(void*); @@ -421,7 +435,7 @@ JL_DLLEXPORT jl_value_t *jl_array_to_string(jl_array_t *a) { if (a->flags.how == 3 && a->offset == 0 && a->elsize == 1 && (jl_array_ndims(a) != 1 || - !(a->maxsize+sizeof(void*)+1 > GC_MAX_SZCLASS && jl_array_nrows(a)+sizeof(void*)+1 <= GC_MAX_SZCLASS))) { + ((a->maxsize + sizeof(void*) + 1 <= GC_MAX_SZCLASS) == (jl_array_len(a) + sizeof(void*) + 1 <= GC_MAX_SZCLASS)))) { jl_value_t *o = jl_array_data_owner(a); if (jl_is_string(o)) { a->flags.isshared = 1; @@ -485,8 +499,15 @@ JL_DLLEXPORT jl_value_t *jl_arrayref(jl_array_t *a, size_t i) assert(i < jl_array_len(a)); jl_value_t *elt; if (!a->flags.ptrarray) { - jl_value_t *el_type = (jl_value_t*)jl_tparam0(jl_typeof(a)); - elt = jl_new_bits(el_type, &((char*)a->data)[i*a->elsize]); + jl_value_t *eltype = (jl_value_t*)jl_tparam0(jl_typeof(a)); + if (jl_is_uniontype(eltype)) { + // isbits union selector bytes are always stored directly after the last array element + uint8_t sel = ((uint8_t*)a->data)[jl_array_len(a) * a->elsize + i]; + eltype = jl_nth_union_component(eltype, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)eltype)) + return ((jl_datatype_t*)eltype)->instance; + } + elt = jl_new_bits(eltype, &((char*)a->data)[i * a->elsize]); } else { elt = ((jl_value_t**)a->data)[i]; @@ -539,13 +560,22 @@ int jl_array_isdefined(jl_value_t **args0, int nargs) JL_DLLEXPORT void jl_arrayset(jl_array_t *a, jl_value_t *rhs, size_t i) { assert(i < jl_array_len(a)); - jl_value_t *el_type = jl_tparam0(jl_typeof(a)); - if (el_type != (jl_value_t*)jl_any_type) { - if (!jl_isa(rhs, el_type)) - jl_type_error("arrayset", el_type, rhs); + jl_value_t *eltype = jl_tparam0(jl_typeof(a)); + if (eltype != (jl_value_t*)jl_any_type) { + if (!jl_isa(rhs, eltype)) + jl_type_error("arrayset", eltype, rhs); } if (!a->flags.ptrarray) { - jl_assign_bits(&((char*)a->data)[i*a->elsize], rhs); + if (jl_is_uniontype(eltype)) { + uint8_t *psel = &((uint8_t*)a->data)[jl_array_len(a) * a->elsize + i]; + unsigned nth = 0; + if (!jl_find_union_component(eltype, jl_typeof(rhs), &nth)) + assert(0 && "invalid arrayset to isbits union"); + *psel = nth; + if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs))) + return; + } + jl_assign_bits(&((char*)a->data)[i * a->elsize], rhs); } else { ((jl_value_t**)a->data)[i] = rhs; @@ -586,6 +616,11 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) nbytes++; oldnbytes++; } + int is_discriminated_union = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); + if (is_discriminated_union) { + nbytes += newlen; + oldnbytes += oldlen; + } int newbuf = 0; if (a->flags.how == 2) { // already malloc'd - use realloc @@ -593,15 +628,15 @@ static int NOINLINE array_resize_buffer(jl_array_t *a, size_t newlen) a->data = jl_gc_managed_realloc(olddata, nbytes, oldnbytes, a->flags.isaligned, (jl_value_t*)a); } - else if (a->flags.how == 3 && jl_is_string(jl_array_data_owner(a))) { + else if (a->flags.how == 3 && jl_is_string(jl_array_data_owner(a)) && !is_discriminated_union) { // if data is in a String, keep it that way jl_value_t *s; if (a->flags.isshared) { - s = jl_alloc_string(nbytes); + s = jl_alloc_string(nbytes - (elsz == 1)); newbuf = 1; } else { - s = jl_gc_realloc_string(jl_array_data_owner(a), nbytes); + s = jl_gc_realloc_string(jl_array_data_owner(a), nbytes - (elsz == 1)); } jl_array_data_owner(a) = s; jl_gc_wb(a, s); @@ -649,6 +684,9 @@ static void NOINLINE array_try_unshare(jl_array_t *a) size_t len = jl_array_nrows(a); size_t es = a->elsize; size_t nbytes = len * es; + if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) { + nbytes += len; + } char *olddata = (char*)a->data; int newbuf = array_resize_buffer(a, len); assert(newbuf); @@ -688,11 +726,16 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, size_t nbinc = inc * elsz; char *data = (char*)a->data; char *newdata; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); if (a->offset >= inc) { newdata = data - nbinc; a->offset -= inc; - if (idx > 0) { + if (idx > 0) memmove(newdata, data, idx * elsz); + if (isbitsunion) { + // move isbits union select bytes back by `inc` & zero out new selector bytes + memmove(data + n * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(data + n * elsz + idx, 0, inc); } } else { @@ -707,6 +750,11 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, if (!array_resize_buffer(a, newlen)) data = (char*)a->data + oldoffsnb; newdata = (char*)a->data + newoffset * elsz; + if (isbitsunion) { + memmove(newdata + newnrows * elsz, data + n * elsz, idx); + memmove(newdata + newnrows * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(newdata + newnrows * elsz + idx, 0, inc); + } // We could use memcpy if resizing allocates a new buffer, // hopefully it's not a particularly important optimization. if (idx > 0 && newdata < data) @@ -719,6 +767,10 @@ STATIC_INLINE void jl_array_grow_at_beg(jl_array_t *a, size_t idx, size_t inc, else { a->offset = (a->maxsize - newnrows) / 2; newdata = data - oldoffsnb + a->offset * elsz; + if (isbitsunion) { + memmove(newdata + newnrows * elsz + idx + inc, data + n * elsz + idx, n - idx); + memset(newdata + newnrows * elsz + idx, 0, inc); + } // We could use memcpy if resizing allocates a new buffer, // hopefully it's not a particularly important optimization. if (idx > 0 && newdata < data) @@ -754,6 +806,8 @@ STATIC_INLINE void jl_array_grow_at_end(jl_array_t *a, size_t idx, } size_t elsz = a->elsize; char *data = (char*)a->data; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); + size_t oldmaxsize = jl_array_len(a); int has_gap = n > idx; size_t reqmaxsize = a->offset + n + inc; if (__unlikely(reqmaxsize > a->maxsize)) { @@ -769,19 +823,43 @@ STATIC_INLINE void jl_array_grow_at_end(jl_array_t *a, size_t idx, char *newdata = (char*)a->data + a->offset * elsz; if (newbuf) { memcpy(newdata, data, nb1); + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz, data + oldmaxsize * elsz, idx); + } if (has_gap) { memcpy(newdata + nb1 + nbinc, data + nb1, n * elsz - nb1); + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz + idx + inc, data + oldmaxsize * elsz + idx, n - idx); + memset(newdata + (oldmaxsize + inc) * elsz + idx, 0, inc); + } } } else if (has_gap) { + if (isbitsunion) { + memmove(newdata + (oldmaxsize + inc) * elsz, newdata + oldmaxsize * elsz, idx); + memmove(newdata + (oldmaxsize + inc) * elsz + idx + inc, newdata + oldmaxsize * elsz + idx, n - idx); + memset(newdata + (oldmaxsize + inc) * elsz + idx, 0, inc); + } memmove(newdata + nb1 + nbinc, newdata + nb1, n * elsz - nb1); } a->data = data = newdata; } else if (has_gap) { size_t nb1 = idx * elsz; + if (isbitsunion) { + memmove(data + (n + inc) * elsz + idx + inc, data + n * elsz + idx, n - idx); + memmove(data + (n + inc) * elsz, data + n * elsz, idx); + memset(data + (n + inc) * elsz + idx, 0, inc); + } memmove(data + nb1 + inc * elsz, data + nb1, n * elsz - nb1); } + else { + if (isbitsunion) { + // need to move isbits union selector bytes back & zero out new bytes + memmove(data + (n + inc) * elsz, data + n * elsz, oldmaxsize); + memset(data + (n + inc) * elsz + idx, 0, inc); + } + } size_t newnrows = n + inc; #ifdef STORE_ARRAY_LEN a->length = newnrows; @@ -840,6 +918,7 @@ STATIC_INLINE void jl_array_del_at_beg(jl_array_t *a, size_t idx, size_t dec, // assume inbounds, assume unshared size_t elsz = a->elsize; size_t offset = a->offset; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); offset += dec; #ifdef STORE_ARRAY_LEN a->length = n - dec; @@ -854,15 +933,28 @@ STATIC_INLINE void jl_array_del_at_beg(jl_array_t *a, size_t idx, size_t dec, size_t nb1 = idx * elsz; // size in bytes of the first block size_t nbtotal = a->nrows * elsz; // size in bytes of the new array // Implicit '\0' for byte arrays - if (elsz == 1) + if (elsz == 1 && !isbitsunion) nbtotal++; - if (idx > 0) + if (idx > 0) { memmove(newdata, olddata, nb1); + if (isbitsunion) { + memmove(newdata + nbtotal, olddata + n * elsz, idx); + memset(newdata + nbtotal + idx, 0, dec); + } + } memmove(newdata + nb1, olddata + nb1 + nbdec, nbtotal - nb1); + if (isbitsunion) { + memmove(newdata + nbtotal + idx, olddata + n * elsz + idx + dec, n - idx); + } a->data = newdata; } else { - a->data = (char*)a->data + nbdec; + char *data = (char*)a->data; + a->data = data + nbdec; + if (isbitsunion) { + // move isbits union selector bytes forward, overwriting the deleted bytes + memmove(data + elsz * n, data + elsz * n + dec, n - dec); + } } a->offset = newoffs; } @@ -874,16 +966,25 @@ STATIC_INLINE void jl_array_del_at_end(jl_array_t *a, size_t idx, size_t dec, // assume inbounds, assume unshared char *data = (char*)a->data; size_t elsz = a->elsize; + int isbitsunion = !a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a))); size_t last = idx + dec; - if (n > last) + if (n > last) { memmove(data + idx * elsz, data + last * elsz, (n - last) * elsz); + if (isbitsunion) { + memmove(data + n * elsz + idx, data + n * elsz + last, n - last); + } + } n -= dec; - if (elsz == 1) + if (elsz == 1 && !isbitsunion) data[n] = 0; a->nrows = n; #ifdef STORE_ARRAY_LEN a->length = n; #endif + if (isbitsunion) { + // move last isbits union selector bytes forward to close the gap of deleted elements + memmove(data + n * elsz, data + (n + dec) * elsz, n); + } } JL_DLLEXPORT void jl_array_del_at(jl_array_t *a, ssize_t idx, size_t dec) @@ -941,9 +1042,15 @@ JL_DLLEXPORT void jl_array_sizehint(jl_array_t *a, size_t sz) JL_DLLEXPORT jl_array_t *jl_array_copy(jl_array_t *ary) { size_t elsz = ary->elsize; + size_t len = jl_array_len(ary); jl_array_t *new_ary = _new_array_(jl_typeof(ary), jl_array_ndims(ary), &ary->nrows, !ary->flags.ptrarray, elsz); - memcpy(new_ary->data, ary->data, jl_array_len(ary) * elsz); + memcpy(new_ary->data, ary->data, len * elsz); + // ensure isbits union arrays copy their selector bytes correctly + if (!ary->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(ary)))) { + memcpy((char*)new_ary->data + len * elsz, + (char*)ary->data + len * elsz, len); + } return new_ary; } diff --git a/src/ast.c b/src/ast.c index 984163f698626..b4e513703f7e8 100644 --- a/src/ast.c +++ b/src/ast.c @@ -7,13 +7,13 @@ #include #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "flisp.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/ast.scm b/src/ast.scm index 4b9d730a392b1..b4c46ab4a3509 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -305,6 +305,13 @@ (define (eq-sym? a b) (or (eq? a b) (and (ssavalue? a) (ssavalue? b) (eqv? (cdr a) (cdr b))))) +(define (blockify e) + (if (and (pair? e) (eq? (car e) 'block)) + (if (null? (cdr e)) + `(block (null)) + e) + `(block ,e))) + (define (make-var-info name) (list name '(core Any) 0)) (define vinfo:name car) (define vinfo:type cadr) diff --git a/src/builtins.c b/src/builtins.c index 4d099f4156a8a..0a5950e930d25 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include "julia_internal.h" #include "builtin_proto.h" #include "intrinsics.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -77,30 +77,40 @@ static int NOINLINE compare_svec(jl_svec_t *a, jl_svec_t *b) // See comment above for an explanation of NOINLINE. static int NOINLINE compare_fields(jl_value_t *a, jl_value_t *b, jl_datatype_t *dt) { - size_t nf = jl_datatype_nfields(dt); - for (size_t f=0; f < nf; f++) { + size_t f, nf = jl_datatype_nfields(dt); + for (f = 0; f < nf; f++) { size_t offs = jl_field_offset(dt, f); char *ao = (char*)jl_data_ptr(a) + offs; char *bo = (char*)jl_data_ptr(b) + offs; - int eq; if (jl_field_isptr(dt, f)) { jl_value_t *af = *(jl_value_t**)ao; jl_value_t *bf = *(jl_value_t**)bo; - if (af == bf) eq = 1; - else if (af==NULL || bf==NULL) eq = 0; - else eq = jl_egal(af, bf); + if (af != bf) { + if (af == NULL || bf == NULL) + return 0; + if (!jl_egal(af, bf)) + return 0; + } } else { jl_datatype_t *ft = (jl_datatype_t*)jl_field_type(dt, f); + if (jl_is_uniontype(ft)) { + uint8_t asel = ((uint8_t*)ao)[jl_field_size(dt, f) - 1]; + uint8_t bsel = ((uint8_t*)bo)[jl_field_size(dt, f) - 1]; + if (asel != bsel) + return 0; + ft = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)ft, asel); + } if (!ft->layout->haspadding) { - eq = bits_equal(ao, bo, jl_field_size(dt, f)); + if (!bits_equal(ao, bo, jl_field_size(dt, f))) + return 0; } else { assert(jl_datatype_nfields(ft) > 0); - eq = compare_fields((jl_value_t*)ao, (jl_value_t*)bo, ft); + if (!compare_fields((jl_value_t*)ao, (jl_value_t*)bo, ft)) + return 0; } } - if (!eq) return 0; } return 1; } @@ -127,9 +137,11 @@ JL_DLLEXPORT int jl_egal(jl_value_t *a, jl_value_t *b) return 0; return !memcmp(jl_string_data(a), jl_string_data(b), l); } - if (dt->mutabl) return 0; + if (dt->mutabl) + return 0; size_t sz = jl_datatype_size(dt); - if (sz == 0) return 1; + if (sz == 0) + return 1; size_t nf = jl_datatype_nfields(dt); if (nf == 0) return bits_equal(jl_data_ptr(a), jl_data_ptr(b), sz); @@ -161,10 +173,10 @@ static uintptr_t bits_hash(void *b, size_t sz) static uintptr_t NOINLINE hash_svec(jl_svec_t *v) { uintptr_t h = 0; - size_t l = jl_svec_len(v); - for(size_t i = 0; i < l; i++) { - jl_value_t *x = jl_svecref(v,i); - uintptr_t u = x==NULL ? 0 : jl_object_id(x); + size_t i, l = jl_svec_len(v); + for (i = 0; i < l; i++) { + jl_value_t *x = jl_svecref(v, i); + uintptr_t u = (x == NULL) ? 0 : jl_object_id(x); h = bitmix(h, u); } return h; @@ -188,9 +200,11 @@ static uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) if (dt == jl_typename_type) return ((jl_typename_t*)v)->hash; #ifdef _P64 - if (v == jl_ANY_flag) return 0x31c472f68ee30bddULL; + if (v == jl_ANY_flag) + return 0x31c472f68ee30bddULL; #else - if (v == jl_ANY_flag) return 0x8ee30bdd; + if (v == jl_ANY_flag) + return 0x8ee30bdd; #endif if (dt == jl_string_type) { #ifdef _P64 @@ -199,24 +213,29 @@ static uintptr_t jl_object_id_(jl_value_t *tv, jl_value_t *v) return memhash32_seed(jl_string_data(v), jl_string_len(v), 0xedc3b677); #endif } - if (dt->mutabl) return inthash((uintptr_t)v); + if (dt->mutabl) + return inthash((uintptr_t)v); size_t sz = jl_datatype_size(tv); uintptr_t h = jl_object_id(tv); - if (sz == 0) return ~h; - size_t nf = jl_datatype_nfields(dt); - if (nf == 0) { + if (sz == 0) + return ~h; + size_t f, nf = jl_datatype_nfields(dt); + if (nf == 0) return bits_hash(jl_data_ptr(v), sz) ^ h; - } - for (size_t f=0; f < nf; f++) { + for (f = 0; f < nf; f++) { size_t offs = jl_field_offset(dt, f); char *vo = (char*)jl_data_ptr(v) + offs; uintptr_t u; if (jl_field_isptr(dt, f)) { jl_value_t *f = *(jl_value_t**)vo; - u = f==NULL ? 0 : jl_object_id(f); + u = (f == NULL) ? 0 : jl_object_id(f); } else { jl_datatype_t *fieldtype = (jl_datatype_t*)jl_field_type(dt, f); + if (jl_is_uniontype(fieldtype)) { + uint8_t sel = ((uint8_t*)vo)[jl_field_size(dt, f) - 1]; + fieldtype = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)fieldtype, sel); + } assert(jl_is_datatype(fieldtype) && !fieldtype->abstract && !fieldtype->mutabl); if (fieldtype->layout->haspadding) u = jl_object_id_((jl_value_t*)fieldtype, (jl_value_t*)vo); @@ -244,7 +263,7 @@ JL_CALLABLE(jl_f_is) JL_NARGS(===, 2, 2); if (args[0] == args[1]) return jl_true; - return jl_egal(args[0],args[1]) ? jl_true : jl_false; + return jl_egal(args[0], args[1]) ? jl_true : jl_false; } JL_CALLABLE(jl_f_typeof) @@ -259,6 +278,10 @@ JL_CALLABLE(jl_f_sizeof) jl_value_t *x = args[0]; if (jl_is_unionall(x) || jl_is_uniontype(x)) { x = jl_unwrap_unionall(x); + size_t elsize = 0, al = 0; + int isinline = jl_islayout_inline(x, &elsize, &al); + if (isinline) + return jl_box_long(elsize); if (!jl_is_datatype(x)) jl_error("argument is an abstract type; size is indeterminate"); } @@ -270,8 +293,9 @@ JL_CALLABLE(jl_f_sizeof) jl_error("type does not have a fixed size"); return jl_box_long(jl_datatype_size(x)); } - if (jl_is_array(x)) + if (jl_is_array(x)) { return jl_box_long(jl_array_len(x) * ((jl_array_t*)x)->elsize); + } if (jl_is_string(x)) return jl_box_long(jl_string_len(x)); if (jl_is_symbol(x)) @@ -604,6 +628,10 @@ JL_CALLABLE(jl_f_svec) JL_CALLABLE(jl_f_getfield) { + if (nargs == 3) { + JL_TYPECHK(getfield, bool, args[2]); + nargs -= 1; + } JL_NARGS(getfield, 2, 2); jl_value_t *v = args[0]; jl_value_t *vt = (jl_value_t*)jl_typeof(v); @@ -695,6 +723,10 @@ static jl_value_t *get_fieldtype(jl_value_t *t, jl_value_t *f) JL_CALLABLE(jl_f_fieldtype) { + if (nargs == 3) { + JL_TYPECHK(fieldtype, bool, args[2]); + nargs -= 1; + } JL_NARGS(fieldtype, 2, 2); jl_datatype_t *st = (jl_datatype_t*)args[0]; if (st == jl_module_type) @@ -920,20 +952,20 @@ JL_CALLABLE(jl_f_arraysize) static size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, const char *fname) { - size_t i=0; - size_t k, stride=1; + size_t i = 0; + size_t k, stride = 1; size_t nd = jl_array_ndims(a); - for(k=0; k < nidxs; k++) { + for (k = 0; k < nidxs; k++) { if (!jl_is_long(args[k])) jl_type_error(fname, (jl_value_t*)jl_long_type, args[k]); - size_t ii = jl_unbox_long(args[k])-1; + size_t ii = jl_unbox_long(args[k]) - 1; i += ii * stride; - size_t d = k>=nd ? 1 : jl_array_dim(a, k); - if (k < nidxs-1 && ii >= d) + size_t d = (k >= nd) ? 1 : jl_array_dim(a, k); + if (k < nidxs - 1 && ii >= d) jl_bounds_error_v((jl_value_t*)a, args, nidxs); stride *= d; } - for(; k < nd; k++) + for (; k < nd; k++) stride *= jl_array_dim(a, k); if (i >= stride) jl_bounds_error_v((jl_value_t*)a, args, nidxs); @@ -942,21 +974,23 @@ static size_t array_nd_index(jl_array_t *a, jl_value_t **args, size_t nidxs, JL_CALLABLE(jl_f_arrayref) { - JL_NARGSV(arrayref, 2); - JL_TYPECHK(arrayref, array, args[0]); - jl_array_t *a = (jl_array_t*)args[0]; - size_t i = array_nd_index(a, &args[1], nargs-1, "arrayref"); + JL_NARGSV(arrayref, 3); + JL_TYPECHK(arrayref, bool, args[0]); + JL_TYPECHK(arrayref, array, args[1]); + jl_array_t *a = (jl_array_t*)args[1]; + size_t i = array_nd_index(a, &args[2], nargs - 2, "arrayref"); return jl_arrayref(a, i); } JL_CALLABLE(jl_f_arrayset) { - JL_NARGSV(arrayset, 3); - JL_TYPECHK(arrayset, array, args[0]); - jl_array_t *a = (jl_array_t*)args[0]; - size_t i = array_nd_index(a, &args[2], nargs-2, "arrayset"); - jl_arrayset(a, args[1], i); - return args[0]; + JL_NARGSV(arrayset, 4); + JL_TYPECHK(arrayset, bool, args[0]); + JL_TYPECHK(arrayset, array, args[1]); + jl_array_t *a = (jl_array_t*)args[1]; + size_t i = array_nd_index(a, &args[3], nargs - 3, "arrayset"); + jl_arrayset(a, args[2], i); + return args[1]; } // IntrinsicFunctions --------------------------------------------------------- diff --git a/src/ccall.cpp b/src/ccall.cpp index 01f622623db34..0554685f84148 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -182,7 +182,7 @@ static Value *runtime_sym_lookup( PHINode *p = irbuilder.CreatePHI(T_pvoidfunc, 2); p->addIncoming(llvmf_orig, enter_bb); p->addIncoming(llvmf, dlsym_lookup); - return irbuilder.CreatePointerCast(p, funcptype); + return irbuilder.CreateBitCast(p, funcptype); } static Value *runtime_sym_lookup( @@ -534,7 +534,7 @@ static Value *julia_to_address( { assert(jl_is_datatype(jlto) && julia_struct_has_layout((jl_datatype_t*)jlto, jlto_env)); - if (!jl_is_cpointer_type(jlto) || !to->isPointerTy()) { + if (!jl_is_cpointer_type(jlto) || to != T_size) { emit_error(ctx, "ccall: & on argument was not matched by Ptr{T} argument type"); return UndefValue::get(to); } @@ -547,19 +547,18 @@ static Value *julia_to_address( ety = jl_tparam0(jlto); typeassert_input(ctx, jvinfo, ety, jlto_env, argn, true); } - assert(to->isPointerTy()); if (jvinfo.isboxed) { if (!jl_is_abstracttype(ety)) { if (jl_is_mutable_datatype(ety)) { // no copy, just reference the data field - return data_pointer(ctx, jvinfo, to); + return ctx.builder.CreateBitCast(emit_pointer_from_objref(ctx, data_pointer(ctx, jvinfo)), to); } - else if (jl_is_immutable_datatype(ety) && jlto != (jl_value_t*)jl_voidpointer_type) { + else if (jl_is_immutable_datatype(ety) && jlto != (jl_value_t*)jl_voidpointer_type) { // anything declared `struct`, except Ptr{Void} // yes copy Value *nbytes; AllocaInst *ai; - if (jl_is_leaf_type(ety) || jl_is_primitivetype(ety)) { + if (((jl_datatype_t*)ety)->layout) { int nb = jl_datatype_size(ety); nbytes = ConstantInt::get(T_int32, nb); ai = emit_static_alloca(ctx, T_int8, nb); @@ -571,7 +570,7 @@ static Value *julia_to_address( } ai->setAlignment(16); ctx.builder.CreateMemCpy(ai, data_pointer(ctx, jvinfo, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size - return emit_bitcast(ctx, ai, to); + return ctx.builder.CreatePtrToInt(ai, to); } } // emit maybe copy @@ -583,14 +582,14 @@ static Value *julia_to_address( Value *ismutable = emit_datatype_mutabl(ctx, jvt); ctx.builder.CreateCondBr(ismutable, mutableBB, immutableBB); ctx.builder.SetInsertPoint(mutableBB); - Value *p1 = data_pointer(ctx, jvinfo, to); + Value *p1 = ctx.builder.CreateBitCast(emit_pointer_from_objref(ctx, data_pointer(ctx, jvinfo)), to); ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(immutableBB); Value *nbytes = emit_datatype_size(ctx, jvt); AllocaInst *ai = ctx.builder.CreateAlloca(T_int8, nbytes); ai->setAlignment(16); ctx.builder.CreateMemCpy(ai, data_pointer(ctx, jvinfo, T_pint8), nbytes, sizeof(void*)); // minimum gc-alignment in julia is pointer size - Value *p2 = emit_bitcast(ctx, ai, to); + Value *p2 = ctx.builder.CreatePtrToInt(ai, to); ctx.builder.CreateBr(afterBB); ctx.builder.SetInsertPoint(afterBB); PHINode *p = ctx.builder.CreatePHI(to, 2); @@ -612,9 +611,7 @@ static Value *julia_to_address( (uint64_t)jl_datatype_size(ety), (uint64_t)jl_datatype_align(ety)); } - if (slot->getType() != to) - slot = emit_bitcast(ctx, slot, to); - return slot; + return ctx.builder.CreatePtrToInt(slot, to); } @@ -781,22 +778,21 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg rt = (jl_value_t*)jl_voidpointer_type; } Type *lrt = julia_type_to_llvm(rt); - if (lrt == NULL) - lrt = T_pint8; interpret_symbol_arg(ctx, sym, args[1], "cglobal", false); if (sym.jl_ptr != NULL) { - res = ctx.builder.CreateIntToPtr(sym.jl_ptr, lrt); + res = ctx.builder.CreateBitCast(sym.jl_ptr, lrt); } else if (sym.fptr != NULL) { - res = literal_static_pointer_val(ctx, (void*)(uintptr_t)sym.fptr, lrt); + res = ConstantInt::get(lrt, (uint64_t)sym.fptr); if (imaging_mode) jl_printf(JL_STDERR,"WARNING: literal address used in cglobal for %s; code cannot be statically compiled\n", sym.f_name); } else { if (imaging_mode) { - res = runtime_sym_lookup(ctx, (PointerType*)lrt, sym.f_lib, sym.f_name, ctx.f); + res = runtime_sym_lookup(ctx, cast(T_pint8), sym.f_lib, sym.f_name, ctx.f); + res = ctx.builder.CreatePtrToInt(res, lrt); } else { void *symaddr = jl_dlsym_e(jl_get_library(sym.f_lib), sym.f_name); @@ -815,7 +811,7 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg } // since we aren't saving this code, there's no sense in // putting anything complicated here: just JIT the address of the cglobal - res = literal_static_pointer_val(ctx, symaddr, lrt); + res = ConstantInt::get(lrt, (uint64_t)symaddr); } } @@ -1495,7 +1491,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else { Value *notany = ctx.builder.CreateICmpNE( - boxed(ctx, runtime_sp, false), + boxed(ctx, runtime_sp), maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_any_type))); error_unless(ctx, notany, "ccall: return type Ref{Any} is invalid. use Ptr{Any} instead."); always_error = false; @@ -1513,12 +1509,6 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) rt = (jl_value_t*)jl_any_type; // convert return type to jl_value_t* } - // check if we require the runtime - // TODO: could be more fine-grained, - // respecting special functions below that don't require the runtime - if (!llvmcall && (!f_lib || f_lib == JL_DL_LIBNAME)) - JL_FEAT_REQUIRE(ctx, runtime); - // some sanity checking and check whether there's a vararg bool isVa; size_t nargt; @@ -1583,25 +1573,24 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (jl_is_long(argi_root)) continue; jl_cgval_t arg_root = emit_expr(ctx, argi_root); - Value *gcuse = arg_root.gcroot ? ctx.builder.CreateLoad(arg_root.gcroot) : arg_root.V; - if (gcuse) { - gc_uses.push_back(gcuse); + if (arg_root.Vboxed || arg_root.V) { + gc_uses.push_back(arg_root.Vboxed ? arg_root.Vboxed : arg_root.V); } } // some special functions if (is_libjulia_func(jl_array_ptr)) { - assert(lrt->isPointerTy()); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 1); assert(!addressOf.at(0)); const jl_cgval_t &ary = argv[0]; jl_value_t *aryex = ccallarg(0); JL_GC_POP(); - return mark_or_box_ccall_result(ctx, emit_bitcast(ctx, emit_arrayptr(ctx, ary, aryex), lrt), + return mark_or_box_ccall_result(ctx, ctx.builder.CreatePtrToInt(emit_arrayptr(ctx, ary, aryex), lrt), retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_value_ptr)) { - assert(lrt->isPointerTy()); + assert(retboxed ? lrt == T_prjlvalue : lrt == T_size); assert(!isVa && !llvmcall && nargt == 1); jl_value_t *tti = jl_svecref(at, 0); Value *ary; @@ -1613,7 +1602,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (jl_is_abstract_ref_type(tti)) { tti = (jl_value_t*)jl_voidpointer_type; - largty = T_pint8; + largty = T_size; isboxed = false; } else { @@ -1629,13 +1618,16 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) if (!retboxed) { return mark_or_box_ccall_result( ctx, - emit_bitcast(ctx, emit_pointer_from_objref(ctx, - emit_bitcast(ctx, ary, T_prjlvalue)), lrt), + emit_pointer_from_objref(ctx, + emit_bitcast(ctx, ary, T_prjlvalue)), retboxed, rt, unionall, static_rt); - } else { + } + else { return mark_or_box_ccall_result( ctx, - maybe_decay_untracked(emit_bitcast(ctx, ary, lrt)), + ctx.builder.CreateAddrSpaceCast( + ctx.builder.CreateIntToPtr(ary, T_pjlvalue), + T_prjlvalue), // TODO: this addrspace cast is invalid (implies that the value is rooted elsewhere) retboxed, rt, unionall, static_rt); } } @@ -1687,12 +1679,19 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) emit_signal_fence(ctx); return ghostValue(jl_void_type); } + else if (is_libjulia_func(jl_gc_use)) { + assert(lrt == T_void); + assert(!isVa && !llvmcall && nargt == 1); + ctx.builder.CreateCall(prepare_call(gc_use_func), {decay_derived(boxed(ctx, argv[0]))}); + JL_GC_POP(); + return ghostValue(jl_void_type); + } else if (_is_libjulia_func((uintptr_t)ptls_getter, "jl_get_ptls_states")) { - assert(lrt == T_pint8); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 0); JL_GC_POP(); return mark_or_box_ccall_result(ctx, - emit_bitcast(ctx, ctx.ptlsStates, lrt), + ctx.builder.CreatePtrToInt(ctx.ptlsStates, lrt), retboxed, rt, unionall, static_rt); } else if (is_libjulia_func(jl_threadid)) { @@ -1765,6 +1764,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } else if (is_libjulia_func(jl_function_ptr)) { assert(!isVa && !llvmcall && nargt == 3); + assert(lrt == T_size); jl_value_t *f = argv[0].constant; jl_value_t *frt = argv[1].constant; if (!frt) { @@ -1791,11 +1791,10 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) llvmf = NULL; } if (llvmf) { - llvmf = prepare_call(llvmf); JL_GC_POP(); JL_GC_POP(); - return mark_or_box_ccall_result(ctx, emit_bitcast(ctx, llvmf, lrt), - retboxed, rt, unionall, static_rt); + Value *fptr = ctx.builder.CreatePtrToInt(prepare_call(llvmf), lrt); + return mark_or_box_ccall_result(ctx, fptr, retboxed, rt, unionall, static_rt); } } JL_GC_POP(); @@ -1827,10 +1826,10 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) } } else if (is_libjulia_func(jl_string_ptr)) { - assert(lrt == T_pint8); + assert(lrt == T_size); assert(!isVa && !llvmcall && nargt == 1 && !addressOf.at(0)); - auto obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); - auto strp = ctx.builder.CreateConstGEP1_32(emit_bitcast(ctx, obj, T_pint8), sizeof(void*)); + Value *obj = emit_pointer_from_objref(ctx, boxed(ctx, argv[0])); + Value *strp = ctx.builder.CreateAdd(obj, ConstantInt::get(T_size, sizeof(void*))); JL_GC_POP(); return mark_or_box_ccall_result(ctx, strp, retboxed, rt, unionall, static_rt); } @@ -1933,9 +1932,6 @@ jl_cgval_t function_sig_t::emit_a_ccall( if (isa(v)) { return jl_cgval_t(); } - // A bit of a hack, but we're trying to get rid of this feature - // anyway. - v = emit_bitcast(ctx, emit_pointer_from_objref(ctx, v), pargty); assert((!toboxed && !byRef) || isa(v)); } diff --git a/src/cgmemmgr.cpp b/src/cgmemmgr.cpp index b4f0ff0969b69..595167f718000 100644 --- a/src/cgmemmgr.cpp +++ b/src/cgmemmgr.cpp @@ -5,7 +5,6 @@ #include "options.h" #include -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" @@ -25,6 +24,7 @@ #ifdef _OS_FREEBSD_ # include #endif +#include "julia_assert.h" namespace { diff --git a/src/cgutils.cpp b/src/cgutils.cpp index c0a3b41b21118..5d22116977751 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -97,22 +97,15 @@ static Value *mark_callee_rooted(IRBuilder<> &irbuilder, Value *V) // --- language feature checks --- -// branch on whether a language feature is enabled or not #define JL_FEAT_TEST(ctx, feature) ((ctx).params->feature) -// require a language feature to be enabled -#define JL_FEAT_REQUIRE(ctx, feature) \ - if (!JL_FEAT_TEST(ctx, feature)) \ - jl_errorf("%s for %s:%d requires the " #feature " language feature, which is disabled", \ - __FUNCTION__, (ctx).file.str().c_str(), *(ctx).line); - // --- hook checks --- -#define JL_HOOK_TEST(params,hook) ((params)->hooks.hook != jl_nothing) +#define JL_HOOK_TEST(params,hook) ((params)->hook != jl_nothing) #define JL_HOOK_CALL(params,hook,argc,...) \ - _hook_call((params)->hooks.hook, {{__VA_ARGS__}}); + _hook_call((params)->hook, {{__VA_ARGS__}}); template static inline void _hook_call(jl_value_t *hook, std::array args) { jl_value_t **argv; @@ -170,15 +163,8 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe return jl_pvalue_dillvmt; jl_datatype_t *jdt = (jl_datatype_t*)jt; // always return the boxed representation for types with hidden content - if (jl_is_abstracttype(jt) || jl_is_array_type(jt) || - jt == (jl_value_t*)jl_sym_type || jt == (jl_value_t*)jl_module_type || - jt == (jl_value_t*)jl_simplevector_type || jt == (jl_value_t*)jl_datatype_type || - jt == (jl_value_t*)jl_method_instance_type) - return jl_pvalue_dillvmt; - if (jdt->ditype != NULL) { - DIType* t = (DIType*)jdt->ditype; - return t; - } + if (jdt->ditype != NULL) + return (DIType*)jdt->ditype; if (jl_is_primitivetype(jt)) { uint64_t SizeInBits = jl_datatype_nbits(jdt); #if JL_LLVM_VERSION >= 40000 @@ -198,11 +184,11 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe return t; #endif } - if (jl_is_structtype(jt) && jdt->layout) { + if (jl_is_structtype(jt) && jdt->uid && jdt->layout && !jl_is_layout_opaque(jdt->layout)) { size_t ntypes = jl_datatype_nfields(jdt); const char *tname = jl_symbol_name(jdt->name->name); std::stringstream unique_name; - unique_name << tname << "_" << globalUnique++; + unique_name << jdt->uid; llvm::DICompositeType *ct = dbuilder->createStructType( NULL, // Scope tname, // Name @@ -219,8 +205,15 @@ static DIType *julia_type_to_di(jl_value_t *jt, DIBuilder *dbuilder, bool isboxe ); jdt->ditype = ct; std::vector Elements; - for (unsigned i = 0; i < ntypes; i++) - Elements.push_back(julia_type_to_di(jl_svecref(jdt->types, i), dbuilder, false)); + for (unsigned i = 0; i < ntypes; i++) { + jl_value_t *el = jl_svecref(jdt->types, i); + DIType *di; + if (jl_field_isptr(jdt, i)) + di = jl_pvalue_dillvmt; + else + di = julia_type_to_di(el, dbuilder, false); + Elements.push_back(di); + } dbuilder->replaceArrays(ct, dbuilder->getOrCreateArray(ArrayRef(Elements))); return ct; } @@ -233,7 +226,7 @@ static Value *emit_pointer_from_objref(jl_codectx_t &ctx, Value *V) { unsigned AS = cast(V->getType())->getAddressSpace(); if (AS != AddressSpace::Tracked && AS != AddressSpace::Derived) - return ctx.builder.CreateBitCast(V, T_pjlvalue); + return ctx.builder.CreatePtrToInt(V, T_size); V = ctx.builder.CreateBitCast(decay_derived(V), PointerType::get(T_jlvalue, AddressSpace::Derived)); CallInst *Call = ctx.builder.CreateCall(prepare_call(pointer_from_objref_func), V); @@ -427,18 +420,14 @@ static Type *bitstype_to_llvm(jl_value_t *bt) assert(jl_is_primitivetype(bt)); if (bt == (jl_value_t*)jl_bool_type) return T_int8; - if (bt == (jl_value_t*)jl_long_type) - return T_size; + if (bt == (jl_value_t*)jl_int32_type) + return T_int32; + if (bt == (jl_value_t*)jl_int64_type) + return T_int64; if (bt == (jl_value_t*)jl_float32_type) return T_float32; if (bt == (jl_value_t*)jl_float64_type) return T_float64; - if (jl_is_cpointer_type(bt)) { - Type *lt = julia_type_to_llvm(jl_tparam0(bt)); - if (lt == T_void) - return T_pint8; - return PointerType::get(lt, 0); - } int nb = jl_datatype_size(bt); return Type::getIntNTy(jl_LLVMContext, nb * 8); } @@ -461,6 +450,14 @@ static bool julia_struct_has_layout(jl_datatype_t *dt, jl_unionall_t *ua) return true; } +static unsigned jl_field_align(jl_datatype_t *dt, size_t i) +{ + unsigned al = jl_field_offset(dt, i); + al |= 16; + al &= -al; + return std::min(al, jl_datatype_align(dt)); +} + static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isboxed) { // this function converts a Julia Type into the equivalent LLVM struct @@ -473,93 +470,117 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox return bitstype_to_llvm(jt); bool isTuple = jl_is_tuple_type(jt); jl_datatype_t *jst = (jl_datatype_t*)jt; + if (jst->struct_decl != NULL) + return (Type*)jst->struct_decl; if (jl_is_structtype(jt) && !(jst->layout && jl_is_layout_opaque(jst->layout))) { - if (jst->struct_decl == NULL) { - size_t i, ntypes = jl_svec_len(jst->types); - if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) - return T_void; - if (!julia_struct_has_layout(jst, ua)) - return NULL; - StructType *structdecl; - if (!isTuple) { - structdecl = StructType::create(jl_LLVMContext, jl_symbol_name(jst->name->name)); - jst->struct_decl = structdecl; - } - std::vector latypes(0); - bool isarray = true; - bool isvector = true; - jl_value_t *jlasttype = NULL; - Type *lasttype = NULL; - bool allghost = true; - for (i = 0; i < ntypes; i++) { - jl_value_t *ty = jl_svecref(jst->types, i); - if (jlasttype != NULL && ty != jlasttype) - isvector = false; - jlasttype = ty; - bool isptr; - if (jst->layout) - isptr = jl_field_isptr(jst, i); - else // compute what jl_compute_field_offsets would say - isptr = jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout; - Type *lty; - if (isptr) - lty = T_pjlvalue; - else if (ty == (jl_value_t*)jl_bool_type) - lty = T_int8; - else - lty = julia_type_to_llvm(ty); - if (lasttype != NULL && lasttype != lty) - isarray = false; - lasttype = lty; - if (type_is_ghost(lty)) - lty = NoopType; - else - allghost = false; - latypes.push_back(lty); + size_t i, ntypes = jl_svec_len(jst->types); + if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) + return T_void; + if (!julia_struct_has_layout(jst, ua)) + return NULL; + std::vector latypes(ntypes); + bool isarray = true; + bool isvector = true; + jl_value_t *jlasttype = NULL; + Type *lasttype = NULL; + bool allghost = true; + for (i = 0; i < ntypes; i++) { + jl_value_t *ty = jl_svecref(jst->types, i); + if (jlasttype != NULL && ty != jlasttype) + isvector = false; + jlasttype = ty; + bool isptr; + size_t fsz = 0, al = 0; + if (jst->layout) { + isptr = jl_field_isptr(jst, i); + fsz = jl_field_size(jst, i); + al = jl_field_align(jst, i); } - if (allghost) { - assert(jst->layout == NULL); // otherwise should have been caught above - jst->struct_decl = T_void; + else { // compute what jl_compute_field_offsets would say + isptr = !jl_islayout_inline(ty, &fsz, &al); + if (!isptr && jl_is_uniontype(jst)) + fsz += 1; } - else if (!isTuple) { - if (jl_is_vecelement_type(jt)) - // VecElement type is unwrapped in LLVM - jst->struct_decl = latypes[0]; - else - structdecl->setBody(latypes); - } - else { - if (isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { - if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) - jst->struct_decl = VectorType::get(lasttype, ntypes); - else - jst->struct_decl = ArrayType::get(lasttype, ntypes); - } - else { - jst->struct_decl = StructType::get(jl_LLVMContext, ArrayRef(&latypes[0], ntypes)); - } + Type *lty; + if (isptr) + lty = T_pjlvalue; + else if (ty == (jl_value_t*)jl_bool_type) + lty = T_int8; + else if (jl_is_uniontype(ty)) { + // pick an Integer type size such that alignment will be correct + // and always end with an Int8 (selector byte) + lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + std::vector Elements(2); + Elements[0] = lty; + Elements[1] = T_int8; + unsigned remainder = (fsz - 1) % al; + while (remainder--) + Elements.push_back(T_int8); + lty = StructType::get(jl_LLVMContext, Elements); } + else + lty = julia_type_to_llvm(ty); + if (lasttype != NULL && lasttype != lty) + isarray = false; + lasttype = lty; + if (type_is_ghost(lty)) + lty = NoopType; + else + allghost = false; + latypes[i] = lty; + } + Type *decl; + if (allghost) { + assert(jst->layout == NULL); // otherwise should have been caught above + decl = T_void; + } + else if (jl_is_vecelement_type(jt)) { + // VecElement type is unwrapped in LLVM + decl = latypes[0]; + } + else if (isTuple && isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { + if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) + decl = VectorType::get(lasttype, ntypes); + else + decl = ArrayType::get(lasttype, ntypes); + } + else { + decl = StructType::get(jl_LLVMContext, latypes); + } + jst->struct_decl = decl; #ifndef JL_NDEBUG - // If LLVM and Julia disagree about alignment, much trouble ensues, so check it! - if (jst->layout) { - const DataLayout &DL = + // If LLVM and Julia disagree about alignment, much trouble ensues, so check it! + if (jst->layout) { + const DataLayout &DL = #if JL_LLVM_VERSION >= 40000 - jl_data_layout; + jl_data_layout; #else - jl_ExecutionEngine->getDataLayout(); -#endif - unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); - unsigned julia_alignment = jl_datatype_align(jst); - // Check that the alignment adheres to the heap alignment. - assert(julia_alignment <= JL_HEAP_ALIGNMENT); - // TODO: Fix alignment calculation in LLVM, as well as in the GC and the struct declaration - if (llvm_alignment <= JL_HEAP_ALIGNMENT) - assert(julia_alignment == llvm_alignment); - } + jl_ExecutionEngine->getDataLayout(); #endif + unsigned llvm_alignment = DL.getABITypeAlignment((Type*)jst->struct_decl); + unsigned julia_alignment = jl_datatype_align(jst); + // Check that the alignment adheres to the heap alignment. + assert(julia_alignment <= JL_HEAP_ALIGNMENT); + // TODO: Fix alignment calculation in LLVM, as well as in the GC and the struct declaration + if (llvm_alignment <= JL_HEAP_ALIGNMENT) + assert(julia_alignment == llvm_alignment); } - return (Type*)jst->struct_decl; - } +#endif + return decl; + } + // TODO: enable this (with tests): + // if (jl_is_uniontype(ty)) { + // // pick an Integer type size such that alignment will be correct + // // and always end with an Int8 (selector byte) + // lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + // std::vector Elements(2); + // Elements[0] = lty; + // Elements[1] = T_int8; + // unsigned remainder = (fsz - 1) % al; + // while (remainder--) + // Elements.push_back(T_int8); + // lty = StructType::get(jl_LLVMContext, makeArrayRef(Elements)); + // } if (isboxed) *isboxed = true; return T_pjlvalue; } @@ -567,7 +588,7 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox static bool is_datatype_all_pointers(jl_datatype_t *dt) { size_t i, l = jl_datatype_nfields(dt); - for(i=0; i < l; i++) { + for (i = 0; i < l; i++) { if (!jl_field_isptr(dt, i)) { return false; } @@ -586,9 +607,9 @@ static bool is_tupletype_homogeneous(jl_svec_t *t, bool allow_va = false) return true; return false; } - for(i=1; i < l; i++) { - if (allow_va && i == l - 1 && jl_is_vararg_type(jl_svecref(t,i))) { - if (t0 != jl_unwrap_vararg(jl_svecref(t,i))) + for (i = 1; i < l; i++) { + if (allow_va && i == l - 1 && jl_is_vararg_type(jl_svecref(t, i))) { + if (t0 != jl_unwrap_vararg(jl_svecref(t, i))) return false; continue; } @@ -683,8 +704,7 @@ static Value *emit_typeptr_addr(jl_codectx_t &ctx, Value *p) return emit_nthptr_addr(ctx, p, -offset); } -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, bool gcooted=true); -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t* type) = delete; // C++11 (temporary to prevent rebase error) +static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &v); static Value* mask_gc_bits(jl_codectx_t &ctx, Value *tag) { @@ -713,7 +733,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) if (p.constant) return mark_julia_const(jl_typeof(p.constant)); if (p.isboxed && !jl_is_leaf_type(p.typ)) { - return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type, /*needsroot*/false); + return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type); } if (p.TIndex) { Value *tindex = ctx.builder.CreateAnd(p.TIndex, ConstantInt::get(T_int8, 0x7f)); @@ -729,7 +749,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) else { // See note above in emit_typeof(Value*), we can't tell the system // about this until we've cleared the GC bits. - pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, ctx.builder.CreateLoad(p.gcroot)), T_ppjlvalue); + pdatatype = emit_bitcast(ctx, emit_typeptr_addr(ctx, p.Vboxed), T_ppjlvalue); } counter = 0; for_each_uniontype_small( @@ -746,7 +766,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) if (!allunboxed) datatype = mask_gc_bits(ctx, datatype); datatype = maybe_decay_untracked(datatype); - return mark_julia_type(ctx, datatype, true, jl_datatype_type, /*needsroot*/false); + return mark_julia_type(ctx, datatype, true, jl_datatype_type); } jl_value_t *aty = p.typ; if (jl_is_type_type(aty)) { @@ -908,7 +928,6 @@ static void raise_exception(jl_codectx_t &ctx, Value *exc, jl_box_voidpointer(wrap(ctx.builder.GetInsertBlock())), jl_box_voidpointer(wrap(exc))); } else { - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jlthrow_func), { mark_callee_rooted(exc) }); } ctx.builder.CreateUnreachable(); @@ -986,7 +1005,7 @@ static void emit_type_error(jl_codectx_t &ctx, const jl_cgval_t &x, Value *type, Value *msg_val = stringConstPtr(ctx.builder, msg); ctx.builder.CreateCall(prepare_call(jltypeerror_func), { fname_val, msg_val, - maybe_decay_untracked(type), mark_callee_rooted(boxed(ctx, x, false))}); + maybe_decay_untracked(type), mark_callee_rooted(boxed(ctx, x))}); } static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *type, const std::string *msg) @@ -1029,7 +1048,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, Value *xtindex = ctx.builder.CreateAnd(x.TIndex, ConstantInt::get(T_int8, 0x7f)); return std::make_pair(ctx.builder.CreateICmpEQ(xtindex, ConstantInt::get(T_int8, tindex)), false); } - else { + else if (x.Vboxed) { // test for (x.TIndex == 0x80 && typeof(x.V) == type) Value *isboxed = ctx.builder.CreateICmpEQ(x.TIndex, ConstantInt::get(T_int8, 0x80)); BasicBlock *currBB = ctx.builder.GetInsertBlock(); @@ -1037,7 +1056,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_isa", ctx.f); ctx.builder.CreateCondBr(isboxed, isaBB, postBB); ctx.builder.SetInsertPoint(isaBB); - Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.V), + Value *istype_boxed = ctx.builder.CreateICmpEQ(emit_typeof(ctx, x.Vboxed), maybe_decay_untracked(literal_pointer_val(ctx, type))); ctx.builder.CreateBr(postBB); ctx.builder.SetInsertPoint(postBB); @@ -1045,6 +1064,9 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, istype->addIncoming(ConstantInt::get(T_int1, 0), currBB); istype->addIncoming(istype_boxed, isaBB); return std::make_pair(istype, false); + } else { + // handle the case where we know that `x` is unboxed (but of unknown type), but that leaf type `type` cannot be unboxed + return std::make_pair(ConstantInt::get(T_int1, 0), false); } } return std::make_pair(ctx.builder.CreateICmpEQ(emit_typeof_boxed(ctx, x), @@ -1080,7 +1102,7 @@ static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *t static void emit_leafcheck(jl_codectx_t &ctx, Value *typ, const std::string &msg) { assert(typ->getType() == T_prjlvalue); - emit_typecheck(ctx, mark_julia_type(ctx, typ, true, jl_any_type, false), (jl_value_t*)jl_datatype_type, msg); + emit_typecheck(ctx, mark_julia_type(ctx, typ, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); Value *isleaf; isleaf = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, emit_bitcast(ctx, decay_derived(typ), T_pint8), offsetof(jl_datatype_t, isleaftype)); isleaf = ctx.builder.CreateLoad(isleaf, tbaa_const); @@ -1089,24 +1111,28 @@ static void emit_leafcheck(jl_codectx_t &ctx, Value *typ, const std::string &msg } #define CHECK_BOUNDS 1 -static bool bounds_check_enabled(jl_codectx_t &ctx) { +static bool bounds_check_enabled(jl_codectx_t &ctx, jl_value_t *inbounds) { #if CHECK_BOUNDS==1 - return (!ctx.is_inbounds && - jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) || - jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON; + if (jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON) + return 1; + if (jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_OFF) + return 0; + if (inbounds == jl_false) + return 0; + return 1; #else return 0; #endif } -static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len) +static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ty, Value *i, Value *len, jl_value_t *boundscheck) { Value *im1 = ctx.builder.CreateSub(i, ConstantInt::get(T_size, 1)); #if CHECK_BOUNDS==1 - if (bounds_check_enabled(ctx)) { + if (bounds_check_enabled(ctx, boundscheck)) { Value *ok = ctx.builder.CreateICmpULT(im1, len); - BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext,"fail",ctx.f); - BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext,"pass"); + BasicBlock *failBB = BasicBlock::Create(jl_LLVMContext, "fail", ctx.f); + BasicBlock *passBB = BasicBlock::Create(jl_LLVMContext, "pass"); ctx.builder.CreateCondBr(ok, passBB, failBB); ctx.builder.SetInsertPoint(failBB); if (!ty) { // jl_value_t** tuple (e.g. the vararg) @@ -1139,8 +1165,6 @@ static Value *emit_bounds_check(jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_v return im1; } -// --- loading and storing --- - // If given alignment is 0 and LLVM's assumed alignment for a load/store via ptr // might be stricter than the Julia alignment for jltype, return the alignment of jltype. // Otherwise return the given alignment. @@ -1166,12 +1190,12 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j Type *elty = julia_type_to_llvm(jltype, &isboxed); if (type_is_ghost(elty)) return ghostValue(jltype); - Value *data; if (isboxed) elty = T_prjlvalue; - // TODO: preserving_pointercast? - if (ptr->getType()->getContainedType(0) != elty) - data = emit_bitcast(ctx, ptr, PointerType::get(elty, 0)); + Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); + Value *data; + if (ptr->getType() != ptrty) + data = emit_bitcast(ctx, ptr, ptrty); else data = ptr; if (idx_0based) @@ -1203,7 +1227,7 @@ static void typed_store(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, const jl_cgval_t &rhs, jl_value_t *jltype, MDNode *tbaa, Value *parent, // for the write barrier, NULL if no barrier needed - unsigned alignment = 0, bool root_box = true) // if the value to store needs a box, should we root it ? + unsigned alignment = 0) { bool isboxed; Type *elty = julia_type_to_llvm(jltype, &isboxed); @@ -1214,19 +1238,21 @@ static void typed_store(jl_codectx_t &ctx, r = emit_unbox(ctx, elty, rhs, jltype); } else { - r = maybe_decay_untracked(boxed(ctx, rhs, root_box)); + r = maybe_decay_untracked(boxed(ctx, rhs)); if (parent != NULL) emit_write_barrier(ctx, parent, r); } Value *data; - if (ptr->getType()->getContainedType(0) != elty) { + Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); + if (ptr->getType() != ptrty) { if (isboxed) { data = emit_bitcast(ctx, ptr, T_pprjlvalue); } else { - data = emit_bitcast(ctx, ptr, PointerType::get(elty, cast(ptr->getType())->getAddressSpace())); + data = emit_bitcast(ctx, ptr, ptrty); } - } else + } else { data = ptr; + } Instruction *store = ctx.builder.CreateAlignedStore(r, ctx.builder.CreateGEP(data, idx_0based), isboxed ? alignment : julia_alignment(r, jltype, alignment)); if (tbaa) @@ -1263,12 +1289,12 @@ static Value *data_pointer(jl_codectx_t &ctx, const jl_cgval_t &x, Type *astype static bool emit_getfield_unknownidx(jl_codectx_t &ctx, jl_cgval_t *ret, const jl_cgval_t &strct, - Value *idx, jl_datatype_t *stt) + Value *idx, jl_datatype_t *stt, jl_value_t *inbounds) { size_t nfields = jl_datatype_nfields(stt); if (strct.ispointer()) { // boxed or stack if (is_datatype_all_pointers(stt)) { - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); bool maybe_null = (unsigned)stt->ninitialized != nfields; size_t minimum_field_size = (size_t)-1; for (size_t i = 0; i < nfields; ++i) { @@ -1285,20 +1311,19 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, maybe_null, minimum_field_size)); if (maybe_null) null_pointer_check(ctx, fld); - *ret = mark_julia_type(ctx, fld, true, jl_any_type, strct.gcroot || !strct.isimmutable); + *ret = mark_julia_type(ctx, fld, true, jl_any_type); return true; } else if (is_tupletype_homogeneous(stt->types)) { assert(nfields > 0); // nf == 0 trapped by all_pointers case jl_value_t *jt = jl_field_type(stt, 0); - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); Value *ptr = data_pointer(ctx, strct); if (!stt->mutabl) { // just compute the pointer and let user load it when necessary Type *fty = julia_type_to_llvm(jt); Value *addr = ctx.builder.CreateGEP(emit_bitcast(ctx, decay_derived(ptr), PointerType::get(fty,0)), idx); *ret = mark_julia_slot(addr, jt, NULL, strct.tbaa); - ret->gcroot = strct.gcroot; ret->isimmutable = strct.isimmutable; return true; } @@ -1315,14 +1340,15 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, else if (is_tupletype_homogeneous(stt->types)) { assert(jl_isbits(stt)); if (nfields == 0) { - idx = emit_bounds_check(ctx, ghostValue(stt), - (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + idx = emit_bounds_check( + ctx, ghostValue(stt), (jl_value_t*)stt, + idx, ConstantInt::get(T_size, nfields), inbounds); *ret = jl_cgval_t(); return true; } assert(!jl_field_isptr(stt, 0)); jl_value_t *jt = jl_field_type(stt, 0); - Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields)); + Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); if (strct.isghost) { *ret = ghostValue(jt); return true; @@ -1374,6 +1400,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st addr = ctx.builder.CreateStructGEP(lt, ptr, idx); } } + int align = jl_field_align(jt, idx); if (jl_field_isptr(jt, idx)) { bool maybe_null = idx >= (unsigned)jt->ninitialized; Instruction *Load = maybe_mark_load_dereferenceable( @@ -1383,18 +1410,34 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st Value *fldv = tbaa_decorate(strct.tbaa, Load); if (maybe_null) null_pointer_check(ctx, fldv); - return mark_julia_type(ctx, fldv, true, jfty, strct.gcroot || !strct.isimmutable); + return mark_julia_type(ctx, fldv, true, jfty); + } + else if (jl_is_uniontype(jfty)) { + int fsz = jl_field_size(jt, idx); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), ctx.builder.CreateLoad(ptindex)); + bool isimmutable = strct.isimmutable; + if (jt->mutabl) { + // move value to an immutable stack slot + Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); + AllocaInst *lv = emit_static_alloca(ctx, AT); + if (align > 1) + lv->setAlignment(align); + Value *nbytes = ConstantInt::get(T_size, fsz - 1); + ctx.builder.CreateMemCpy(lv, addr, nbytes, align); + addr = lv; + isimmutable = true; + } + jl_cgval_t fieldval = mark_julia_slot(addr, jfty, tindex, strct.tbaa); + fieldval.isimmutable = isimmutable; + return fieldval; } else if (!jt->mutabl) { // just compute the pointer and let user load it when necessary jl_cgval_t fieldval = mark_julia_slot(addr, jfty, NULL, strct.tbaa); fieldval.isimmutable = strct.isimmutable; - fieldval.gcroot = strct.gcroot; return fieldval; } - int align = jl_field_offset(jt, idx); - align |= 16; - align &= -align; return typed_load(ctx, addr, ConstantInt::get(T_size, 0), jfty, strct.tbaa, true, align); } else if (isa(strct.V)) { @@ -1608,23 +1651,22 @@ static Value *emit_arraysize_for_unsafe_dim(jl_codectx_t &ctx, } // `nd == -1` means the dimension is unknown. -static Value *emit_array_nd_index(jl_codectx_t &ctx, - const jl_cgval_t &ainfo, jl_value_t *ex, ssize_t nd, const jl_cgval_t *argv, size_t nidxs) +static Value *emit_array_nd_index( + jl_codectx_t &ctx, const jl_cgval_t &ainfo, jl_value_t *ex, ssize_t nd, + const jl_cgval_t *argv, size_t nidxs, jl_value_t *inbounds) { Value *a = boxed(ctx, ainfo); Value *i = ConstantInt::get(T_size, 0); Value *stride = ConstantInt::get(T_size, 1); #if CHECK_BOUNDS==1 - bool bc = (!ctx.is_inbounds && - jl_options.check_bounds != JL_OPTIONS_CHECK_BOUNDS_OFF) || - jl_options.check_bounds == JL_OPTIONS_CHECK_BOUNDS_ON; - BasicBlock *failBB=NULL, *endBB=NULL; + bool bc = bounds_check_enabled(ctx, inbounds); + BasicBlock *failBB = NULL, *endBB = NULL; if (bc) { failBB = BasicBlock::Create(jl_LLVMContext, "oob"); endBB = BasicBlock::Create(jl_LLVMContext, "idxend"); } #endif - Value **idxs = (Value**)alloca(sizeof(Value*)*nidxs); + Value **idxs = (Value**)alloca(sizeof(Value*) * nidxs); for (size_t k = 0; k < nidxs; k++) { idxs[k] = emit_unbox(ctx, T_size, argv[k], NULL); } @@ -1652,33 +1694,10 @@ static Value *emit_array_nd_index(jl_codectx_t &ctx, // We have already emitted a bounds check for each index except for // the last one which we therefore have to do here. bool linear_indexing = nd == -1 || nidxs < (size_t)nd; - if (linear_indexing) { - // Compare the linearized index `i` against the linearized size of - // the accessed array, i.e. `if !(i < alen) goto error`. - if (nidxs > 1) { - // TODO: REMOVE DEPWARN AND RETURN FALSE AFTER 0.6. - // We need to check if this is inside the non-linearized size - BasicBlock *partidx = BasicBlock::Create(jl_LLVMContext, "partlinidx"); - BasicBlock *partidxwarn = BasicBlock::Create(jl_LLVMContext, "partlinidxwarn"); - Value *d = emit_arraysize_for_unsafe_dim(ctx, ainfo, ex, nidxs, nd); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(ii, d), endBB, partidx); - - // We failed the normal bounds check; check to see if we're - // inside the linearized size (partial linear indexing): - ctx.f->getBasicBlockList().push_back(partidx); - ctx.builder.SetInsertPoint(partidx); - Value *alen = emit_arraylen(ctx, ainfo, ex); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), partidxwarn, failBB); - - // We passed the linearized bounds check; now throw the depwarn: - ctx.f->getBasicBlockList().push_back(partidxwarn); - ctx.builder.SetInsertPoint(partidxwarn); - ctx.builder.CreateCall(prepare_call(jldepwarnpi_func), ConstantInt::get(T_size, nidxs)); - ctx.builder.CreateBr(endBB); - } else { - Value *alen = emit_arraylen(ctx, ainfo, ex); - ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), endBB, failBB); - } + if (linear_indexing && nidxs == 1) { + // Check against the entire linear span of the array + Value *alen = emit_arraylen(ctx, ainfo, ex); + ctx.builder.CreateCondBr(ctx.builder.CreateICmpULT(i, alen), endBB, failBB); } else { // Compare the last index of the access against the last dimension of // the accessed array, i.e. `if !(last_index < last_dimension) goto error`. @@ -1876,8 +1895,42 @@ static Value *_boxed_special(jl_codectx_t &ctx, const jl_cgval_t &vinfo, Type *t return box; } +static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t *supertype, jl_value_t *ut) +{ + Value *tindex = ConstantInt::get(T_int8, 0); + unsigned counter = 0; + for_each_uniontype_small( + [&](unsigned idx, jl_datatype_t *jt) { + if (jl_subtype((jl_value_t*)jt, supertype)) { + Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), datatype); + tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); + } + }, + ut, + counter); + return tindex; +} +// get the runtime tindex value +static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) +{ + if (val.typ == jl_bottom_type) + return UndefValue::get(T_int8); + if (val.constant) + return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); + if (val.isboxed) + return compute_box_tindex(ctx, emit_typeof_boxed(ctx, val), val.typ, typ); + assert(val.TIndex); + return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); +} +/* + * Box unboxed values in a union. Optionally, skip certain unboxed values, + * returning `V_null` in one of the skipped cases. If `skip` is not empty, + * skip[0] (corresponding to unknown boxed) must always be set. In that + * case, the calling code must separately deal with the case where + * `vinfo` is already an unkown boxed union (union tag 0x80). + */ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallBitVector &skip) { // given vinfo::Union{T, S}, emit IR of the form: @@ -1928,13 +1981,12 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB vinfo.typ, counter); ctx.builder.SetInsertPoint(defaultBB); - if (skip.size() > 0 && skip[0]) { - // skip[0] specifies where to return NULL or the original pointer - // if the value was not handled above + if (skip.size() > 0) { + assert(skip[0]); box_merge->addIncoming(maybe_decay_untracked(V_null), defaultBB); ctx.builder.CreateBr(postBB); } - else if ((vinfo.V == NULL || isa(vinfo.V)) && !vinfo.gcroot) { + else if (!vinfo.Vboxed) { Function *trap_func = Intrinsic::getDeclaration( ctx.f->getParent(), Intrinsic::trap); @@ -1942,9 +1994,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB ctx.builder.CreateUnreachable(); } else { - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this union is a boxed value, rather than an interior pointer of some sort - box_merge->addIncoming(ctx.builder.CreateLoad(vinfo.gcroot), defaultBB); + box_merge->addIncoming(vinfo.Vboxed, defaultBB); ctx.builder.CreateBr(postBB); } ctx.builder.SetInsertPoint(postBB); @@ -1954,7 +2004,7 @@ static Value *box_union(jl_codectx_t &ctx, const jl_cgval_t &vinfo, const SmallB // this is used to wrap values for generic contexts, where a // dynamically-typed value is required (e.g. argument to unknown function). // if it's already a pointer it's left alone. -static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool gcrooted) +static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) { jl_value_t *jt = vinfo.typ; if (jt == jl_bottom_type || jt == NULL) @@ -1963,10 +2013,8 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool gcrooted) if (vinfo.constant) return maybe_decay_untracked(literal_pointer_val(ctx, vinfo.constant)); if (vinfo.isboxed) { - assert(vinfo.V && "Missing value for box."); - // We're guaranteed here that Load(.gcroot) == .V, because we have determined - // that this value is a box, so if it has a gcroot, that's where the value is. - return vinfo.gcroot ? ctx.builder.CreateLoad(vinfo.gcroot) : vinfo.V; + assert(vinfo.V == vinfo.Vboxed); + return vinfo.V; } Value *box; @@ -1988,12 +2036,6 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo, bool gcrooted) box = maybe_decay_untracked(box); } } - if (gcrooted) { - // make a gcroot for the new box - // (unless the caller explicitly said this was unnecessary) - Value *froot = emit_local_root(ctx); - ctx.builder.CreateStore(box, froot); - } return box; } @@ -2075,7 +2117,7 @@ static void emit_unionmove(jl_codectx_t &ctx, Value *dest, const jl_cgval_t &src static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std::string &msg) { Value *t = emit_typeof_boxed(ctx, x); - emit_typecheck(ctx, mark_julia_type(ctx, t, true, jl_any_type, false), (jl_value_t*)jl_datatype_type, msg); + emit_typecheck(ctx, mark_julia_type(ctx, t, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); Value *istype = ctx.builder.CreateICmpEQ(mark_callee_rooted(emit_datatype_name(ctx, t)), @@ -2095,8 +2137,6 @@ static void emit_cpointercheck(jl_codectx_t &ctx, const jl_cgval_t &x, const std // allocation for known size object static Value *emit_allocobj(jl_codectx_t &ctx, size_t static_size, Value *jt) { - JL_FEAT_REQUIRE(ctx, dynamic_alloc); - JL_FEAT_REQUIRE(ctx, runtime); Value *ptls_ptr = emit_bitcast(ctx, ctx.ptlsStates, T_pint8); auto call = ctx.builder.CreateCall(prepare_call(jl_alloc_obj_func), {ptls_ptr, ConstantInt::get(T_size, static_size), @@ -2157,16 +2197,29 @@ static void emit_setfield(jl_codectx_t &ctx, ConstantInt::get(T_size, jl_field_offset(sty, idx0))); jl_value_t *jfty = jl_svecref(sty->types, idx0); if (jl_field_isptr(sty, idx0)) { - Value *r = maybe_decay_untracked(boxed(ctx, rhs, false)); // don't need a temporary gcroot since it'll be rooted by strct + Value *r = maybe_decay_untracked(boxed(ctx, rhs)); // don't need a temporary gcroot since it'll be rooted by strct tbaa_decorate(strct.tbaa, ctx.builder.CreateStore(r, emit_bitcast(ctx, addr, T_pprjlvalue))); if (wb && strct.isboxed) emit_checked_write_barrier(ctx, boxed(ctx, strct), r); } + else if (jl_is_uniontype(jfty)) { + int fsz = jl_field_size(sty, idx0); + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); + if (rhs_union.typ == jl_bottom_type) + return; + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, addr, T_pint8), ConstantInt::get(T_size, fsz - 1)); + ctx.builder.CreateStore(tindex, ptindex); + // copy data + if (!rhs.isghost) { + emit_unionmove(ctx, addr, rhs, NULL, false, NULL); + } + } else { - int align = jl_field_offset(sty, idx0); - align |= 16; - align &= -align; + int align = jl_field_align(sty, idx0); typed_store(ctx, addr, ConstantInt::get(T_size, 0), rhs, jfty, strct.tbaa, data_pointer(ctx, strct, T_pjlvalue), align); } @@ -2187,50 +2240,81 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg if (nf > 0) { if (jl_isbits(sty)) { Type *lt = julia_type_to_llvm(ty); + unsigned na = (nargs - 1 < nf) ? (nargs - 1) : nf; + // whether we should perform the initialization with the struct as a IR value // or instead initialize the stack buffer with stores bool init_as_value = false; if (lt->isVectorTy() || - jl_is_vecelement_type(ty) || - type_is_ghost(lt)) // maybe also check the size ? + jl_is_vecelement_type(ty)) { // maybe also check the size ? init_as_value = true; + } - size_t na = nargs-1 < nf ? nargs-1 : nf; Value *strct; - if (init_as_value) - strct = UndefValue::get(lt == T_void ? NoopType : lt); + if (type_is_ghost(lt)) + strct = NULL; + else if (init_as_value) + strct = UndefValue::get(lt); else strct = emit_static_alloca(ctx, lt); - unsigned idx = 0; - for (size_t i = 0; i < na; i++) { + for (unsigned i = 0; i < na; i++) { jl_value_t *jtype = jl_svecref(sty->types, i); - Type *fty = julia_type_to_llvm(jtype); const jl_cgval_t &fval_info = argv[i + 1]; emit_typecheck(ctx, fval_info, jtype, "new"); - if (!type_is_ghost(fty)) { - Value *fval = NULL, *dest = NULL; - if (!init_as_value) { - // avoid unboxing the argument explicitly - // and use memcpy instead - dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); - } + Type *fty; + if (type_is_ghost(lt)) + continue; + else if (jl_is_vecelement_type(ty)) + fty = lt; + else + fty = cast(lt)->getTypeAtIndex(i); + if (type_is_ghost(fty)) + continue; + Value *dest = NULL; + if (!init_as_value) { + // avoid unboxing the argument explicitly + // and use memcpy instead + dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, i); + } + Value *fval = NULL; + if (jl_is_uniontype(jtype)) { + assert(!init_as_value && "unimplemented"); + StructType *lt_i = cast(fty); + // compute tindex from rhs + jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); + if (rhs_union.typ == jl_bottom_type) + return jl_cgval_t(); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jtype); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *ptindex = ctx.builder.CreateConstInBoundsGEP2_32( + lt_i, dest, 0, lt_i->getNumElements() - 1); + ctx.builder.CreateStore(tindex, ptindex); + if (!rhs_union.isghost) + emit_unionmove(ctx, dest, fval_info, NULL, false, NULL); + // If you wanted to implement init_as_value, + // would need to emit the union-move into temporary memory, + // then load it and combine with the tindex. + // But more efficient to just store it directly. + } + else { fval = emit_unbox(ctx, fty, fval_info, jtype, dest); - - if (init_as_value) { - if (lt->isVectorTy()) - strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, idx)); - else if (jl_is_vecelement_type(ty)) - strct = fval; // VecElement type comes unwrapped in LLVM. - else if (lt->isAggregateType()) - strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&idx, 1)); - else - assert(false); - } } - idx++; + if (init_as_value) { + assert(fval); + if (lt->isVectorTy()) + strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, i)); + else if (jl_is_vecelement_type(ty)) + strct = fval; // VecElement type comes unwrapped in LLVM. + else if (lt->isAggregateType()) + strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&i, 1)); + else + assert(false); + } } - if (init_as_value) + if (type_is_ghost(lt)) + return mark_julia_const(sty->instance); + else if (init_as_value) return mark_julia_type(ctx, strct, false, ty); else return mark_julia_slot(strct, ty, NULL, tbaa_stack); @@ -2312,14 +2396,12 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) { return (a->cached == b->cached) && // language features - (a->runtime == b->runtime) && - (a->exceptions == b->exceptions) && (a->track_allocations == b->track_allocations) && (a->code_coverage == b->code_coverage) && (a->static_alloc == b->static_alloc) && - (a->dynamic_alloc == b->dynamic_alloc) && + (a->prefer_specsig == b->prefer_specsig) && // hooks - (a->hooks.module_setup == b->hooks.module_setup) && - (a->hooks.module_activation == b->hooks.module_activation) && - (a->hooks.raise_exception == b->hooks.raise_exception); + (a->module_setup == b->module_setup) && + (a->module_activation == b->module_activation) && + (a->raise_exception == b->raise_exception); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 4d19a5adac3aa..4118850b1fc7d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -41,7 +40,6 @@ #include #include #include -#include #include // IR building @@ -86,7 +84,6 @@ #include #endif #include -#include "fix_llvm_assert.h" using namespace llvm; namespace llvm { @@ -101,6 +98,7 @@ namespace llvm { #include "julia_internal.h" #include "jitlayers.h" #include "codegen_shared.h" +#include "julia_assert.h" // LLVM version compatibility macros legacy::PassManager *jl_globalPM; @@ -236,6 +234,7 @@ static MDNode *tbaa_arraysize; // A size in a jl_array_t static MDNode *tbaa_arraylen; // The len in a jl_array_t static MDNode *tbaa_arrayflags; // The flags in a jl_array_t static MDNode *tbaa_const; // Memory that is immutable by the time LLVM can see it +static MDNode *tbaa_arrayselbyte; // a selector byte in a isbits Union jl_array_t // Basic DITypes static DICompositeType *jl_value_dillvmt; @@ -342,7 +341,6 @@ static Function *expect_func; static Function *jldlsym_func; static Function *jlnewbits_func; static Function *jltypeassert_func; -static Function *jldepwarnpi_func; //static Function *jlgetnthfield_func; static Function *jlgetnthfieldchecked_func; //static Function *jlsetnthfield_func; @@ -358,6 +356,7 @@ static GlobalVariable *jlgetworld_global; // placeholder functions static Function *gcroot_flush_func; +static Function *gc_use_func; static Function *except_enter_func; static Function *pointer_from_objref_func; @@ -392,9 +391,14 @@ static MDNode *best_tbaa(jl_value_t *jt) { // metadata tracking for a llvm Value* during codegen struct jl_cgval_t { Value *V; // may be of type T* or T, or set to NULL if ghost (or if the value has not been initialized yet, for a variable definition) + // For unions, we may need to keep a reference to the boxed part individually. + // If this is non-NULL, then, at runtime, we satisfy the invariant that (for the corresponding + // runtime values) if `(TIndex | 0x80) != 0`, then `Vboxed == V` (by value). + // For conenience, we also set this value of isboxed values, in which case + // it is equal (at compile time) to V. + Value *Vboxed; Value *TIndex; // if `V` is an unboxed (tagged) Union described by `typ`, this gives the DataType index (1-based, small int) as an i8 jl_value_t *constant; // constant value (rooted in linfo.def.roots) - Value *gcroot; // the gcroot associated with V (if it has one) jl_value_t *typ; // the original type of V, never NULL bool isboxed; // whether this value is a jl_value_t* allocated on the heap with the right type tag bool isghost; // whether this value is "ghost" @@ -412,23 +416,24 @@ struct jl_cgval_t { //} jl_cgval_t(Value *V, Value *gcroot, bool isboxed, jl_value_t *typ, Value *tindex) : // general constructor (with pointer type auto-detect) V(V), // V is allowed to be NULL in a jl_varinfo_t context, but not during codegen contexts + Vboxed(isboxed ? V : nullptr), TIndex(tindex), constant(NULL), - gcroot(gcroot), typ(typ), isboxed(isboxed), isghost(false), isimmutable(isboxed && jl_is_immutable_datatype(typ)), tbaa(isboxed ? best_tbaa(typ) : nullptr) { + assert(gcroot == nullptr); assert(!(isboxed && TIndex != NULL)); assert(TIndex == NULL || TIndex->getType() == T_int8); } jl_cgval_t(jl_value_t *typ) : // ghost value constructor V(NULL), + Vboxed(NULL), TIndex(NULL), constant(((jl_datatype_t*)typ)->instance), - gcroot(NULL), typ(typ), isboxed(false), isghost(true), @@ -440,9 +445,9 @@ struct jl_cgval_t { } jl_cgval_t(const jl_cgval_t &v, jl_value_t *typ, Value *tindex) : // copy constructor with new type V(v.V), + Vboxed(v.Vboxed), TIndex(tindex), constant(v.constant), - gcroot(v.gcroot), typ(typ), isboxed(v.isboxed), isghost(v.isghost), @@ -460,9 +465,9 @@ struct jl_cgval_t { } jl_cgval_t() : // undef / unreachable / default constructor V(UndefValue::get(T_void)), + Vboxed(NULL), TIndex(NULL), constant(NULL), - gcroot(NULL), typ(jl_bottom_type), isboxed(false), isghost(true), @@ -560,8 +565,6 @@ class jl_codectx_t { Value *world_age_field = NULL; bool debug_enabled = false; - bool is_inbounds = false; - const jl_cgparams_t *params = NULL; jl_codectx_t(LLVMContext &llvmctx) @@ -573,7 +576,6 @@ class jl_codectx_t { }; static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr); -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi = NULL); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, jl_binding_t **pbnd, bool assign); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, bool isvol, MDNode *tbaa); @@ -661,7 +663,7 @@ static inline jl_cgval_t mark_julia_slot(Value *v, jl_value_t *typ, Value *tinde return tagval; } -static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_value_t *typ, bool needsroot = true) +static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_value_t *typ) { if (jl_is_datatype(typ) && jl_is_datatype_singleton((jl_datatype_t*)typ)) { // no need to explicitly load/store a constant/ghost value @@ -691,17 +693,12 @@ static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isbox } return mark_julia_slot(loc, typ, NULL, tbaa_stack); } - Value *froot = NULL; - if (needsroot && isboxed) { - froot = emit_local_root(ctx); - ctx.builder.CreateStore(v, froot); - } - return jl_cgval_t(v, froot, isboxed, typ, NULL); + return jl_cgval_t(v, NULL, isboxed, typ, NULL); } -static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_datatype_t *typ, bool needsroot = true) +static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isboxed, jl_datatype_t *typ) { - return mark_julia_type(ctx, v, isboxed, (jl_value_t*)typ, needsroot); + return mark_julia_type(ctx, v, isboxed, (jl_value_t*)typ); } // see if it might be profitable (and cheap) to change the type of v to typ @@ -722,8 +719,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return v; // not worth trying to improve type info if (!isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch (there weren't any boxed values in the union) @@ -738,6 +735,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(v, typ, NULL); } +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ); + // --- allocating local variables --- static jl_sym_t *slot_symbol(jl_codectx_t &ctx, int s) @@ -817,8 +816,154 @@ static void jl_rethrow_with_add(const char *fmt, ...) jl_rethrow(); } +static jl_cgval_t convert_julia_type_union(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) +{ + // previous value was a split union, compute new index, or box + Value *new_tindex = ConstantInt::get(T_int8, 0x80); + SmallBitVector skip_box(1, true); + Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); + if (jl_is_uniontype(typ)) { + // compute the TIndex mapping from v.typ -> typ + unsigned counter = 0; + for_each_uniontype_small( + // for each old union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned new_idx = get_box_tindex(jt, typ); + bool t; + if (new_idx) { + // found a matching element, + // match it against either the unboxed index + Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); + new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); + t = true; + } + else if (!jl_subtype((jl_value_t*)jt, typ)) { + // new value doesn't need to be boxed + // since it isn't part of the new union + t = true; + } + else { + // will actually need to box this element + // since it appeared as a leaftype in the original type + // but not in the remark type + t = false; + } + skip_box.resize(idx + 1, t); + }, + v.typ, + counter); + } + + // some of the values are still unboxed + if (!isa(new_tindex)) { + Value *wasboxed = NULL; + // If the old value was boxed and unknown (type tag 0x80), + // it is possible that the tag was actually one of the types + // that are now explicitly represented. To find out, we need + // to compare typeof(v.Vboxed) (i.e. the type of the unknown + // value) against all the types that are now explicitly + // selected and select the appropriate one as our new tindex. + if (v.Vboxed) { + wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); + new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); + wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); + + BasicBlock *currBB = ctx.builder.GetInsertBlock(); + + // We lazily create a BB for this, once we decide that we + // actually need it. + Value *union_box_dt = NULL; + BasicBlock *union_isaBB = NULL; + auto maybe_setup_union_isa = [&]() { + union_isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); + ctx.builder.SetInsertPoint(union_isaBB); + union_box_dt = emit_typeof(ctx, v.Vboxed); + }; + + // If we don't find a match. The type remains unknown + // (0x80). We could use `v.Tindex`, here, since we know + // it has to be 0x80, but it seems likely the backend + // will like the explicit constant better. + Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); + unsigned counter = 0; + for_each_uniontype_small( + // for each new union-split value + [&](unsigned idx, jl_datatype_t *jt) { + unsigned old_idx = get_box_tindex(jt, v.typ); + if (old_idx == 0) { + // didn't handle this item before, select its new union index + maybe_setup_union_isa(); + Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); + union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); + } + }, + typ, + counter); + if (union_box_dt) { + BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); + ctx.builder.CreateBr(postBB); + ctx.builder.SetInsertPoint(currBB); + Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); + ctx.builder.CreateCondBr(wasunknown, union_isaBB, postBB); + ctx.builder.SetInsertPoint(postBB); + PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); + tindex_phi->addIncoming(new_tindex, currBB); + tindex_phi->addIncoming(union_box_tindex, union_isaBB); + new_tindex = tindex_phi; + } + } + if (!skip_box.all()) { + // some values weren't unboxed in the new union + // box them now (tindex above already selected 0x80 = box for them) + Value *boxv = box_union(ctx, v, skip_box); + if (v.Vboxed) { + // If the value is boxed both before and after, we don't need + // to touch it at all. Otherwise we're either transitioning + // unboxed->boxed, or leaving an unboxed value in place. + Value *isboxed = ctx.builder.CreateICmpNE( + ctx.builder.CreateAnd(new_tindex, ConstantInt::get(T_int8, 0x80)), + ConstantInt::get(T_int8, 0)); + boxv = ctx.builder.CreateSelect( + ctx.builder.CreateAnd(wasboxed, isboxed), v.Vboxed, boxv); + } + if (v.V == NULL) { + // v.V might be NULL if it was all ghost objects before + return jl_cgval_t(boxv, NULL, false, typ, new_tindex); + } else { + Value *isboxv = ctx.builder.CreateIsNotNull(boxv); + Value *slotv; + MDNode *tbaa; + bool isimmutable; + if (v.ispointer()) { + slotv = v.V; + tbaa = v.tbaa; + isimmutable = v.isimmutable; + } + else { + slotv = emit_static_alloca(ctx, v.V->getType()); + ctx.builder.CreateStore(v.V, slotv); + tbaa = tbaa_stack; + isimmutable = true; + } + slotv = ctx.builder.CreateSelect(isboxv, + decay_derived(boxv), + decay_derived(emit_bitcast(ctx, slotv, boxv->getType()))); + jl_cgval_t newv = jl_cgval_t(slotv, NULL, false, typ, new_tindex); + newv.Vboxed = boxv; + newv.tbaa = tbaa; + newv.isimmutable = isimmutable; + return newv; + } + } + } + else { + return jl_cgval_t(boxed(ctx, v), NULL, true, typ, NULL); + } + return jl_cgval_t(v, typ, new_tindex); +} + // given a value marked with type `v.typ`, compute the mapping and/or boxing to return a value of type `typ` -static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ, bool needsroot = true) +static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_value_t *typ) { if (typ == (jl_value_t*)jl_typeofbottom_type) return ghostValue(typ); // normalize TypeofBottom to Type{Union{}} @@ -831,8 +976,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ if (jl_is_leaf_type(typ)) { if (v.TIndex && !isbits_spec(typ)) { // discovered that this union-split type must actually be isboxed - if (v.V) { - return jl_cgval_t(v.V, v.gcroot, true, typ, NULL); + if (v.Vboxed) { + return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); } else { // type mismatch: there weren't any boxed values in the union @@ -851,145 +996,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ else { bool makeboxed = false; if (v.TIndex) { - // previous value was a split union, compute new index, or box - new_tindex = ConstantInt::get(T_int8, 0x80); - SmallBitVector skip_box(1, true); - Value *tindex = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x7f)); - if (jl_is_uniontype(typ)) { - // compute the TIndex mapping from v.typ -> typ - unsigned counter = 0; - for_each_uniontype_small( - // for each old union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned new_idx = get_box_tindex(jt, typ); - bool t; - if (new_idx) { - // found a matching element, - // match it against either the unboxed index - Value *cmp = ctx.builder.CreateICmpEQ(tindex, ConstantInt::get(T_int8, idx)); - new_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, new_idx), new_tindex); - t = true; - } - else if (!jl_subtype((jl_value_t*)jt, typ)) { - // new value doesn't need to be boxed - // since it isn't part of the new union - t = true; - } - else { - // will actually need to box this element - // since it appeared as a leaftype in the original type - // but not in the remark type - t = false; - } - skip_box.resize(idx + 1, t); - }, - v.typ, - counter); - } - - // some of the values are still unboxed - if (!isa(new_tindex)) { - Value *wasboxed = NULL; - // check if some of the old values might have been boxed - // and copy that information over into the new tindex - if (v.ispointer() && v.V && !isa(v.V)) { - wasboxed = ctx.builder.CreateAnd(v.TIndex, ConstantInt::get(T_int8, 0x80)); - new_tindex = ctx.builder.CreateOr(wasboxed, new_tindex); - wasboxed = ctx.builder.CreateICmpNE(wasboxed, ConstantInt::get(T_int8, 0)); - - // may need to handle compute_box_tindex for some of the values - BasicBlock *currBB = ctx.builder.GetInsertBlock(); - Value *union_box_dt = NULL; - Value *union_box_tindex = ConstantInt::get(T_int8, 0x80); - unsigned counter = 0; - for_each_uniontype_small( - // for each new union-split value - [&](unsigned idx, jl_datatype_t *jt) { - unsigned old_idx = get_box_tindex(jt, v.typ); - if (old_idx == 0) { - if (!union_box_dt) { - BasicBlock *isaBB = BasicBlock::Create(jl_LLVMContext, "union_isa", ctx.f); - ctx.builder.SetInsertPoint(isaBB); - union_box_dt = emit_typeof(ctx, v.V); - } - // didn't handle this item before, select its new union index - Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), union_box_dt); - union_box_tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, 0x80 | idx), union_box_tindex); - } - }, - typ, - counter); - if (union_box_dt) { - BasicBlock *isaBB = ctx.builder.GetInsertBlock(); - BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_union_isa", ctx.f); - ctx.builder.CreateBr(postBB); - ctx.builder.SetInsertPoint(currBB); - Value *wasunknown = ctx.builder.CreateICmpEQ(v.TIndex, ConstantInt::get(T_int8, 0x80)); - ctx.builder.CreateCondBr(wasunknown, isaBB, postBB); - ctx.builder.SetInsertPoint(postBB); - PHINode *tindex_phi = ctx.builder.CreatePHI(T_int8, 2); - tindex_phi->addIncoming(new_tindex, currBB); - tindex_phi->addIncoming(union_box_tindex, isaBB); - new_tindex = tindex_phi; - } - - } - - if (!skip_box.all()) { - // some values weren't unboxed in the new union - // box them now (tindex above already selected 0x80 = box for them) - // root the result, and return a new mark_julia_slot over the result - Value *boxv = box_union(ctx, v, skip_box); - Value *froot = NULL; - if (needsroot) { - // build a new gc-root, as needed - froot = emit_local_root(ctx); - Value *newroot = boxv; - if (wasboxed || v.gcroot) { // oldbox might be all ghost values (which don't need roots) - // store either the old box or the new box into the gc-root (skip_box ensures these are mutually-exclusive) - // need to clone the value from `v.gcroot` if this isn't a new box - Value *oldroot; - if (v.gcroot) - oldroot = ctx.builder.CreateLoad(v.gcroot); - else - oldroot = v.V; - newroot = ctx.builder.CreateSelect(wasboxed, emit_bitcast(ctx, oldroot, boxv->getType()), newroot); - } - ctx.builder.CreateStore(newroot, froot); - } - if (v.V == NULL) { - // v.V might be NULL if it was all ghost objects before - return jl_cgval_t(boxv, froot, false, typ, new_tindex); - } - else { - Value *isboxv = ctx.builder.CreateIsNotNull(boxv); - Value *slotv; - MDNode *tbaa; - bool isimmutable; - if (v.ispointer()) { - slotv = v.V; - tbaa = v.tbaa; - isimmutable = v.isimmutable; - } - else { - slotv = emit_static_alloca(ctx, v.V->getType()); - ctx.builder.CreateStore(v.V, slotv); - tbaa = tbaa_stack; - isimmutable = true; - } - slotv = ctx.builder.CreateSelect(isboxv, - decay_derived(boxv), emit_bitcast(ctx, slotv, boxv->getType())); - jl_cgval_t newv = jl_cgval_t(slotv, froot, false, typ, new_tindex); - newv.tbaa = tbaa; - newv.isimmutable = isimmutable; - return newv; - } - } - } - else { - new_tindex = NULL; - makeboxed = true; - } + return convert_julia_type_union(ctx, v, typ); } else if (!v.isboxed && jl_is_uniontype(typ)) { // previous value was unboxed (leaftype), statically compute union tindex @@ -1021,13 +1028,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ } if (makeboxed) { // convert to a simple isboxed value - Value *boxv = boxed(ctx, v, false); - Value *froot = NULL; - if (needsroot) { - froot = emit_local_root(ctx); - ctx.builder.CreateStore(maybe_decay_untracked(boxv), froot); - } - return jl_cgval_t(boxv, froot, true, typ, NULL); + return jl_cgval_t(boxed(ctx, v), NULL, true, typ, NULL); } } return jl_cgval_t(v, typ, new_tindex); @@ -1419,10 +1420,6 @@ extern "C" JL_DLLEXPORT void *jl_function_ptr(jl_function_t *f, jl_value_t *rt, jl_value_t *argt) { JL_GC_PUSH1(&argt); - if (jl_is_tuple(argt)) { - // TODO: maybe deprecation warning, better checking - argt = (jl_value_t*)jl_apply_tuple_type_v((jl_value_t**)jl_data_ptr(argt), jl_nfields(argt)); - } JL_LOCK(&codegen_lock); Function *llvmf = jl_cfunction_object(f, rt, (jl_tupletype_t*)argt); JL_GC_POP(); @@ -1823,7 +1820,7 @@ static jl_value_t *static_eval(jl_codectx_t &ctx, jl_value_t *ex, int sparams=tr if (e->head == call_sym) { jl_value_t *f = static_eval(ctx, jl_exprarg(e, 0), sparams, allow_alloc); if (f) { - if (jl_array_dim0(e->args) == 3 && f==jl_builtin_getfield) { + if (jl_array_dim0(e->args) == 3 && f == jl_builtin_getfield) { m = (jl_module_t*)static_eval(ctx, jl_exprarg(e, 1), sparams, allow_alloc); // Check the tag before evaluating `s` so that a value of random // type won't be corrupted. @@ -1997,20 +1994,6 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) // ---- Get Element Pointer (GEP) instructions within the GC frame ---- -// Emit a gc-root slot indicator -static Value *emit_local_root(jl_codectx_t &ctx, jl_varinfo_t *vi) -{ - Instruction *newroot = new AllocaInst(T_prjlvalue, 0, "gcroot", /*InsertBefore*/ctx.ptlsStates); - if (vi) { - vi->boxroot->replaceAllUsesWith(newroot); - newroot->takeName(vi->boxroot); - vi->boxroot->eraseFromParent(); - vi->boxroot = newroot; - } - - return newroot; -} - static void jl_add_method_root(jl_codectx_t &ctx, jl_value_t *val) { if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val) || @@ -2075,8 +2058,41 @@ static jl_cgval_t emit_getfield(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_s mark_julia_const((jl_value_t*)name) }; Value *result = emit_jlcall(ctx, jlgetfield_func, maybe_decay_untracked(V_null), myargs_array, 2); - bool needsgcroot = true; // !arg1.isimmutable || !jl_is_leaf_type(arg1.typ) || !is_datatype_all_pointers((jl_datatype_t*)arg1.typ); // TODO: probably want this as a llvm pass - return mark_julia_type(ctx, result, true, jl_any_type, needsgcroot); + return mark_julia_type(ctx, result, true, jl_any_type); +} + +static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2); + +static Value *emit_bitsunion_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) +{ + assert(arg1.typ == arg2.typ && arg1.TIndex && arg2.TIndex && jl_is_uniontype(arg1.typ) && "unimplemented"); + Value *tindex = arg1.TIndex; + BasicBlock *defaultBB = BasicBlock::Create(jl_LLVMContext, "unionbits_is_boxed", ctx.f); + SwitchInst *switchInst = ctx.builder.CreateSwitch(tindex, defaultBB); + BasicBlock *postBB = BasicBlock::Create(jl_LLVMContext, "post_unionbits_is", ctx.f); + ctx.builder.SetInsertPoint(postBB); + PHINode *phi = ctx.builder.CreatePHI(T_int1, 2); + unsigned counter = 0; + for_each_uniontype_small( + [&](unsigned idx, jl_datatype_t *jt) { + BasicBlock *tempBB = BasicBlock::Create(jl_LLVMContext, "unionbits_is", ctx.f); + ctx.builder.SetInsertPoint(tempBB); + switchInst->addCase(ConstantInt::get(T_int8, idx), tempBB); + jl_cgval_t sel_arg1(arg1, (jl_value_t*)jt, NULL); + jl_cgval_t sel_arg2(arg2, (jl_value_t*)jt, NULL); + phi->addIncoming(emit_bits_compare(ctx, sel_arg1, sel_arg2), tempBB); + ctx.builder.CreateBr(postBB); + }, + arg1.typ, + counter); + ctx.builder.SetInsertPoint(defaultBB); + Function *trap_func = Intrinsic::getDeclaration( + ctx.f->getParent(), + Intrinsic::trap); + ctx.builder.CreateCall(trap_func); + ctx.builder.CreateUnreachable(); + ctx.builder.SetInsertPoint(postBB); + return ctx.builder.CreateAnd(phi, ctx.builder.CreateICmpEQ(arg1.TIndex, arg2.TIndex)); } static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) @@ -2084,6 +2100,9 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const assert(jl_is_datatype(arg1.typ) && arg1.typ == arg2.typ); Type *at = julia_type_to_llvm(arg1.typ); + if (type_is_ghost(at)) + return ConstantInt::get(T_int1, 1); + if (at->isIntegerTy() || at->isPointerTy() || at->isFloatingPointTy()) { Type *at_int = INTT(at); Value *varg1 = emit_unbox(ctx, at_int, arg1, arg1.typ); @@ -2132,11 +2151,29 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const Value *subAns, *fld1, *fld2; fld1 = ctx.builder.CreateConstGEP2_32(at, varg1, 0, i); fld2 = ctx.builder.CreateConstGEP2_32(at, varg2, 0, i); - if (type_is_ghost(fld1->getType()->getPointerElementType())) + Type *at_i = cast(fld1)->getResultElementType(); + if (type_is_ghost(at_i)) continue; - subAns = emit_bits_compare(ctx, - mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), - mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); + if (jl_is_uniontype(fldty)) { + unsigned tindex_offset = cast(at_i)->getNumElements() - 1; + Value *ptindex1 = ctx.builder.CreateConstInBoundsGEP2_32( + at_i, fld1, 0, tindex_offset); + Value *ptindex2 = ctx.builder.CreateConstInBoundsGEP2_32( + at_i, fld2, 0, tindex_offset); + Value *tindex1 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, ptindex1)); + Value *tindex2 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, ptindex2)); + subAns = emit_bitsunion_compare(ctx, + mark_julia_slot(fld1, fldty, tindex1, arg1.tbaa), + mark_julia_slot(fld2, fldty, tindex2, arg2.tbaa)); + } + else { + assert(jl_is_leaf_type(fldty)); + subAns = emit_bits_compare(ctx, + mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), + mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); + } answer = ctx.builder.CreateAnd(answer, subAns); } return answer; @@ -2171,8 +2208,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva // which is already enough to ensure pointer uniqueness for this test // even if the other pointer managed to get garbage collected return ctx.builder.CreateICmpEQ( - mark_callee_rooted(boxed(ctx, arg1, false)), - mark_callee_rooted(boxed(ctx, arg2, false))); + mark_callee_rooted(boxed(ctx, arg1)), + mark_callee_rooted(boxed(ctx, arg2))); } if (jl_type_intersection(rt1, rt2) == (jl_value_t*)jl_bottom_type) // types are disjoint (exhaustive test) @@ -2200,6 +2237,9 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva return cmp; } + // if (arg1.tindex || arg2.tindex) + // TODO: handle with emit_bitsunion_compare + int ptr_comparable = 0; // whether this type is unique'd by pointer if (rt1 == (jl_value_t*)jl_sym_type || rt2 == (jl_value_t*)jl_sym_type) ptr_comparable = 1; @@ -2222,9 +2262,8 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva decay_derived(varg2)); } - JL_FEAT_REQUIRE(ctx, runtime); Value *varg1 = mark_callee_rooted(boxed(ctx, arg1)); - Value *varg2 = mark_callee_rooted(boxed(ctx, arg2, false)); // potentially unrooted! + Value *varg2 = mark_callee_rooted(boxed(ctx, arg2)); return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegal_func), {varg1, varg2}), T_int1); } @@ -2267,7 +2306,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_subtype(ty.typ, (jl_value_t*)jl_type_type)) { Value *rt_arg = boxed(ctx, arg); Value *rt_ty = boxed(ctx, ty); - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jltypeassert_func), {rt_arg, rt_ty}); *ret = arg; return true; @@ -2308,7 +2346,6 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, nva = ctx.builder.CreateTrunc(nva, T_int32); #endif Value *theArgs = ctx.builder.CreateGEP(ctx.argArray, ConstantInt::get(T_size, ctx.nReqArgs)); - JL_FEAT_REQUIRE(ctx, runtime); Value *r = ctx.builder.CreateCall(prepare_call(jlapply2va_func), { theF, theArgs, nva }); *ret = mark_julia_type(ctx, r, true, jl_any_type); return true; @@ -2328,7 +2365,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } else if (f == jl_builtin_throw && nargs == 1) { - Value *arg1 = boxed(ctx, argv[1], false); // rooted by throw + Value *arg1 = boxed(ctx, argv[1]); raise_exception(ctx, arg1); *ret = jl_cgval_t(); return true; @@ -2383,10 +2420,10 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin_arrayref && nargs >= 2) { - const jl_cgval_t &ary = argv[1]; + else if (f == jl_builtin_arrayref && nargs >= 3) { + const jl_cgval_t &ary = argv[2]; bool indexes_ok = true; - for (size_t i = 2; i <= nargs; i++) { + for (size_t i = 3; i <= nargs; i++) { if (argv[i].typ != (jl_value_t*)jl_long_type) { indexes_ok = false; break; @@ -2396,31 +2433,52 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_array_type(aty_dt) && indexes_ok) { jl_value_t *ety = jl_tparam0(aty_dt); jl_value_t *ndp = jl_tparam1(aty_dt); - if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 2)) { - jl_value_t *ary_ex = jl_exprarg(ex, 1); - if (!jl_array_store_unboxed(ety)) + if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 3)) { + jl_value_t *ary_ex = jl_exprarg(ex, 2); + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); + if (isboxed) ety = (jl_value_t*)jl_any_type; ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; - Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[2], nargs - 1); - if (jl_array_store_unboxed(ety) && jl_datatype_size(ety) == 0) { - assert(jl_is_datatype(ety)); + jl_value_t *boundscheck = argv[1].constant; + Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[3], nargs - 2, boundscheck); + if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { assert(((jl_datatype_t*)ety)->instance != NULL); *ret = ghostValue(ety); } + else if (!isboxed && jl_is_uniontype(ety)) { + Value *nbytes = ConstantInt::get(T_size, elsz); + Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), T_pint8); + // isbits union selector bytes are stored directly after the last array element + Value *selidx = ctx.builder.CreateMul(emit_arraylen_prim(ctx, ary), nbytes); + selidx = ctx.builder.CreateAdd(selidx, idx); + Value *ptindex = ctx.builder.CreateGEP(T_int8, data, selidx); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + tbaa_decorate(tbaa_arrayselbyte, ctx.builder.CreateLoad(T_int8, ptindex))); + Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (elsz + al - 1) / al); + AllocaInst *lv = emit_static_alloca(ctx, AT); + if (al > 1) + lv->setAlignment(al); + Value *elidx = ctx.builder.CreateMul(idx, nbytes); + ctx.builder.CreateMemCpy(lv, ctx.builder.CreateGEP(T_int8, data, elidx), nbytes, al); + *ret = mark_julia_slot(lv, ety, tindex, tbaa_stack); + } else { - *ret = typed_load(ctx, emit_arrayptr(ctx, ary, ary_ex), idx, ety, - jl_array_store_unboxed(ety) ? tbaa_arraybuf : tbaa_ptrarraybuf); + *ret = typed_load(ctx, + emit_arrayptr(ctx, ary, ary_ex), + idx, ety, + !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf); } return true; } } } - else if (f == jl_builtin_arrayset && nargs >= 3) { - const jl_cgval_t &ary = argv[1]; - const jl_cgval_t &val = argv[2]; + else if (f == jl_builtin_arrayset && nargs >= 4) { + const jl_cgval_t &ary = argv[2]; + const jl_cgval_t &val = argv[3]; bool indexes_ok = true; - for (size_t i = 3; i <= nargs; i++) { + for (size_t i = 4; i <= nargs; i++) { if (argv[i].typ != (jl_value_t*)jl_long_type) { indexes_ok = false; break; @@ -2430,16 +2488,18 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, if (jl_is_array_type(aty_dt) && indexes_ok) { jl_value_t *ety = jl_tparam0(aty_dt); jl_value_t *ndp = jl_tparam1(aty_dt); - if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 3)) { + if (!jl_has_free_typevars(ety) && (jl_is_long(ndp) || nargs == 4)) { if (jl_subtype(val.typ, ety)) { // TODO: probably should just convert this to a type-assert - bool isboxed = !jl_array_store_unboxed(ety); + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (isboxed) ety = (jl_value_t*)jl_any_type; - jl_value_t *ary_ex = jl_exprarg(ex, 1); + jl_value_t *ary_ex = jl_exprarg(ex, 2); ssize_t nd = jl_is_long(ndp) ? jl_unbox_long(ndp) : -1; - Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[3], nargs - 2); - if (!isboxed && jl_datatype_size(ety) == 0) { - assert(jl_is_datatype(ety)); + jl_value_t *boundscheck = argv[1].constant; + Value *idx = emit_array_nd_index(ctx, ary, ary_ex, nd, &argv[4], nargs - 3, boundscheck); + if (!isboxed && jl_is_datatype(ety) && jl_datatype_size(ety) == 0) { + // no-op } else { PHINode *data_owner = NULL; // owner object against which the write barrier must check @@ -2476,21 +2536,45 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, data_owner->addIncoming(aryv, curBB); data_owner->addIncoming(own_ptr, ownedBB); } - typed_store(ctx, - emit_arrayptr(ctx, ary, ary_ex, isboxed), - idx, val, ety, - !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, - data_owner, 0, - false); // don't need to root the box if we had to make one since it's being stored in the array immediatly + if (jl_is_uniontype(ety)) { + Value *nbytes = ConstantInt::get(T_size, elsz); + Value *data = emit_bitcast(ctx, emit_arrayptr(ctx, ary, ary_ex), T_pint8); + if (!(val.typ == jl_bottom_type)) { + // compute tindex from val + jl_cgval_t rhs_union = convert_julia_type(ctx, val, ety); + Value *tindex = compute_tindex_unboxed(ctx, rhs_union, ety); + tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); + Value *selidx = ctx.builder.CreateMul(emit_arraylen_prim(ctx, ary), nbytes); + selidx = ctx.builder.CreateAdd(selidx, idx); + Value *ptindex = ctx.builder.CreateGEP(T_int8, data, selidx); + ctx.builder.CreateStore(tindex, ptindex); + if (jl_is_datatype(val.typ) && jl_datatype_size(val.typ) == 0) { + // no-op + } + else { + // copy data + Value *elidx = ctx.builder.CreateMul(idx, nbytes); + Value *addr = ctx.builder.CreateGEP(T_int8, data, elidx); + emit_unionmove(ctx, addr, val, NULL, false, NULL); + } + } + } + else { + typed_store(ctx, + emit_arrayptr(ctx, ary, ary_ex, isboxed), + idx, val, ety, + !isboxed ? tbaa_arraybuf : tbaa_ptrarraybuf, + data_owner, 0); + } + *ret = ary; + return true; } - *ret = ary; - return true; } } } } - else if (f == jl_builtin_getfield && nargs == 2) { + else if (f == jl_builtin_getfield && (nargs == 2 || nargs == 3)) { const jl_cgval_t &obj = argv[1]; const jl_cgval_t &fld = argv[2]; if (fld.constant && fld.typ == (jl_value_t*)jl_symbol_type) { @@ -2508,10 +2592,11 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, ctx.builder.CreateGEP(ctx.argArray, ConstantInt::get(T_size, ctx.nReqArgs)), NULL, false, NULL, NULL); Value *idx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen); + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + idx = emit_bounds_check(ctx, va_ary, NULL, idx, valen, boundscheck); idx = ctx.builder.CreateAdd(idx, ConstantInt::get(T_size, ctx.nReqArgs)); Value *v = tbaa_decorate(tbaa_value, ctx.builder.CreateLoad(ctx.builder.CreateGEP(ctx.argArray, idx))); - *ret = mark_julia_type(ctx, v, /*boxed*/ true, jl_any_type, /*needsgcroot*/ false); + *ret = mark_julia_type(ctx, v, /*boxed*/ true, jl_any_type); return true; } } @@ -2531,7 +2616,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else { // unknown index Value *vidx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - if (emit_getfield_unknownidx(ctx, ret, obj, vidx, utt)) { + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + if (emit_getfield_unknownidx(ctx, ret, obj, vidx, utt, boundscheck)) { return true; } } @@ -2550,11 +2636,13 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *vidx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); // This is not necessary for correctness, but allows to omit // the extra code for getting the length of the tuple - if (!bounds_check_enabled(ctx)) { + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + if (!bounds_check_enabled(ctx, boundscheck)) { vidx = ctx.builder.CreateSub(vidx, ConstantInt::get(T_size, 1)); } else { vidx = emit_bounds_check(ctx, obj, (jl_value_t*)obj.typ, vidx, - emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj))); + emit_datatype_nfields(ctx, emit_typeof_boxed(ctx, obj)), + jl_true); } Value *ptr = data_pointer(ctx, obj); *ret = typed_load(ctx, ptr, vidx, jt, obj.tbaa, false); @@ -2631,7 +2719,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin_fieldtype && nargs == 2) { + else if (f == jl_builtin_fieldtype && (nargs == 2 || nargs == 3)) { const jl_cgval_t &typ = argv[1]; const jl_cgval_t &fld = argv[2]; if ((jl_is_type_type(typ.typ) && jl_is_leaf_type(jl_tparam0(typ.typ))) || @@ -2643,7 +2731,8 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, Value *types_svec = emit_datatype_types(ctx, tyv); Value *types_len = emit_datatype_nfields(ctx, tyv); Value *idx = emit_unbox(ctx, T_size, fld, (jl_value_t*)jl_long_type); - emit_bounds_check(ctx, typ, (jl_value_t*)jl_datatype_type, idx, types_len); + jl_value_t *boundscheck = (nargs == 3 ? argv[3].constant : jl_true); + emit_bounds_check(ctx, typ, (jl_value_t*)jl_datatype_type, idx, types_len, boundscheck); Value *fieldtyp_p = ctx.builder.CreateGEP(decay_derived(emit_bitcast(ctx, types_svec, T_pprjlvalue)), idx); Value *fieldtyp = tbaa_decorate(tbaa_const, ctx.builder.CreateLoad(fieldtyp_p)); *ret = mark_julia_type(ctx, fieldtyp, true, (jl_value_t*)jl_type_type); @@ -2672,12 +2761,14 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, auto len = emit_arraylen(ctx, obj, ary_ex); jl_value_t *ety = jl_tparam0(sty); Value *elsize; + size_t elsz = 0, al = 0; + bool isboxed = !jl_islayout_inline(ety, &elsz, &al); if (!jl_has_free_typevars(ety)) { - if (!jl_array_store_unboxed(ety)) { + if (isboxed) { elsize = ConstantInt::get(T_size, sizeof(void*)); } else { - elsize = ConstantInt::get(T_size, jl_datatype_size(ety)); + elsize = ConstantInt::get(T_size, elsz); } } else { @@ -2764,7 +2855,7 @@ static Value *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, if (theF) theArgs.push_back(theF); for (size_t i = 0; i < nargs; i++) { - Value *arg = maybe_decay_untracked(boxed(ctx, argv[i], false)); + Value *arg = maybe_decay_untracked(boxed(ctx, argv[i])); theArgs.push_back(arg); } SmallVector argsT; @@ -2869,9 +2960,7 @@ static jl_cgval_t emit_call_function_object(jl_method_instance_t *li, jl_llvm_fu jlretty, tindex, tbaa_stack); - // root this, if the return value was a box (tindex & 0x80) != 0 - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(box, retval.gcroot); + retval.Vboxed = box; break; } case jl_returninfo_t::Ghosts: @@ -2922,12 +3011,11 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex) } } } - JL_FEAT_REQUIRE(ctx, runtime); jl_cgval_t result = mark_julia_type(ctx, emit_jlcall( ctx, prepare_call(jlinvoke_func), - boxed(ctx, lival, false), + boxed(ctx, lival), argv, nargs), true, rt); @@ -2974,16 +3062,6 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex) } } - if (!JL_FEAT_TEST(ctx, runtime)) { - char* name = NULL; - if (jl_is_symbol(args[0])) - name = jl_symbol_name((jl_sym_t*)args[0]); - if (jl_is_globalref(args[0])) - name = jl_symbol_name(jl_globalref_name(args[0])); - jl_errorf("generic call to %s requires the runtime language feature", - name ? name : ""); - } - // emit function and arguments Value *callval = emit_jlcall(ctx, jlapplygeneric_func, nullptr, argv, nargs); return mark_julia_type(ctx, callval, true, rt); @@ -3026,7 +3104,6 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t b = jl_get_binding(m, s); if (b == NULL) { // var not found. switch to delayed lookup. - JL_FEAT_REQUIRE(ctx, runtime); std::stringstream name; name << "delayedvar" << globalUnique++; Constant *initnul = V_null; @@ -3088,7 +3165,7 @@ static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i) maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jl_tvar_type))); jl_sym_t *name = (jl_sym_t*)jl_svecref(ctx.linfo->def.method->sparam_syms, i); undef_var_error_ifnot(ctx, isnull, name); - return mark_julia_type(ctx, sp, true, jl_any_type, false); + return mark_julia_type(ctx, sp, true, jl_any_type); } static jl_cgval_t emit_global(jl_codectx_t &ctx, jl_sym_t *sym) @@ -3236,7 +3313,6 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.boxroot != NULL) { Value *boxed = ctx.builder.CreateLoad(vi.boxroot, vi.isVolatile); Value *box_isnull; - v.gcroot = vi.boxroot; if (vi.usedUndef) box_isnull = ctx.builder.CreateICmpNE(boxed, maybe_decay_untracked(V_null)); if (vi.pTIndex) { @@ -3248,16 +3324,15 @@ static jl_cgval_t emit_local(jl_codectx_t &ctx, jl_value_t *slotload) if (vi.usedUndef) isnull = ctx.builder.CreateSelect(load_unbox, isnull, box_isnull); if (v.V) { // v.V will be null if it is a union of all ghost values - boxed = decay_derived(boxed); v.V = ctx.builder.CreateSelect(load_unbox, emit_bitcast(ctx, - decay_derived(v.V), boxed->getType()), boxed); + decay_derived(v.V), boxed->getType()), decay_derived(boxed)); } else v.V = boxed; + v.Vboxed = boxed; v = update_julia_type(ctx, v, typ); } else { - v = mark_julia_type(ctx, boxed, true, typ, - /*gc-root*/!vi.isArgument); // if an argument, doesn't need an additional root + v = mark_julia_type(ctx, boxed, true, typ); if (vi.usedUndef) isnull = box_isnull; } @@ -3309,33 +3384,6 @@ static Value *try_emit_union_alloca(jl_codectx_t &ctx, jl_uniontype_t *ut, bool return NULL; } -static Value *compute_box_tindex(jl_codectx_t &ctx, Value *datatype, jl_value_t *supertype, jl_value_t *ut) -{ - Value *tindex = ConstantInt::get(T_int8, 0); - unsigned counter = 0; - for_each_uniontype_small( - [&](unsigned idx, jl_datatype_t *jt) { - if (jl_subtype((jl_value_t*)jt, supertype)) { - Value *cmp = ctx.builder.CreateICmpEQ(maybe_decay_untracked(literal_pointer_val(ctx, (jl_value_t*)jt)), datatype); - tindex = ctx.builder.CreateSelect(cmp, ConstantInt::get(T_int8, idx), tindex); - } - }, - ut, - counter); - return tindex; -} - -// get the runtime tindex value -static Value *compute_tindex_unboxed(jl_codectx_t &ctx, const jl_cgval_t &val, jl_value_t *typ) -{ - if (val.constant) - return ConstantInt::get(T_int8, get_box_tindex((jl_datatype_t*)jl_typeof(val.constant), typ)); - if (val.isboxed) - return compute_box_tindex(ctx, emit_typeof_boxed(ctx, val), val.typ, typ); - assert(val.TIndex); - return ctx.builder.CreateAnd(val.TIndex, ConstantInt::get(T_int8, 0x7f)); -} - static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) { if (jl_is_ssavalue(l)) { @@ -3364,26 +3412,15 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) size_t min_align; dest = try_emit_union_alloca(ctx, ((jl_uniontype_t*)jt), allunbox, min_align); Value *isboxed = NULL; - if (slot.ispointer() && slot.V != NULL && !isa(slot.V)) { + if (slot.isboxed || slot.Vboxed != nullptr) { isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(slot.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); } if (dest) emit_unionmove(ctx, dest, slot, isboxed, false, NULL); - Value *gcroot = NULL; if (isboxed) { - Value *box; - if (slot.gcroot) { - gcroot = emit_local_root(ctx); - // This might load the wrong object in general, but if it gets selected, below, - // we know that it was in fact the one we wanted. - box = ctx.builder.CreateLoad(slot.gcroot); - } else { - gcroot = emit_static_alloca(ctx, T_pjlvalue); - box = V_null; - } - ctx.builder.CreateStore(box, gcroot); + Value *box = slot.Vboxed ? slot.Vboxed : V_null; if (dest) // might be all ghost values dest = ctx.builder.CreateSelect(isboxed, decay_derived(box), @@ -3394,8 +3431,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) else { assert(allunbox && "Failed to allocate correct union-type storage."); } + Value *box = slot.Vboxed; slot = mark_julia_slot(dest, slot.typ, slot.TIndex, tbaa_stack); - slot.gcroot = gcroot; + slot.Vboxed = box; } else { bool isboxed; @@ -3423,9 +3461,8 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (bp == NULL && s != NULL) bp = global_binding_pointer(ctx, ctx.module, s, &bnd, true); if (bp != NULL) { // it's a global - JL_FEAT_REQUIRE(ctx, runtime); assert(bnd); - Value *rval = mark_callee_rooted(boxed(ctx, emit_expr(ctx, r), false)); // no root needed since this is about to be assigned to a global + Value *rval = mark_callee_rooted(boxed(ctx, emit_expr(ctx, r))); ctx.builder.CreateCall(prepare_call(jlcheckassign_func), { literal_pointer_val(ctx, bnd), rval }); @@ -3441,14 +3478,9 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) if (!vi.used) return; - bool needs_root = false; - if ((!vi.isSA && rval_info.gcroot) || !rval_info.isboxed) - // rval needed a gcroot, so lval will need one too - needs_root = true; - // convert rval-type to lval-type jl_value_t *slot_type = vi.value.typ; - rval_info = convert_julia_type(ctx, rval_info, slot_type, /*needs-root*/true); + rval_info = convert_julia_type(ctx, rval_info, slot_type); if (rval_info.typ == jl_bottom_type) return; @@ -3483,23 +3515,18 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r) // store boxed variables Value *isboxed = NULL; if (vi.boxroot) { - if (isa(vi.boxroot) && needs_root) - emit_local_root(ctx, &vi); // promote variable slot to a gcroot Value *rval; if (vi.pTIndex && rval_info.TIndex) { ctx.builder.CreateStore(rval_info.TIndex, vi.pTIndex, vi.isVolatile); isboxed = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(rval_info.TIndex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - rval = maybe_decay_untracked(V_null); - if (rval_info.ispointer() && rval_info.V != NULL && !isa(rval_info.V) && - !(isa(isboxed) && cast(isboxed)->isZero())) // might be all ghost values or otherwise definitely not boxed - rval = ctx.builder.CreateLoad(rval_info.gcroot); + rval = maybe_decay_untracked(rval_info.Vboxed ? rval_info.Vboxed : V_null); assert(!vi.value.constant); } else { assert(!vi.pTIndex || rval_info.isboxed || rval_info.constant); - rval = maybe_decay_untracked(boxed(ctx, rval_info, false)); + rval = maybe_decay_untracked(boxed(ctx, rval_info)); } ctx.builder.CreateStore(maybe_decay_untracked(rval), vi.boxroot, vi.isVolatile); } @@ -3632,15 +3659,13 @@ static void emit_stmtpos(jl_codectx_t &ctx, jl_value_t *expr) jl_expr_t *ex = (jl_expr_t*)expr; jl_value_t **args = (jl_value_t**)jl_array_data(ex->args); jl_sym_t *head = ex->head; - if (head == line_sym || head == meta_sym || head == boundscheck_sym || - head == inbounds_sym) { + if (head == line_sym || head == meta_sym || head == inbounds_sym) { // some expression types are metadata and can be ignored // in statement position return; } else if (head == leave_sym) { assert(jl_is_long(args[0])); - JL_FEAT_REQUIRE(ctx, runtime); ctx.builder.CreateCall(prepare_call(jlleave_func), ConstantInt::get(T_int32, jl_unbox_long(args[0]))); } @@ -3750,7 +3775,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) else if (head == method_sym) { jl_value_t *mn = args[0]; assert(jl_expr_nargs(ex) != 1 || jl_is_symbol(mn) || jl_is_slot(mn)); - JL_FEAT_REQUIRE(ctx, runtime); Value *bp = NULL, *name, *bp_owner = V_null; jl_binding_t *bnd = NULL; @@ -3817,7 +3841,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) sym = jl_globalref_name(sym); } if (jl_is_symbol(sym)) { - JL_FEAT_REQUIRE(ctx, runtime); jl_binding_t *bnd = NULL; (void)global_binding_pointer(ctx, mod, sym, &bnd, true); assert(bnd); ctx.builder.CreateCall(prepare_call(jldeclareconst_func), @@ -3847,7 +3870,6 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) true, jl_any_type); } else if (head == copyast_sym) { - JL_FEAT_REQUIRE(ctx, runtime); jl_value_t *arg = args[0]; if (jl_is_quotenode(arg)) { jl_value_t *arg1 = jl_fieldref(arg, 0); @@ -3878,14 +3900,13 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) jl_error("Expr(:inbounds) in value position"); } else if (head == boundscheck_sym) { - jl_error("Expr(:boundscheck) in value position"); + return mark_julia_const(bounds_check_enabled(ctx, jl_true) ? jl_true : jl_false); } else { if (!strcmp(jl_symbol_name(head), "$")) jl_error("syntax: prefix \"$\" in non-quoted expression"); if (jl_is_toplevel_only_expr(expr) && !jl_is_method(ctx.linfo->def.method)) { - JL_FEAT_REQUIRE(ctx, runtime); // call interpreter to run a toplevel expr from inside a // compiled toplevel thunk. Value *args[2] = { @@ -3944,21 +3965,16 @@ static void emit_cfunc_invalidate( allocate_gc_frame(ctx, b0); Function::arg_iterator AI = gf_thunk->arg_begin(); - Value *myargs = new AllocaInst(T_prjlvalue, -#if JL_LLVM_VERSION >= 50000 - 0, -#endif - ConstantInt::get(T_int32, nargs), "jlcall", ctx.ptlsStates); + jl_cgval_t *myargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nargs); if (cc == jl_returninfo_t::SRet || cc == jl_returninfo_t::Union) ++AI; for (size_t i = 0; i < nargs; i++) { jl_value_t *jt = jl_nth_slot_type(lam->specTypes, i); bool isboxed; Type *et = julia_type_to_llvm(jt, &isboxed); - Value *arg_box; if (type_is_ghost(et)) { assert(jl_is_datatype(jt) && ((jl_datatype_t*)jt)->instance); - arg_box = literal_pointer_val(ctx, ((jl_datatype_t*)jt)->instance); + myargs[i] = mark_julia_const(((jl_datatype_t*)jt)->instance); } else { Value *arg_v = &*AI; @@ -3966,24 +3982,21 @@ static void emit_cfunc_invalidate( Type *at = arg_v->getType(); if (isboxed) { assert(at == T_prjlvalue && et == T_pjlvalue); - arg_box = arg_v; + myargs[i] = mark_julia_type(ctx, arg_v, true, jt); } else if (et->isAggregateType()) { - arg_box = boxed(ctx, mark_julia_slot(arg_v, jt, NULL, tbaa_const), false); + myargs[i] = mark_julia_slot(arg_v, jt, NULL, tbaa_const); } else { assert(at == et); - arg_box = boxed(ctx, mark_julia_type(ctx, arg_v, false, jt), false); + myargs[i] = mark_julia_type(ctx, arg_v, false, jt); } (void)at; } - Value *argn = ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue, myargs, i); - ctx.builder.CreateStore(maybe_decay_untracked(arg_box), argn); } assert(AI == gf_thunk->arg_end()); - Value *nargs_v = ConstantInt::get(T_int32, nargs); - Value *gf_ret = ctx.builder.CreateCall(prepare_call(jlapplygeneric_func), { myargs, nargs_v }); - jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type, /*needsroot*/false); + Value *gf_ret = emit_jlcall(ctx, jlapplygeneric_func, NULL, myargs, nargs); + jl_cgval_t gf_retbox = mark_julia_type(ctx, gf_ret, true, jl_any_type); jl_value_t *astrt = lam->rettype; if (cc != jl_returninfo_t::Boxed) { emit_typecheck(ctx, gf_retbox, astrt, "cfunction"); @@ -4350,7 +4363,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t if (toboxed) { assert(!sig.sret); // return a jl_value_t* - r = boxed(ctx, retval, false); // no gcroot since this is on the return path + r = boxed(ctx, retval); } else if (sig.sret && jlfunc_sret) { // nothing to do @@ -4562,10 +4575,10 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin jl_cgval_t retval; switch (f.cc) { case jl_returninfo_t::Boxed: - retval = mark_julia_type(ctx, call, true, jlretty, /*needsroot*/false); + retval = mark_julia_type(ctx, call, true, jlretty); break; case jl_returninfo_t::Register: - retval = mark_julia_type(ctx, call, false, jlretty, /*needsroot*/false); + retval = mark_julia_type(ctx, call, false, jlretty); break; case jl_returninfo_t::SRet: retval = mark_julia_slot(result, jlretty, NULL, tbaa_stack); @@ -4577,19 +4590,18 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin jlretty, ctx.builder.CreateExtractValue(call, 1), tbaa_stack); - retval.gcroot = emit_local_root(ctx); - ctx.builder.CreateStore(ctx.builder.CreateExtractValue(call, 0), retval.gcroot); + retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); break; case jl_returninfo_t::Ghosts: retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); break; } - ctx.builder.CreateRet(boxed(ctx, retval, false)); // no gcroot needed since this on the return path + ctx.builder.CreateRet(boxed(ctx, retval)); assert(!ctx.roots); return w; } -static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, bool va, jl_code_info_t *src) +static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, bool va, jl_code_info_t *src, bool prefer_specsig) { if (va || needsparam) return false; @@ -4602,6 +4614,8 @@ static bool uses_specsig(jl_value_t *sig, jl_value_t *rettype, bool needsparam, if (jl_nparams(sig) == 0) return false; // not invalid, consider if specialized signature is worthwhile + if (prefer_specsig) + return true; if (isbits_spec(rettype, false)) return true; if (jl_is_uniontype(rettype)) { @@ -4855,7 +4869,7 @@ static std::unique_ptr emit_function( } jl_value_t *jlrettype = lam->rettype; - bool specsig = uses_specsig(lam->specTypes, jlrettype, needsparams, va, src); + bool specsig = uses_specsig(lam->specTypes, jlrettype, needsparams, va, src, params->prefer_specsig); if (!specsig) ctx.nReqArgs--; // function not part of argArray in jlcall @@ -5207,17 +5221,17 @@ static std::unique_ptr emit_function( Argument *Arg = &*AI++; if (isboxed) maybe_mark_argument_dereferenceable(Arg, argType); - theArg = mark_julia_type(ctx, Arg, isboxed, argType, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, Arg, isboxed, argType); } } else { if (i == 0) { // first (function) arg is separate in jlcall - theArg = mark_julia_type(ctx, fArg, true, vi.value.typ, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, fArg, true, vi.value.typ); } else { Value *argPtr = ctx.builder.CreateGEP(argArray, ConstantInt::get(T_size, i-1)); - theArg = mark_julia_type(ctx, ctx.builder.CreateLoad(argPtr), true, vi.value.typ, /*needsgcroot*/false); + theArg = mark_julia_type(ctx, ctx.builder.CreateLoad(argPtr), true, vi.value.typ); if (ctx.debug_enabled && vi.dinfo && !vi.boxroot && !vi.value.V) { SmallVector addr; addr.push_back(llvm::dwarf::DW_OP_deref); @@ -5255,10 +5269,8 @@ static std::unique_ptr emit_function( } } else { - Value *argp = boxed(ctx, theArg, false); // skip the temporary gcroot since it would be folded to argp anyways + Value *argp = boxed(ctx, theArg); ctx.builder.CreateStore(argp, vi.boxroot); - if (!theArg.isboxed) - emit_local_root(ctx, &vi); // create a root for vi } // get arrayvar data if applicable if (arrayvars.find(i) != arrayvars.end()) { @@ -5285,7 +5297,6 @@ static std::unique_ptr emit_function( ConstantInt::get(T_int32, nreq - 1)) }); restTuple->setAttributes(jltuple_func->getAttributes()); ctx.builder.CreateStore(restTuple, vi.boxroot); - emit_local_root(ctx, &vi); // create a root for vi } } @@ -5307,24 +5318,14 @@ static std::unique_ptr emit_function( DebugLoc loc; StringRef file; ssize_t line; - bool is_inbounds; bool loc_changed; bool is_poploc; bool in_user_code; }; std::vector stmtprops(stmtslen); std::vector DI_stack; - std::vector inbounds_stack{false}; - auto is_inbounds = [&] () { - // inbounds rule is either of top two values on inbounds stack are true - size_t sz = inbounds_stack.size(); - bool inbounds = sz && inbounds_stack.back(); - if (sz > 1) - inbounds |= inbounds_stack[sz - 2]; - return inbounds; - }; StmtProp cur_prop{topdebugloc, filename, toplineno, - false, true, false, false}; + true, false, false}; ctx.line = &cur_prop.line; if (coverage_mode != JL_LOG_NONE || malloc_log_mode) { cur_prop.in_user_code = (!jl_is_submodule(ctx.module, jl_base_module) && @@ -5431,29 +5432,9 @@ static std::unique_ptr emit_function( cur_prop.loc_changed = true; } } - if (expr) { - jl_value_t **args = (jl_value_t**)jl_array_data(expr->args); - if (expr->head == inbounds_sym) { - // manipulate inbounds stack - if (jl_array_len(expr->args) > 0) { - jl_value_t *arg = args[0]; - if (arg == jl_true) { - inbounds_stack.push_back(true); - } - else if (arg == jl_false) { - inbounds_stack.push_back(false); - } - else if (!inbounds_stack.empty()) { - inbounds_stack.pop_back(); - } - } - } - } - cur_prop.is_inbounds = is_inbounds(); stmtprops[i] = cur_prop; } DI_stack.clear(); - inbounds_stack.clear(); // step 12. Do codegen in control flow order std::vector> workstack; @@ -5556,7 +5537,6 @@ static std::unique_ptr emit_function( !props.is_poploc) { coverageVisitLine(ctx, props.file, props.line); } - ctx.is_inbounds = props.is_inbounds; jl_value_t *stmt = jl_array_ptr_ref(stmts, cursor); jl_expr_t *expr = jl_is_expr(stmt) ? (jl_expr_t*)stmt : nullptr; if (jl_is_labelnode(stmt)) { @@ -5569,7 +5549,7 @@ static std::unique_ptr emit_function( // this is basically a copy of emit_assignment, // but where the assignment slot is the retval jl_cgval_t retvalinfo = emit_expr(ctx, jl_exprarg(expr, 0)); - retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype, /*needs-root*/false); + retvalinfo = convert_julia_type(ctx, retvalinfo, jlrettype); if (retvalinfo.typ == jl_bottom_type) { ctx.builder.CreateUnreachable(); find_next_stmt(-1); @@ -5582,7 +5562,7 @@ static std::unique_ptr emit_function( Type *retty = f->getReturnType(); switch (returninfo.cc) { case jl_returninfo_t::Boxed: - retval = boxed(ctx, retvalinfo, false); // skip the gcroot on the return path + retval = boxed(ctx, retvalinfo); // skip the gcroot on the return path break; case jl_returninfo_t::Register: if (type_is_ghost(retty)) @@ -5604,20 +5584,13 @@ static std::unique_ptr emit_function( } else { data = maybe_decay_untracked(V_null); - if (retvalinfo.ispointer() && !isa(retvalinfo.V)) { + if (retvalinfo.Vboxed) { // also need to account for the possibility the return object is boxed // and avoid / skip copying it to the stack isboxed_union = ctx.builder.CreateICmpNE( ctx.builder.CreateAnd(tindex, ConstantInt::get(T_int8, 0x80)), ConstantInt::get(T_int8, 0)); - // Lift the select, because gcroot may be NULL if - // there's no boxed value. - if (isa(isboxed_union)) - data = cast(isboxed_union)->isZero() ? data : ctx.builder.CreateLoad(retvalinfo.gcroot); - else - data = ctx.builder.CreateSelect(isboxed_union, - ctx.builder.CreateLoad(retvalinfo.gcroot), - data); + data = ctx.builder.CreateSelect(isboxed_union, retvalinfo.Vboxed, data); } } } @@ -5626,7 +5599,7 @@ static std::unique_ptr emit_function( //assert(retvalinfo.isboxed); tindex = compute_tindex_unboxed(ctx, retvalinfo, jlrettype); tindex = ctx.builder.CreateOr(tindex, ConstantInt::get(T_int8, 0x80)); - data = maybe_decay_untracked(boxed(ctx, retvalinfo, false)); // skip the gcroot on the return path + data = maybe_decay_untracked(boxed(ctx, retvalinfo)); sret = NULL; } retval = UndefValue::get(retty); @@ -5906,6 +5879,7 @@ static void init_julia_llvm_meta(void) tbaa_arraylen = tbaa_make_child("jtbaa_arraylen", tbaa_array_scalar).first; tbaa_arrayflags = tbaa_make_child("jtbaa_arrayflags", tbaa_array_scalar).first; tbaa_const = tbaa_make_child("jtbaa_const", nullptr, true).first; + tbaa_arrayselbyte = tbaa_make_child("jtbaa_arrayselbyte", tbaa_array_scalar).first; } static void init_julia_llvm_env(Module *m) @@ -6023,7 +5997,7 @@ static void init_julia_llvm_env(Module *m) StructType::create(jl_LLVMContext, ArrayRef(vaelts,sizeof(vaelts)/sizeof(vaelts[0])), "jl_array_t"); - jl_parray_llvmt = PointerType::get(jl_array_llvmt,0); + jl_parray_llvmt = PointerType::get(jl_array_llvmt, 0); global_to_llvm("__stack_chk_guard", (void*)&__stack_chk_guard, m); Function *jl__stack_chk_fail = @@ -6266,13 +6240,6 @@ static void init_julia_llvm_env(Module *m) jlapply2va_func = jlcall_func_to_llvm("jl_apply_2va", &jl_apply_2va, m); - std::vector argsdepwarnpi(0); - argsdepwarnpi.push_back(T_size); - jldepwarnpi_func = Function::Create(FunctionType::get(T_void, argsdepwarnpi, false), - Function::ExternalLinkage, - "jl_depwarn_partial_indexing", m); - add_named_global(jldepwarnpi_func, &jl_depwarn_partial_indexing); - std::vector args_1ptr(0); args_1ptr.push_back(T_prjlvalue); queuerootfun = Function::Create(FunctionType::get(T_void, args_1ptr, false), @@ -6513,7 +6480,13 @@ static void init_julia_llvm_env(Module *m) "julia.gcroot_flush"); add_named_global(gcroot_flush_func, (void*)NULL, /*dllimport*/false); - pointer_from_objref_func = Function::Create(FunctionType::get(T_pjlvalue, + gc_use_func = Function::Create(FunctionType::get(T_void, + ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), + Function::ExternalLinkage, + "julia.gc_use"); + add_named_global(gc_use_func, (void*)NULL, /*dllimport*/false); + + pointer_from_objref_func = Function::Create(FunctionType::get(T_size, ArrayRef(PointerType::get(T_jlvalue, AddressSpace::Derived)), false), Function::ExternalLinkage, "julia.pointer_from_objref"); @@ -6533,7 +6506,7 @@ static void init_julia_llvm_env(Module *m) add_named_global(jlgetworld_global, &jl_world_counter); jl_globalPM = new legacy::PassManager(); - jl_globalPM->add(new TargetLibraryInfoWrapperPass(Triple(jl_TargetMachine->getTargetTriple()))); + addTargetPasses(jl_globalPM, jl_TargetMachine); addOptimizationPasses(jl_globalPM, jl_options.opt_level); } diff --git a/src/common_symbols1.inc b/src/common_symbols1.inc index 013dfdd15dc06..09b1cf5c6adb5 100644 --- a/src/common_symbols1.inc +++ b/src/common_symbols1.inc @@ -14,7 +14,6 @@ jl_symbol("getindex"), jl_symbol("new"), jl_symbol("arrayref"), jl_symbol("static_parameter"), -jl_symbol("abstractarray.jl"), jl_symbol("slt_int"), jl_symbol("convert"), jl_symbol("start"), diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index 4554dfef79b5b..0a1d0eb73cbea 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -249,4 +249,4 @@ jl_symbol("NF"), jl_symbol("isvarargtype"), jl_symbol("n"), jl_symbol("inferred"), -jl_symbol("eachindex"), +jl_symbol("abstractarray.jl"), diff --git a/src/datatype.c b/src/datatype.c index 72f21e2beaf2c..9fb6b19d5591f 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -8,9 +8,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -230,6 +230,44 @@ STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) } } +static int jl_layout_isbits(jl_value_t *ty) +{ + if (jl_isbits(ty) && jl_is_leaf_type(ty)) { + if (((jl_datatype_t*)ty)->layout) // layout check handles possible layout recursion + return 1; + } + return 0; +} + +static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) +{ + if (jl_is_uniontype(ty)) { + unsigned na = union_isbits(((jl_uniontype_t*)ty)->a, nbytes, align); + if (na == 0) + return 0; + unsigned nb = union_isbits(((jl_uniontype_t*)ty)->b, nbytes, align); + if (nb == 0) + return 0; + return na + nb; + } + if (jl_layout_isbits(ty)) { + size_t sz = jl_datatype_size(ty); + size_t al = jl_datatype_align(ty); + if (*nbytes < sz) + *nbytes = sz; + if (*align < al) + *align = al; + return 1; + } + return 0; +} + +JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al) +{ + unsigned countbits = union_isbits(eltype, fsz, al); + return countbits > 0 && countbits < 127; +} + void jl_compute_field_offsets(jl_datatype_t *st) { size_t sz = 0, alignm = 1; @@ -293,16 +331,20 @@ void jl_compute_field_offsets(jl_datatype_t *st) for (size_t i = 0; i < nfields; i++) { jl_value_t *ty = jl_field_type(st, i); - size_t fsz, al; - if (jl_isbits(ty) && jl_is_leaf_type(ty) && ((jl_datatype_t*)ty)->layout) { - fsz = jl_datatype_size(ty); - // Should never happen + size_t fsz = 0, al = 0; + if (jl_islayout_inline(ty, &fsz, &al)) { if (__unlikely(fsz > max_size)) + // Should never happen goto throw_ovf; - al = jl_datatype_align(ty); desc[i].isptr = 0; - if (((jl_datatype_t*)ty)->layout->haspadding) + if (jl_is_uniontype(ty)) { haspadding = 1; + fsz += 1; // selector byte + } + else { // isbits struct + if (((jl_datatype_t*)ty)->layout->haspadding) + haspadding = 1; + } } else { fsz = sizeof(void*); @@ -328,7 +370,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) goto throw_ovf; sz += fsz; } - if (homogeneous && lastty!=NULL && jl_is_tuple_type(st)) { + if (homogeneous && lastty != NULL && jl_is_tuple_type(st)) { // Some tuples become LLVM vectors with stronger alignment than what was calculated above. unsigned al = jl_special_vector_alignment(nfields, lastty); assert(al % alignm == 0); @@ -448,21 +490,17 @@ typedef struct { int64_t b; } bits128_t; -// Note that this function updates len -static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) +// TODO: do we care that this has invalid alignment assumptions? +JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *dt, void *data) { jl_ptls_t ptls = jl_get_ptls_states(); assert(jl_is_datatype(dt)); jl_datatype_t *bt = (jl_datatype_t*)dt; size_t nb = jl_datatype_size(bt); - if (nb == 0) - return jl_new_struct_uninit(bt); - *len = LLT_ALIGN(*len, jl_datatype_align(bt)); - data = (char*)data + (*len); - *len += nb; + if (nb == 0) return jl_new_struct_uninit(bt); // returns bt->instance if (bt == jl_uint8_type) return jl_box_uint8(*(uint8_t*)data); if (bt == jl_int64_type) return jl_box_int64(*(int64_t*)data); - if (bt == jl_bool_type) return (*(int8_t*)data) ? jl_true:jl_false; + if (bt == jl_bool_type) return (*(int8_t*)data) ? jl_true : jl_false; if (bt == jl_int32_type) return jl_box_int32(*(int32_t*)data); if (bt == jl_float64_type) return jl_box_float64(*(double*)data); @@ -478,10 +516,13 @@ static jl_value_t *jl_new_bits_internal(jl_value_t *dt, void *data, size_t *len) return v; } -JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *bt, void *data) +// used by boot.jl +JL_DLLEXPORT jl_value_t *jl_typemax_uint(jl_value_t *bt) { - size_t len = 0; - return jl_new_bits_internal(bt, data, &len); + uint64_t data = 0xffffffffffffffffULL; + jl_value_t *v = jl_gc_alloc(jl_get_ptls_states(), sizeof(size_t), bt); + memcpy(jl_data_ptr(v), &data, sizeof(size_t)); + return v; } void jl_assign_bits(void *dest, jl_value_t *bits) @@ -666,7 +707,7 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) size_t nf = jl_datatype_nfields(type); va_start(args, type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); - for(size_t i=0; i < nf; i++) { + for (size_t i = 0; i < nf; i++) { jl_set_nth_field(jv, i, va_arg(args, jl_value_t*)); } va_end(args); @@ -680,7 +721,10 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, if (type->instance != NULL) return type->instance; size_t nf = jl_datatype_nfields(type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); - for(size_t i=0; i < na; i++) { + for (size_t i = 0; i < na; i++) { + jl_value_t *ft = jl_field_type(type, i); + if (!jl_isa(args[i], ft)) + jl_type_error("new", ft, args[i]); jl_set_nth_field(jv, i, args[i]); } for(size_t i=na; i < nf; i++) { @@ -722,37 +766,62 @@ JL_DLLEXPORT jl_value_t *jl_get_nth_field(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); assert(i < jl_datatype_nfields(st)); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { return *(jl_value_t**)((char*)v + offs); } - return jl_new_bits(jl_field_type(st,i), (char*)v + offs); + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + uint8_t sel = ((uint8_t*)v)[offs + jl_field_size(st, i) - 1]; + ty = jl_nth_union_component(ty, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + return ((jl_datatype_t*)ty)->instance; + } + return jl_new_bits(ty, (char*)v + offs); } JL_DLLEXPORT jl_value_t *jl_get_nth_field_checked(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); if (i >= jl_datatype_nfields(st)) - jl_bounds_error_int(v, i+1); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + jl_bounds_error_int(v, i + 1); + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { jl_value_t *fval = *(jl_value_t**)((char*)v + offs); if (fval == NULL) jl_throw(jl_undefref_exception); return fval; } - return jl_new_bits(jl_field_type(st,i), (char*)v + offs); + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + size_t fsz = jl_field_size(st, i); + uint8_t sel = ((uint8_t*)v)[offs + fsz - 1]; + ty = jl_nth_union_component(ty, sel); + if (jl_is_datatype_singleton((jl_datatype_t*)ty)) + return ((jl_datatype_t*)ty)->instance; + } + return jl_new_bits(ty, (char*)v + offs); } JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { *(jl_value_t**)((char*)v + offs) = rhs; if (rhs != NULL) jl_gc_wb(v, rhs); } else { + jl_value_t *ty = jl_field_type(st, i); + if (jl_is_uniontype(ty)) { + uint8_t *psel = &((uint8_t*)v)[offs + jl_field_size(st, i) - 1]; + unsigned nth = 0; + if (!jl_find_union_component(ty, jl_typeof(rhs), &nth)) + assert(0 && "invalid field assignment to isbits union"); + *psel = nth; + if (jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(rhs))) + return; + } jl_assign_bits((char*)v + offs, rhs); } } @@ -760,8 +829,8 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, jl_value_t *rhs) JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i) { jl_datatype_t *st = (jl_datatype_t*)jl_typeof(v); - size_t offs = jl_field_offset(st,i); - if (jl_field_isptr(st,i)) { + size_t offs = jl_field_offset(st, i); + if (jl_field_isptr(st, i)) { return *(jl_value_t**)((char*)v + offs) != NULL; } return 1; diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index b2a3f070f60ed..3c232af029afd 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -22,7 +22,6 @@ #include #include #include -#include "fix_llvm_assert.h" using namespace llvm; @@ -46,7 +45,7 @@ using llvm_file_magic = sys::fs::file_magic; #include #include #include -#include +#include "julia_assert.h" typedef object::SymbolRef SymRef; diff --git a/src/disasm.cpp b/src/disasm.cpp index 219836e4a59b8..a85d714cf1e25 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -69,13 +69,13 @@ #include #include #include "llvm/IR/AssemblyAnnotationWriter.h" -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" using namespace llvm; #include "debuginfo.h" +#include "julia_assert.h" // helper class for tracking inlining context while printing debug info class DILineInfoPrinter { diff --git a/src/dlload.c b/src/dlload.c index d7232408f268c..371aa38804fa9 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "platform.h" @@ -16,6 +15,7 @@ #include #include #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/dump.c b/src/dump.c index 0480d1a5b09c0..ba2df8034321f 100644 --- a/src/dump.c +++ b/src/dump.c @@ -5,7 +5,6 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" @@ -20,6 +19,7 @@ #else #define RUNNING_ON_VALGRIND 0 #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -570,7 +570,8 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li jl_serialize_value(s, jl_box_long(jl_array_dim(ar,i))); jl_serialize_value(s, jl_typeof(ar)); if (!ar->flags.ptrarray) { - size_t tot = jl_array_len(ar) * ar->elsize; + size_t extra = jl_is_uniontype(jl_tparam0(jl_typeof(ar))) ? jl_array_len(ar) : 0; + size_t tot = jl_array_len(ar) * ar->elsize + extra; ios_write(s->s, (char*)jl_array_data(ar), tot); } else { @@ -721,8 +722,11 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li size_t i, nf = jl_datatype_nfields(jl_typemap_entry_type); while ((jl_value_t*)te != jl_nothing) { for (i = 1; i < nf; i++) { - if (jl_field_size(jl_typemap_entry_type, i) > 0) + if (jl_field_size(jl_typemap_entry_type, i) > 0) { jl_serialize_value(s, jl_get_nth_field((jl_value_t*)te, i)); + if (!jl_field_isptr(jl_typemap_entry_type, i)) + write_int8(s->s, 0); + } } te = te->next; } @@ -808,7 +812,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li return; } size_t nf = jl_datatype_nfields(t); - if (nf == 0 && jl_datatype_size(t)>0) { + if (nf == 0 && jl_datatype_size(t) > 0) { if (t->name == jl_pointer_typename && jl_unbox_voidpointer(v) != (void*)-1) { // normalize most pointers to NULL, to help catch memory errors // but permit MAP_FAILED / INVALID_HANDLE to be stored unchanged @@ -824,8 +828,17 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li else { size_t i; for (i = 0; i < nf; i++) { - if (jl_field_size(t, i) > 0) { + size_t offs = jl_field_offset(t, i); + size_t fsz = jl_field_size(t, i); + if (fsz > 0) { jl_serialize_value(s, jl_get_nth_field(v, i)); + if (!jl_field_isptr(t, i)) { + uint8_t sel = 0; + if (jl_is_uniontype(jl_field_type(t, i))) { + sel = ((uint8_t*)v)[offs + fsz - 1]; + } + write_int8(s->s, sel); + } } } } @@ -1334,7 +1347,8 @@ static jl_value_t *jl_deserialize_value_array(jl_serializer_state *s, jl_value_t jl_value_t *aty = jl_deserialize_value(s, &jl_astaggedvalue(a)->type); jl_set_typeof(a, aty); if (!a->flags.ptrarray) { - size_t tot = jl_array_len(a) * a->elsize; + size_t extra = jl_is_uniontype(jl_tparam0(aty)) ? jl_array_len(a) : 0; + size_t tot = jl_array_len(a) * a->elsize + extra; ios_read(s->s, (char*)jl_array_data(a), tot); } else { @@ -1589,13 +1603,21 @@ static void jl_deserialize_struct(jl_serializer_state *s, jl_value_t *v, size_t size_t i, nf = jl_datatype_nfields(dt); char *data = (char*)jl_data_ptr(v); for (i = startfield; i < nf; i++) { - if (jl_field_size(dt, i) > 0) { + size_t offs = jl_field_offset(dt, i); + size_t fsz = jl_field_size(dt, i); + jl_value_t **fld = (jl_value_t**)(data + offs); + if (fsz > 0) { if (jl_field_isptr(dt, i)) { - jl_value_t **fld = (jl_value_t**)(data+jl_field_offset(dt, i)); *fld = jl_deserialize_value(s, fld); } else { - jl_set_nth_field(v, i, jl_deserialize_value(s, NULL)); + jl_value_t *fldval = jl_deserialize_value(s, NULL); + jl_assign_bits((char*)fld, fldval); + uint8_t union_selector = read_uint8(s->s); + if (union_selector) { + uint8_t *psel = (uint8_t*)fld + fsz - 1; + *psel = union_selector - 1; + } } } } @@ -1674,7 +1696,7 @@ static jl_value_t *jl_deserialize_value_any(jl_serializer_state *s, jl_value_t * } } jl_set_typeof(v, dt); - if (jl_datatype_nfields(dt) == 0 && jl_datatype_size(dt)>0) { + if (jl_datatype_nfields(dt) == 0 && jl_datatype_size(dt) > 0) { int nby = jl_datatype_size(dt); ios_read(s->s, (char*)jl_data_ptr(v), nby); } @@ -2218,7 +2240,6 @@ JL_DLLEXPORT void jl_fill_argnames(jl_array_t *data, jl_array_t *names) } else { uint8_t *d = (uint8_t*)data->data; -#ifndef NDEBUG assert(jl_typeis(data, jl_array_uint8_type)); int b3 = d[1]; int b2 = d[2]; @@ -2226,7 +2247,7 @@ JL_DLLEXPORT void jl_fill_argnames(jl_array_t *data, jl_array_t *names) int b0 = d[4]; int nslots = b0 | (b1<<8) | (b2<<16) | (b3<<24); assert(nslots >= nargs); -#endif + (void)nslots; char *namestr = (char*)d + 5; for (i = 0; i < nargs; i++) { size_t namelen = strlen(namestr); @@ -2290,7 +2311,7 @@ JL_DLLEXPORT int jl_save_incremental(const char *fname, jl_array_t *worklist) return 0; } -#ifndef NDEBUG +#ifndef JL_NDEBUG // skip the performance optimizations of jl_types_equal and just use subtyping directly // one of these types is invalid - that's why we're doing the recache type operation static int jl_invalid_types_equal(jl_datatype_t *a, jl_datatype_t *b) @@ -2672,7 +2693,7 @@ void jl_init_serializer(void) jl_emptysvec, jl_emptytuple, jl_false, jl_true, jl_nothing, jl_any_type, call_sym, invoke_sym, goto_ifnot_sym, return_sym, body_sym, line_sym, - lambda_sym, jl_symbol("tuple"), assign_sym, isdefined_sym, + lambda_sym, jl_symbol("tuple"), assign_sym, isdefined_sym, boundscheck_sym, // empirical list of very common symbols #include "common_symbols1.inc" diff --git a/src/fix_llvm_assert.h b/src/fix_llvm_assert.h deleted file mode 100644 index 776b127c15846..0000000000000 --- a/src/fix_llvm_assert.h +++ /dev/null @@ -1,16 +0,0 @@ -// This file is a part of Julia. License is MIT: https://julialang.org/license - -// Include this file after every blocks of LLVM includes to set the assertion back. - -#ifdef NDEBUG -# ifndef JL_NDEBUG -# undef NDEBUG -# include -// Set NDEBUG back so that we can include another LLVM header right after -# define NDEBUG -# endif -#else -# ifdef JL_NDEBUG -# undef JL_NDEBUG -# endif -#endif diff --git a/src/gc-debug.c b/src/gc-debug.c index 0e59a5594575e..4f40e5d737023 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -8,7 +8,7 @@ // so that we can always use the assert macro in this file // for use under their respective enable flags #undef NDEBUG -#include +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gc-pages.c b/src/gc-pages.c index b0b3feca52c6e..60c89fd977c5b 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -4,6 +4,7 @@ #ifndef _OS_WINDOWS_ # include #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/gc.c b/src/gc.c index 3e59fdeb534d0..0c706061f6f13 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1,6 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include "gc.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -857,6 +858,9 @@ static size_t array_nbytes(jl_array_t *a) sz = a->elsize * a->maxsize + (a->elsize == 1 ? 1 : 0); else sz = a->elsize * jl_array_len(a); + if (!a->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(a)))) + // account for isbits Union array selector bytes + sz += jl_array_len(a); return sz; } @@ -1974,7 +1978,7 @@ module_binding: { } finlist: { - // Scan a finalizer list. see `gc_mark_finlist_t` + // Scan a finalizer (or format compatible) list. see `gc_mark_finlist_t` gc_mark_finlist_t *finlist = gc_pop_markdata(&sp, gc_mark_finlist_t); jl_value_t **begin = finlist->begin; jl_value_t **end = finlist->end; @@ -2273,6 +2277,8 @@ static void mark_roots(jl_gc_mark_cache_t *gc_cache, gc_mark_sp_t *sp) // constants gc_mark_queue_obj(gc_cache, sp, jl_typetype_type); gc_mark_queue_obj(gc_cache, sp, jl_emptytuple_type); + + gc_mark_queue_finlist(gc_cache, sp, &partial_inst, 0); } // find unmarked objects that need to be finalized from the finalizer list "list". diff --git a/src/gc.h b/src/gc.h index 3817e717d0d34..71ae65b3175f7 100644 --- a/src/gc.h +++ b/src/gc.h @@ -14,7 +14,6 @@ #ifndef _MSC_VER #include #endif -#include #include #include "julia.h" #include "julia_internal.h" @@ -25,6 +24,7 @@ #define MAP_ANONYMOUS MAP_ANON #endif #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -188,7 +188,7 @@ typedef struct { uint8_t bits; // GC bits of the module (the bits to mark the binding buffer with) } gc_mark_binding_t; -// Finalizer list +// Finalizer (or object) list typedef struct { jl_value_t **begin; jl_value_t **end; diff --git a/src/gf.c b/src/gf.c index 42c4d60fe7c52..6e01d6de2669a 100644 --- a/src/gf.c +++ b/src/gf.c @@ -10,12 +10,12 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" #ifndef _OS_WINDOWS_ #include #endif +#include "julia_assert.h" // @nospecialize has no effect if the number of overlapping methods is greater than this #define MAX_UNSPECIALIZED_CONFLICTS 32 @@ -1223,7 +1223,7 @@ static void method_overwrite(jl_typemap_entry_t *newentry, jl_method_t *oldvalue jl_method_t *method = (jl_method_t*)newentry->func.method; jl_module_t *newmod = method->module; jl_module_t *oldmod = oldvalue->module; - if (newmod != jl_main_module || oldmod != jl_main_module) { + if (jl_options.warn_overwrite == JL_OPTIONS_WARN_OVERWRITE_ON) { JL_STREAM *s = JL_STDERR; jl_printf(s, "WARNING: Method definition "); jl_static_show_func_sig(s, (jl_value_t*)newentry->sig); @@ -1552,7 +1552,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ JL_UNLOCK(&mt->writelock); return linfo; } - if (jl_is_leaf_type((jl_value_t*)types)) + if (jl_is_leaf_type((jl_value_t*)types)) // FIXME: this is the wrong predicate cache = 1; jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, types, cache, allow_exec, world); if (cache) { @@ -1745,9 +1745,25 @@ JL_DLLEXPORT jl_value_t *jl_get_spec_lambda(jl_tupletype_t *types, size_t world) return li ? (jl_value_t*)li : jl_nothing; } +// see if a call to m with computed from `types` is ambiguous +JL_DLLEXPORT int jl_is_call_ambiguous(jl_tupletype_t *types, jl_method_t *m) +{ + if (m->ambig == jl_nothing) + return 0; + for (size_t i = 0; i < jl_array_len(m->ambig); i++) { + jl_method_t *mambig = (jl_method_t*)jl_array_ptr_ref(m->ambig, i); + if (jl_subtype((jl_value_t*)types, (jl_value_t*)mambig->sig)) + return 1; + } + return 0; +} + +// see if a call to m with a subtype of `types` might be ambiguous +// if types is from a call signature (approximated by isleaftype), this is the same as jl_is_call_ambiguous above JL_DLLEXPORT int jl_has_call_ambiguities(jl_tupletype_t *types, jl_method_t *m) { - if (m->ambig == jl_nothing) return 0; + if (m->ambig == jl_nothing) + return 0; for (size_t i = 0; i < jl_array_len(m->ambig); i++) { jl_method_t *mambig = (jl_method_t*)jl_array_ptr_ref(m->ambig, i); if (!jl_has_empty_intersection((jl_value_t*)mambig->sig, (jl_value_t*)types)) @@ -1951,6 +1967,8 @@ JL_DLLEXPORT jl_value_t *jl_gf_invoke_lookup(jl_datatype_t *types, size_t world) JL_GC_POP(); if (!entry) return jl_nothing; + if (jl_is_call_ambiguous(types, entry->func.method)) + return jl_nothing; return (jl_value_t*)entry; } diff --git a/src/init.c b/src/init.c index 9d9ef5273e1bc..3556f7d8a90d9 100644 --- a/src/init.c +++ b/src/init.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include @@ -24,6 +23,7 @@ #include "builtin_proto.h" #undef DEFINE_BUILTIN_GLOBALS #include "threading.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -744,11 +744,6 @@ static jl_value_t *core(const char *name) return jl_get_global(jl_core_module, jl_symbol(name)); } -static jl_value_t *basemod(const char *name) -{ - return jl_get_global(jl_base_module, jl_symbol(name)); -} - // fetch references to things defined in boot.jl void jl_get_builtin_hooks(void) { @@ -799,17 +794,11 @@ void jl_get_builtin_hooks(void) jl_weakref_type = (jl_datatype_t*)core("WeakRef"); jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name; -} - -JL_DLLEXPORT void jl_get_system_hooks(void) -{ - if (jl_argumenterror_type) return; // only do this once - jl_argumenterror_type = (jl_datatype_t*)basemod("ArgumentError"); - jl_methoderror_type = (jl_datatype_t*)basemod("MethodError"); - jl_loaderror_type = (jl_datatype_t*)basemod("LoadError"); - jl_initerror_type = (jl_datatype_t*)basemod("InitError"); - jl_complex_type = (jl_unionall_t*)basemod("Complex"); + jl_argumenterror_type = (jl_datatype_t*)core("ArgumentError"); + jl_methoderror_type = (jl_datatype_t*)core("MethodError"); + jl_loaderror_type = (jl_datatype_t*)core("LoadError"); + jl_initerror_type = (jl_datatype_t*)core("InitError"); } void jl_get_builtins(void) diff --git a/src/interpreter.c b/src/interpreter.c index b995aa4cbe8ec..3892df26f90ac 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -2,13 +2,13 @@ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "builtin_proto.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -270,8 +270,12 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) JL_GC_PUSH2(&thetype, &v); assert(jl_is_structtype(thetype)); v = jl_new_struct_uninit((jl_datatype_t*)thetype); - for(size_t i=1; i < nargs; i++) { - jl_set_nth_field(v, i-1, eval(args[i], s)); + for (size_t i = 1; i < nargs; i++) { + jl_value_t *ft = jl_field_type(thetype, i - 1); + jl_value_t *fldv = eval(args[i], s); + if (!jl_isa(fldv, ft)) + jl_type_error("new", ft, fldv); + jl_set_nth_field(v, i - 1, fldv); } JL_GC_POP(); return v; @@ -376,7 +380,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) } else if (ex->head == primtype_sym) { if (inside_typedef) - jl_error("cannot eval a new bits type definition while defining another type"); + jl_error("cannot eval a new primitive type definition while defining another type"); jl_value_t *name = args[0]; jl_value_t *super = NULL, *para = NULL, *vnb = NULL, *temp = NULL; jl_datatype_t *dt = NULL; @@ -391,11 +395,11 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) assert(jl_is_svec(para)); vnb = eval(args[2], s); if (!jl_is_long(vnb)) - jl_errorf("invalid declaration of bits type %s", + jl_errorf("invalid declaration of primitive type %s", jl_symbol_name((jl_sym_t*)name)); ssize_t nb = jl_unbox_long(vnb); - if (nb < 1 || nb>=(1<<23) || (nb&7) != 0) - jl_errorf("invalid number of bits in type %s", + if (nb < 1 || nb >= (1 << 23) || (nb & 7) != 0) + jl_errorf("invalid number of bits in primitive type %s", jl_symbol_name((jl_sym_t*)name)); dt = jl_new_primitivetype(name, modu, NULL, (jl_svec_t*)para, nb); w = dt->name->wrapper; @@ -424,7 +428,7 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) } else if (ex->head == structtype_sym) { if (inside_typedef) - jl_error("cannot eval a new data type definition while defining another type"); + jl_error("cannot eval a new struct type definition while defining another type"); jl_value_t *name = args[0]; jl_value_t *para = eval(args[1], s); jl_value_t *temp = NULL; @@ -458,12 +462,13 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_set_datatype_super(dt, super); dt->types = (jl_svec_t*)eval(args[4], s); jl_gc_wb(dt, dt->types); - for(size_t i=0; i < jl_svec_len(dt->types); i++) { + for (size_t i = 0; i < jl_svec_len(dt->types); i++) { jl_value_t *elt = jl_svecref(dt->types, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt)) + if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { jl_type_error_rt(jl_symbol_name(dt->name->name), "type definition", (jl_value_t*)jl_type_type, elt); + } } jl_reinstantiate_inner_types(dt); } @@ -495,12 +500,15 @@ static jl_value_t *eval(jl_value_t *e, interpreter_state *s) jl_errorf("syntax: %s", jl_string_data(args[0])); jl_throw(args[0]); } + else if (ex->head == boundscheck_sym) { + return jl_true; + } else if (ex->head == boundscheck_sym || ex->head == inbounds_sym || ex->head == fastmath_sym || ex->head == simdloop_sym || ex->head == meta_sym) { return jl_nothing; } jl_errorf("unsupported or misplaced expression %s", jl_symbol_name(ex->head)); - return (jl_value_t*)jl_nothing; + abort(); } jl_value_t *jl_toplevel_eval_body(jl_module_t *m, jl_array_t *stmts) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 5b7397ddc80d0..f59169287a0e0 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -146,99 +146,113 @@ static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) return ctx.builder.CreateZExt(x, to); } -#if JL_LLVM_VERSION >= 40000 -#define LLVM_FP(a,b) APFloat(a(),b) -#else -#define LLVM_FP(a,b) APFloat(a,b) -#endif -static Constant *julia_const_to_llvm(void *ptr, jl_value_t *bt) +static Constant *julia_const_to_llvm(const void *ptr, jl_datatype_t *bt) { // assumes `jl_isbits(bt)`. // `ptr` can point to a inline field, do not read the tag from it. // make sure to return exactly the type specified by // julia_type_to_llvm as this will be assumed by the callee. - if (bt == (jl_value_t*)jl_bool_type) - return ConstantInt::get(T_int8, (*(uint8_t*)ptr) ? 1 : 0); + if (bt == jl_bool_type) + return ConstantInt::get(T_int8, (*(const uint8_t*)ptr) ? 1 : 0); - if (bt == (jl_value_t*)jl_ssavalue_type) - return NULL; + if (jl_is_vecelement_type((jl_value_t*)bt)) + bt = (jl_datatype_t*)jl_tparam0(bt); + + Type *lt = julia_struct_to_llvm((jl_value_t*)bt, NULL, NULL); - if (jl_is_vecelement_type(bt)) - bt = jl_tparam0(bt); + if (type_is_ghost(lt)) + return UndefValue::get(NoopType); - if (jl_is_cpointer_type(bt)) - return ConstantExpr::getIntToPtr(ConstantInt::get(T_size, *(uintptr_t*)ptr), julia_type_to_llvm(bt)); if (jl_is_primitivetype(bt)) { - int nb = jl_datatype_size(bt); - // TODO: non-power-of-2 size datatypes may not be interpreted correctly on big-endian systems - switch (nb) { - case 1: { - uint8_t data8 = *(uint8_t*)ptr; - return ConstantInt::get(T_int8, data8); - } - case 2: { - uint16_t data16 = *(uint16_t*)ptr; - return ConstantInt::get(T_int16, data16); - } - case 4: { - uint32_t data32 = *(uint32_t*)ptr; - if (bt == (jl_value_t*)jl_float32_type) - return ConstantFP::get(jl_LLVMContext, - LLVM_FP(APFloat::IEEEsingle, - APInt(32, data32))); - return ConstantInt::get(T_int32, data32); + if (lt->isFloatTy()) { + uint32_t data32 = *(const uint32_t*)ptr; + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), APInt(32, data32))); } - case 8: { - uint64_t data64 = *(uint64_t*)ptr; - if (bt == (jl_value_t*)jl_float64_type) - return ConstantFP::get(jl_LLVMContext, - LLVM_FP(APFloat::IEEEdouble, - APInt(64, data64))); - return ConstantInt::get(T_int64, data64); + if (lt->isDoubleTy()) { + uint64_t data64 = *(const uint64_t*)ptr; + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), APInt(64, data64))); } - default: - size_t nw = (nb+sizeof(uint64_t)-1)/sizeof(uint64_t); - uint64_t *data = (uint64_t*)ptr; - APInt val; -#if !defined(_P64) - // malloc may not be 16-byte aligned on P32, - // but we must ensure that llvm's uint64_t reads don't fall - // off the end of a page - // where 16-byte alignment requirement == (8-byte typetag) % (uint64_t ArrayRef access) - if (nb % 16 != 0) { - uint64_t *data_a64 = (uint64_t*)alloca(sizeof(uint64_t)*nw); - memcpy(data_a64, data, nb); - val = APInt(8*nb, ArrayRef(data_a64, nw)); - } - else -#endif - val = APInt(8*nb, ArrayRef(data, nw)); - return ConstantInt::get(IntegerType::get(jl_LLVMContext,8*nb),val); + int nb = jl_datatype_size(bt); + APInt val(8 * nb, 0); + void *bits = const_cast(val.getRawData()); + assert(sys::IsLittleEndianHost); + memcpy(bits, ptr, nb); + if (lt->isFloatingPointTy()) { + return ConstantFP::get(jl_LLVMContext, + APFloat(lt->getFltSemantics(), val)); } + assert(cast(lt)->getBitWidth() == 8u * nb); + return ConstantInt::get(lt, val); } + + CompositeType *lct = cast(lt); size_t nf = jl_datatype_nfields(bt); - Constant **fields = (Constant**)alloca(nf * sizeof(Constant*)); + std::vector fields(nf); for (size_t i = 0; i < nf; i++) { - size_t offs = jl_field_offset((jl_datatype_t*)bt, i); + size_t offs = jl_field_offset(bt, i); + assert(!jl_field_isptr(bt, i)); jl_value_t *ft = jl_field_type(bt, i); - Constant *val = julia_const_to_llvm((char*)ptr + offs, ft); - if (val == NULL) - return NULL; + Type *lft = lct->getTypeAtIndex(i); + const uint8_t *ov = (const uint8_t*)ptr + offs; + Constant *val; + if (jl_is_uniontype(ft)) { + // compute the same type layout as julia_struct_to_llvm + size_t fsz = jl_field_size(bt, i); + size_t al = jl_field_align(bt, i); + uint8_t sel = ((const uint8_t*)ptr)[offs + fsz - 1]; + jl_value_t *active_ty = jl_nth_union_component(ft, sel); + size_t active_sz = jl_datatype_size(active_ty); + ArrayType *aty = cast(cast(lft)->getTypeAtIndex(0u)); + assert(aty->getElementType() == IntegerType::get(jl_LLVMContext, 8 * al) && + aty->getNumElements() == (fsz - 1) / al); + std::vector ArrayElements(0); + for (unsigned j = 0; j < aty->getNumElements(); j++) { + APInt Elem(8 * al, 0); + void *bits = const_cast(Elem.getRawData()); + if (active_sz > al) { + memcpy(bits, ov, al); + active_sz -= al; + } + else if (active_sz > 0) { + memcpy(bits, ov, active_sz); + active_sz = 0; + } + ov += al; + ArrayElements.push_back(ConstantInt::get(aty->getElementType(), Elem)); + } + std::vector Elements(0); + Elements.push_back(ConstantArray::get(aty, ArrayElements)); + unsigned remainder = (fsz - 1) % al; + while (remainder--) { + uint8_t byte; + if (active_sz > 0) { + byte = *ov; + active_sz -= 1; + } + else { + byte = 0; + } + ov += 1; + APInt Elem(8, byte); + Elements.push_back(ConstantInt::get(T_int8, Elem)); + } + Elements.push_back(ConstantInt::get(T_int8, sel)); + val = ConstantStruct::get(cast(lft), Elements); + } + else { + val = julia_const_to_llvm(ov, (jl_datatype_t*)ft); + } fields[i] = val; } - Type *t = julia_struct_to_llvm(bt, NULL, NULL); - if (type_is_ghost(t)) - return UndefValue::get(NoopType); - if (t->isVectorTy()) - return ConstantVector::get(ArrayRef(fields, nf)); - if (StructType *st = dyn_cast(t)) { - return ConstantStruct::get(st, ArrayRef(fields, nf)); - } - else { - ArrayType *at = cast(t); - return ConstantArray::get(at, ArrayRef(fields, nf)); - } + if (lct->isVectorTy()) + return ConstantVector::get(fields); + if (StructType *st = dyn_cast(lct)) + return ConstantStruct::get(st, fields); + ArrayType *at = cast(lct); + return ConstantArray::get(at, fields); } static Constant *julia_const_to_llvm(jl_value_t *e) @@ -250,7 +264,7 @@ static Constant *julia_const_to_llvm(jl_value_t *e) jl_value_t *bt = jl_typeof(e); if (!jl_isbits(bt)) return NULL; - return julia_const_to_llvm(e, bt); + return julia_const_to_llvm(e, (jl_datatype_t*)bt); } static jl_cgval_t ghostValue(jl_value_t *ty); @@ -302,6 +316,9 @@ static Value *emit_unbox(jl_codectx_t &ctx, Type *to, const jl_cgval_t &x, jl_va } if (!dest) return unboxed; + Type *dest_ty = unboxed->getType()->getPointerTo(); + if (dest->getType() != dest_ty) + dest = emit_bitcast(ctx, dest, dest_ty); ctx.builder.CreateStore(unboxed, dest, volatile_store); return NULL; } @@ -574,15 +591,15 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) Value *idx = emit_unbox(ctx, T_size, i, (jl_value_t*)jl_long_type); Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(T_size, 1)); - if (!jl_isbits(ety)) { - if (ety == (jl_value_t*)jl_any_type) { - Value *thePtr = emit_unbox(ctx, T_pprjlvalue, e, e.typ); - return mark_julia_type( - ctx, - ctx.builder.CreateAlignedLoad(ctx.builder.CreateGEP(thePtr, im1), align_nb), - true, - ety); - } + if (ety == (jl_value_t*)jl_any_type) { + Value *thePtr = emit_unbox(ctx, T_pprjlvalue, e, e.typ); + return mark_julia_type( + ctx, + ctx.builder.CreateAlignedLoad(ctx.builder.CreateGEP(T_prjlvalue, thePtr, im1), align_nb), + true, + ety); + } + else if (!jl_isbits(ety)) { if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { emit_error(ctx, "pointerref: invalid pointer type"); return jl_cgval_t(); @@ -594,16 +611,17 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) im1 = ctx.builder.CreateMul(im1, ConstantInt::get(T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); Value *thePtr = emit_unbox(ctx, T_pint8, e, e.typ); - thePtr = ctx.builder.CreateGEP(emit_bitcast(ctx, thePtr, T_pint8), im1); + thePtr = ctx.builder.CreateGEP(T_int8, emit_bitcast(ctx, thePtr, T_pint8), im1); ctx.builder.CreateMemCpy(emit_bitcast(ctx, strct, T_pint8), thePtr, size, 1); return mark_julia_type(ctx, strct, true, ety); } - - bool isboxed; - Type *ptrty = julia_type_to_llvm(e.typ, &isboxed); - assert(!isboxed); - Value *thePtr = emit_unbox(ctx, ptrty, e, e.typ); - return typed_load(ctx, thePtr, im1, ety, tbaa_data, true, align_nb); + else { + bool isboxed; + Type *ptrty = julia_type_to_llvm(ety, &isboxed); + assert(!isboxed); + Value *thePtr = emit_unbox(ctx, ptrty->getPointerTo(), e, e.typ); + return typed_load(ctx, thePtr, im1, ety, tbaa_data, true, align_nb); + } } static jl_cgval_t emit_runtime_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) @@ -641,7 +659,15 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) Value *im1 = ctx.builder.CreateSub(idx, ConstantInt::get(T_size, 1)); Value *thePtr; - if (!jl_isbits(ety) && ety != (jl_value_t*)jl_any_type) { + if (ety == (jl_value_t*)jl_any_type) { + // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. + thePtr = emit_unbox(ctx, T_psize, e, e.typ); + Instruction *store = ctx.builder.CreateAlignedStore( + emit_pointer_from_objref(ctx, boxed(ctx, x)), + ctx.builder.CreateGEP(T_size, thePtr, im1), align_nb); + tbaa_decorate(tbaa_data, store); + } + else if (!jl_isbits(ety)) { if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { emit_error(ctx, "pointerset: invalid pointer type"); return jl_cgval_t(); @@ -650,23 +676,15 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) uint64_t size = jl_datatype_size(ety); im1 = ctx.builder.CreateMul(im1, ConstantInt::get(T_size, LLT_ALIGN(size, jl_datatype_align(ety)))); - ctx.builder.CreateMemCpy(ctx.builder.CreateGEP(thePtr, im1), + ctx.builder.CreateMemCpy(ctx.builder.CreateGEP(T_int8, thePtr, im1), data_pointer(ctx, x, T_pint8), size, align_nb); } else { bool isboxed; - Type *ptrty = julia_type_to_llvm(e.typ, &isboxed); + Type *ptrty = julia_type_to_llvm(ety, &isboxed); assert(!isboxed); - thePtr = emit_unbox(ctx, ptrty, e, e.typ); - if (ety == (jl_value_t*)jl_any_type) { - // unsafe_store to Ptr{Any} is allowed to implicitly drop GC roots. - Instruction *store = ctx.builder.CreateAlignedStore( - emit_pointer_from_objref(ctx, boxed(ctx, x, false)), - ctx.builder.CreateGEP(thePtr, im1), align_nb); - tbaa_decorate(tbaa_data, store); - } else { - typed_store(ctx, thePtr, im1, x, ety, tbaa_data, NULL, align_nb); - } + thePtr = emit_unbox(ctx, ptrty->getPointerTo(), e, e.typ); + typed_store(ctx, thePtr, im1, x, ety, tbaa_data, NULL, align_nb); } return mark_julia_type(ctx, thePtr, false, aty); } diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index 83fd0f31852c9..cd986a0336643 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -60,13 +60,14 @@ namespace llvm { #include #include #include "codegen_shared.h" -#include "fix_llvm_assert.h" using namespace llvm; #include "julia.h" #include "julia_internal.h" #include "jitlayers.h" +#include "julia_assert.h" + RTDyldMemoryManager* createRTDyldMemoryManager(void); static Type *T_void; @@ -92,7 +93,14 @@ void jl_init_jit(Type *T_pjlvalue_) // Except for parts of this file which were copied from LLVM, under the UIUC license (marked below). -// this defines the set of optimization passes defined for Julia at various optimization levels +void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM) +{ + PM->add(new TargetLibraryInfoWrapperPass(Triple(TM->getTargetTriple()))); + PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); +} + +// this defines the set of optimization passes defined for Julia at various optimization levels. +// it assumes that the TLI and TTI wrapper passes have already been added. void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level) { #ifdef JL_DEBUG_BUILD @@ -132,7 +140,6 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level) return; } PM->add(createPropagateJuliaAddrspaces()); - PM->add(createTargetTransformInfoWrapperPass(jl_TargetMachine->getTargetIRAnalysis())); PM->add(createTypeBasedAAWrapperPass()); if (jl_options.opt_level >= 3) { PM->add(createBasicAAWrapperPass()); @@ -485,6 +492,7 @@ JuliaOJIT::JuliaOJIT(TargetMachine &TM) CompilerT(this) ) { + addTargetPasses(&PM, &TM); addOptimizationPasses(&PM, jl_generating_output() ? 0 : jl_options.opt_level); if (TM.addPassesToEmitMC(PM, Ctx, ObjStream)) llvm_unreachable("Target does not support MC emission."); @@ -1137,7 +1145,7 @@ void jl_dump_native(const char *bc_fname, const char *unopt_bc_fname, const char )); legacy::PassManager PM; - PM.add(new TargetLibraryInfoWrapperPass(Triple(TM->getTargetTriple()))); + addTargetPasses(&PM, TM.get()); // set up optimization passes std::unique_ptr unopt_bc_OS; @@ -1269,6 +1277,7 @@ class JuliaPipeline : public Pass { (void)jl_init_llvm(); PMTopLevelManager *TPM = Stack.top()->getTopLevelManager(); TPMAdapter Adapter(TPM); + addTargetPasses(&Adapter, jl_TargetMachine); addOptimizationPasses(&Adapter, OptLevel); } JuliaPipeline() : Pass(PT_PassManager, ID) {} diff --git a/src/jitlayers.h b/src/jitlayers.h index 96e0c3bc19f69..9b20c9d704a47 100644 --- a/src/jitlayers.h +++ b/src/jitlayers.h @@ -22,7 +22,7 @@ extern legacy::PassManager *jl_globalPM; #include -#include "fix_llvm_assert.h" +#include "julia_assert.h" extern "C" { extern int globalUnique; @@ -42,6 +42,7 @@ extern size_t jltls_offset_idx; typedef struct {Value *gv; int32_t index;} jl_value_llvm; // uses 1-based indexing +void addTargetPasses(legacy::PassManagerBase *PM, TargetMachine *TM); void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level); void* jl_emit_and_add_to_shadow(GlobalVariable *gv, void *gvarinit = NULL); GlobalVariable *jl_emit_sysimg_slot(Module *m, Type *typ, const char *name, diff --git a/src/jl_uv.c b/src/jl_uv.c index c8ad59e7e4152..640c4afd9baad 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -7,7 +7,6 @@ #include #include #include -#include #ifdef _OS_WINDOWS_ #include @@ -28,6 +27,8 @@ #define write _write #endif +#include "julia_assert.h" + #ifdef __cplusplus #include extern "C" { diff --git a/src/jlapi.c b/src/jlapi.c index be3e74be8b578..ebd16f11914ba 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -10,9 +10,9 @@ #include #include #include -#include #include "julia.h" #include "options.h" +#include "julia_assert.h" #ifdef __cplusplus #include diff --git a/src/jloptions.c b/src/jloptions.c index 61698c7c38e3d..64b512212c67c 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -10,6 +10,7 @@ #else #include "getopt.h" #endif +#include "julia_assert.h" #ifdef _OS_WINDOWS_ char *shlib_ext = ".dll"; @@ -34,6 +35,7 @@ JL_DLLEXPORT const char *jl_get_default_sysimg_path(void) jl_options_t jl_options = { 0, // quiet + -1, // banner NULL, // julia_home NULL, // julia_bin NULL, // eval @@ -58,6 +60,7 @@ jl_options_t jl_options = { 0, // quiet #endif JL_OPTIONS_CHECK_BOUNDS_DEFAULT, // check_bounds 1, // deprecation warning + 0, // method overwrite warning 1, // can_inline JL_OPTIONS_POLLY_ON, // polly JL_OPTIONS_FAST_MATH_DEFAULT, @@ -69,6 +72,7 @@ jl_options_t jl_options = { 0, // quiet NULL, // bind-to NULL, // output-bc NULL, // output-unopt-bc + NULL, // output-jit-bc NULL, // output-o NULL, // output-ji 0, // incremental @@ -100,7 +104,8 @@ static const char opts[] = // interactive options " -i Interactive mode; REPL runs and isinteractive() is true\n" - " -q, --quiet Quiet startup (no banner)\n" + " -q, --quiet Quiet startup: no banner, suppress REPL warnings\n" + " --banner={yes|no} Enable or disable startup banner\n" " --color={yes|no} Enable or disable color text\n" " --history-file={yes|no} Load or save history\n\n" @@ -123,6 +128,7 @@ static const char opts[] = // error and warning options " --depwarn={yes|no|error} Enable or disable syntax and method deprecation warnings (\"error\" turns warnings into errors)\n\n" + " --warn-overwrite={yes|no} Enable or disable method overwrite warnings" // compiler output options " --output-o name Generate an object file (including system image data)\n" @@ -156,6 +162,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_output_unopt_bc, opt_output_bc, opt_depwarn, + opt_warn_overwrite, opt_inline, opt_polly, opt_math_mode, @@ -166,7 +173,8 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_output_ji, opt_use_precompiled, opt_use_compilecache, - opt_incremental + opt_incremental, + opt_banner }; static const char* const shortopts = "+vhqH:e:E:L:J:C:ip:O:g:"; static const struct option longopts[] = { @@ -176,6 +184,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "version", no_argument, 0, 'v' }, { "help", no_argument, 0, 'h' }, { "quiet", no_argument, 0, 'q' }, + { "banner", required_argument, 0, opt_banner }, { "home", required_argument, 0, 'H' }, { "eval", required_argument, 0, 'e' }, { "print", required_argument, 0, 'E' }, @@ -201,6 +210,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "output-ji", required_argument, 0, opt_output_ji }, { "output-incremental",required_argument, 0, opt_incremental }, { "depwarn", required_argument, 0, opt_depwarn }, + { "warn-overwrite", required_argument, 0, opt_warn_overwrite }, { "inline", required_argument, 0, opt_inline }, { "polly", required_argument, 0, opt_polly }, { "math-mode", required_argument, 0, opt_math_mode }, @@ -275,9 +285,6 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) case 'h': // help jl_printf(JL_STDOUT, "%s%s", usage, opts); jl_exit(0); - case 'q': // quiet - jl_options.quiet = 1; - break; case 'g': // debug info if (optarg != NULL) { if (!strcmp(optarg,"0")) @@ -310,6 +317,19 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) jl_options.image_file = strdup(optarg); jl_options.image_file_specified = 1; break; + case 'q': // quiet + jl_options.quiet = 1; + if (jl_options.banner < 0) + jl_options.banner = 0; + break; + case opt_banner: // banner + if (!strcmp(optarg,"yes")) + jl_options.banner = 1; + else if (!strcmp(optarg,"no")) + jl_options.banner = 0; + else + jl_errorf("julia: invalid argument to --banner={yes|no} (%s)", optarg); + break; case opt_use_precompiled: if (!strcmp(optarg,"yes")) jl_options.use_precompiled = JL_OPTIONS_USE_PRECOMPILED_YES; @@ -478,6 +498,14 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --depwarn={yes|no|error} (%s)", optarg); break; + case opt_warn_overwrite: + if (!strcmp(optarg,"yes")) + jl_options.warn_overwrite = JL_OPTIONS_WARN_OVERWRITE_ON; + else if (!strcmp(optarg,"no")) + jl_options.warn_overwrite = JL_OPTIONS_WARN_OVERWRITE_OFF; + else + jl_errorf("julia: invalid argument to --warn-overwrite={yes|no|} (%s)", optarg); + break; case opt_inline: if (!strcmp(optarg,"yes")) jl_options.can_inline = 1; diff --git a/src/jltypes.c b/src/jltypes.c index d50ad990ae9d8..95b215913711e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -7,13 +7,13 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" #include "builtin_proto.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -65,7 +65,6 @@ jl_datatype_t *jl_float32_type; jl_datatype_t *jl_float64_type; jl_datatype_t *jl_floatingpoint_type; jl_datatype_t *jl_number_type; -jl_unionall_t *jl_complex_type; jl_datatype_t *jl_signed_type; JL_DLLEXPORT jl_value_t *jl_emptytuple=NULL; @@ -128,7 +127,7 @@ jl_value_t *jl_memory_exception; jl_value_t *jl_readonlymemory_exception; union jl_typemap_t jl_cfunction_list; -jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 1, 1, 1, {NULL, NULL, NULL}}; +jl_cgparams_t jl_default_cgparams = {1, 1, 1, 1, 0, NULL, NULL, NULL}; // --- type properties and predicates --- @@ -271,7 +270,7 @@ JL_DLLEXPORT int (jl_is_leaf_type)(jl_value_t *v) { if (jl_is_datatype(v)) { int isleaf = ((jl_datatype_t*)v)->isleaftype; -#ifdef NDEBUG +#ifdef JL_NDEBUG return isleaf; #else if (((jl_datatype_t*)v)->abstract) { @@ -369,6 +368,22 @@ jl_value_t *jl_nth_union_component(jl_value_t *v, int i) return nth_union_component(v, &i); } +// inverse of jl_nth_union_component +int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth) +{ + if (jl_is_uniontype(haystack)) { + if (jl_find_union_component(((jl_uniontype_t*)haystack)->a, needle, nth)) + return 1; + if (jl_find_union_component(((jl_uniontype_t*)haystack)->b, needle, nth)) + return 1; + return 0; + } + if (needle == haystack) + return 1; + (*nth)++; + return 0; +} + static void flatten_type_union(jl_value_t **types, size_t n, jl_value_t **out, size_t *idx) { size_t i; @@ -1065,7 +1080,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si JL_GC_POP(); } -static arraylist_t partial_inst; +arraylist_t partial_inst; int inside_typedef = 0; static jl_value_t *extract_wrapper(jl_value_t *t) @@ -1648,9 +1663,9 @@ void jl_init_types(void) jl_methtable_type = jl_new_uninitialized_datatype(); jl_nothing = jl_gc_permobj(0, NULL); - jl_default_cgparams.hooks.module_setup = jl_nothing; - jl_default_cgparams.hooks.module_activation = jl_nothing; - jl_default_cgparams.hooks.raise_exception = jl_nothing; + jl_default_cgparams.module_setup = jl_nothing; + jl_default_cgparams.module_activation = jl_nothing; + jl_default_cgparams.raise_exception = jl_nothing; jl_emptysvec = (jl_svec_t*)jl_gc_permobj(sizeof(void*), jl_simplevector_type); jl_svec_set_len_unsafe(jl_emptysvec, 0); @@ -1832,14 +1847,10 @@ void jl_init_types(void) jl_emptytuple_type->instance = jl_emptytuple; // non-primitive definitions follow - jl_int32_type = NULL; jl_int32_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int32"), core, jl_any_type, jl_emptysvec, 32); - jl_int64_type = NULL; jl_int64_type = jl_new_primitivetype((jl_value_t*)jl_symbol("Int64"), core, jl_any_type, jl_emptysvec, 64); - - jl_uint8_type = NULL; jl_uint8_type = jl_new_primitivetype((jl_value_t*)jl_symbol("UInt8"), core, jl_any_type, jl_emptysvec, 8); diff --git a/src/julia-parser.scm b/src/julia-parser.scm index b7ca3a19577c6..53e36ec03eeaf 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -337,13 +337,20 @@ (write-char (read-char port) str) (read-digs #t #f) (disallow-dot)) - (io.ungetc port c)))) + (io.ungetc port c))))) + (if (and (char? c) + (or (eq? pred char-bin?) (eq? pred char-oct?) + (and (eq? pred char-hex?) (not is-hex-float-literal))) + (or (char-numeric? c) + (and (identifier-start-char? c) + (syntax-deprecation port ;; remove after v0.7 + (string (get-output-string str) c) + (string (get-output-string str) " * " c)) + #f))) ;; remove after v0.7 ;; disallow digits after binary or octal literals, e.g., 0b12 - (if (and (or (eq? pred char-bin?) (eq? pred char-oct?)) - (not (eof-object? c)) - (char-numeric? c)) - (error (string "invalid numeric constant \"" - (get-output-string str) c "\""))))) + ;; and disallow identifier chars after hex literals. + (error (string "invalid numeric constant \"" + (get-output-string str) c "\"")))) (let* ((s (get-output-string str)) (r (cond ((eq? pred char-hex?) 16) ((eq? pred char-oct?) 8) @@ -1238,6 +1245,11 @@ (and (eq? (car sig) 'where) (valid-func-sig? paren (cadr sig)))))) +(define (valid-1arg-func-sig? sig) + (or (symbol? sig) + (and (pair? sig) (eq? (car sig) '|::|) + (symbol? (cadr sig))))) + (define (unwrap-where x) (if (and (pair? x) (eq? (car x) 'where)) (unwrap-where (cadr x)) @@ -1291,6 +1303,21 @@ `(for ,(if (length= ranges 1) (car ranges) (cons 'block ranges)) ,body))) + ((let) + (let ((binds (if (memv (peek-token s) '(#\newline #\;)) + '() + (parse-comma-separated-assignments s)))) + (if (not (or (eof-object? (peek-token s)) + (memv (peek-token s) '(#\newline #\; end)))) + (error "let variables should end in \";\" or newline")) + (let* ((ex (begin0 (parse-block s) + (expect-end s word))) + (ex (if (and (length= ex 2) (pair? (cadr ex)) (eq? (caadr ex) 'line)) + `(block) ;; don't need line info in an empty let block + ex))) + `(let ,(if (length= binds 1) (car binds) (cons 'block binds)) + ,ex)))) + ((if elseif) (if (newline? (peek-token s)) (error (string "missing condition in \"if\" at " current-filename @@ -1319,19 +1346,6 @@ (begin0 (list word test then (parse-block s)) (expect-end s 'if))) (else (error (string "unexpected \"" nxt "\"")))))) - ((let) - (let ((binds (if (memv (peek-token s) '(#\newline #\;)) - '() - (parse-comma-separated-assignments s)))) - (if (not (or (eof-object? (peek-token s)) - (memv (peek-token s) '(#\newline #\; end)))) - (error "let variables should end in \";\" or newline")) - (let ((ex (parse-block s))) - (expect-end s word) - ;; don't need line info in an empty let block - (if (and (length= ex 2) (pair? (cadr ex)) (eq? (caadr ex) 'line)) - `(let (block) ,@binds) - `(let ,ex ,@binds))))) ((global local) (let* ((const (and (eq? (peek-token s) 'const) @@ -1361,9 +1375,9 @@ (take-token s) `(function ,sig)) (let* ((usig (unwrap-where sig)) - (def (if (or (symbol? usig) - (and (pair? usig) (eq? (car usig) '|::|) - (symbol? (cadr usig)))) + (def (if (or (valid-1arg-func-sig? usig) + (and (assignment? usig) + (valid-1arg-func-sig? (cadr usig)))) (if paren ;; in "function (x)" the (x) is a tuple (rewrap-where `(tuple ,usig) sig) @@ -1452,7 +1466,10 @@ (var (if nl #f (parse-eq* s))) (var? (and (not nl) (or (and (symbol? var) (not (eq? var 'false)) (not (eq? var 'true))) - (and (length= var 2) (eq? (car var) '$))))) + (and (length= var 2) (eq? (car var) '$)) + (and (syntax-deprecation s (string "catch " (deparse var) "") + (string "catch; " (deparse var) "")) + #f)))) (catch-block (if (eq? (require-token s) 'finally) `(block ,(line-number-node s)) (parse-block s)))) @@ -1599,7 +1616,18 @@ ;; as above, but allows both "i=r" and "i in r" (define (parse-iteration-spec s) - (let* ((lhs (parse-pipes s)) + (let* ((outer? (if (eq? (peek-token s) 'outer) + (begin + (take-token s) + (let ((nxt (peek-token s))) + (if (or (memq nxt '(= in ∈)) + (not (symbol? nxt)) + (operator? nxt)) + (begin (ts:put-back! s 'outer #t) + #f) + #t))) + #f)) + (lhs (parse-pipes s)) (t (peek-token s))) (cond ((memq t '(= in ∈)) (take-token s) @@ -1609,7 +1637,9 @@ ;; should be: (error "invalid iteration specification") (syntax-deprecation s (string "for " (deparse `(= ,lhs ,rhs)) " " t) (string "for " (deparse `(= ,lhs ,rhs)) "; " t))) - `(= ,lhs ,rhs))) + (if outer? + `(= (outer ,lhs) ,rhs) + `(= ,lhs ,rhs)))) ((and (eq? lhs ':) (closing-token? t)) ':) (else (error "invalid iteration specification"))))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 172478f3550fc..2385ab724bcfe 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -90,20 +90,16 @@ ;; the array `a` in the `n`th index. ;; `tuples` are a list of the splatted arguments that precede index `n` ;; `last` = is this last index? -;; returns a call to endof(a), trailingsize(a,n), or size(a,n) +;; returns a call to endof(a) or size(a,n) (define (end-val a n tuples last) (if (null? tuples) - (if last - (if (= n 1) - `(call (top endof) ,a) - `(call (top trailingsize) ,a ,n)) + (if (and last (= n 1)) + `(call (top endof) ,a) `(call (top size) ,a ,n)) (let ((dimno `(call (top +) ,(- n (length tuples)) ,.(map (lambda (t) `(call (top length) ,t)) tuples)))) - (if last - `(call (top trailingsize) ,a ,dimno) - `(call (top size) ,a ,dimno))))) + `(call (top size) ,a ,dimno)))) ;; replace `end` for the closest ref expression, so doesn't go inside nested refs (define (replace-end ex a n tuples last) @@ -153,11 +149,7 @@ ;; except for rest arg (define (method-lambda-expr argl body rett) (let ((argl (map arg-name argl)) - (body (if (and (pair? body) (eq? (car body) 'block)) - (if (null? (cdr body)) - `(block (null)) - body) - `(block ,body)))) + (body (blockify body))) `(lambda ,argl () (scope-block ,(if (equal? rett '(core Any)) @@ -359,7 +351,7 @@ types))) (call (core svec) ,@temps))) ,body ,isstaged)))) - (if (symbol? name) + (if (or (symbol? name) (globalref? name)) `(block (method ,name) ,mdef (unnecessary ,name)) ;; return the function mdef))))) @@ -367,9 +359,9 @@ (define (scopenest names vals expr) (if (null? names) expr - `(let (block - ,(scopenest (cdr names) (cdr vals) expr)) - (= ,(car names) ,(car vals))))) + `(let (= ,(car names) ,(car vals)) + (block + ,(scopenest (cdr names) (cdr vals) expr))))) (define empty-vector-any '(call (core AnyVector) 0)) @@ -381,9 +373,7 @@ (if (nospecialize-meta? a) (caddr a) a)) kargl)) (pargl (cdr argl)) ;; positional args - (body (if (and (pair? body) (eq? (car body) 'block)) - body - `(block ,body))) + (body (blockify body)) (ftype (decl-type (car pargl))) ;; 1-element list of vararg argument, or empty if none (vararg (let ((l (if (null? pargl) '() (last pargl)))) @@ -489,10 +479,10 @@ (block ;; ii = i*2 - 1 (= ,ii (call (top -) (call (top *) ,i 2) 1)) - (= ,elt (call (core arrayref) ,kw ,ii)) + (= ,elt (call (core arrayref) true ,kw ,ii)) ,(foldl (lambda (kn else) (let* ((k (car kn)) - (rval0 `(call (core arrayref) ,kw + (rval0 `(call (core arrayref) true ,kw (call (top +) ,ii 1))) ;; note: if the "declared" type of a KW arg ;; includes something from keyword-sparams @@ -532,7 +522,7 @@ `(foreigncall 'jl_array_ptr_1d_push (core Void) (call (core svec) Any Any) 'ccall 2 ,rkw (tuple ,elt - (call (core arrayref) ,kw + (call (core arrayref) true ,kw (call (top +) ,ii 1))))) (map (lambda (k temp) (cons (if (decl? k) `(,(car k) ,temp ,(caddr k)) temp) @@ -999,6 +989,32 @@ (loop (cadr ex) (append! (reverse (cddr ex)) vars)) `(where ,ex ,.(reverse! vars))))) +(define (lower-destructuring-args argl) + (define (check-lhs a) + (if (expr-contains-p (lambda (e) (or (decl? e) (assignment? e) (kwarg? e))) + a) + (error (string "invalid argument destructuring syntax \"" (deparse a) "\"")) + a)) + (define (transform-arg a) + (cond ((and (pair? a) (eq? (car a) 'tuple)) + (let ((a2 (gensy))) + (cons a2 `(local (= ,(check-lhs a) ,a2))))) + ((or (and (decl? a) (length= a 3)) (kwarg? a)) + (let ((x (transform-arg (cadr a)))) + (cons `(,(car a) ,(car x) ,(caddr a)) (cdr x)))) + ((vararg? a) + (let ((x (transform-arg (cadr a)))) + (cons `(... ,(car x)) (cdr x)))) + (else (cons a #f)))) + (let loop ((argl argl) + (newa '()) + (stmts '())) + (if (null? argl) + (cons (reverse newa) (reverse stmts)) + (let ((a (transform-arg (car argl)))) + (loop (cdr argl) (cons (car a) newa) + (if (cdr a) (cons (cdr a) stmts) stmts)))))) + (define (expand-function-def- e) (let* ((name (cadr e)) (where (if (and (pair? name) (eq? (car name) 'where)) @@ -1011,7 +1027,7 @@ (dcl (and (pair? name) (eq? (car name) '|::|))) (rett (if dcl (caddr name) '(core Any))) (name (if dcl (cadr name) name))) - (cond ((and (length= e 2) (symbol? name)) + (cond ((and (length= e 2) (or (symbol? name) (globalref? name))) (if (or (eq? name 'true) (eq? name 'false)) (error (string "invalid function name \"" name "\""))) `(method ,name)) @@ -1046,6 +1062,9 @@ (farg (if (decl? name) (adj-decl name) `(|::| |#self#| (call (core Typeof) ,name)))) + (argl-stmts (lower-destructuring-args argl)) + (argl (car argl-stmts)) + (body (insert-after-meta body (cdr argl-stmts))) (argl (fix-arglist (arglist-unshift argl farg) (and (not (any kwarg? argl)) (not (and (pair? argl) @@ -1101,9 +1120,23 @@ `(call ,name ,@argl)) ,body))))) +(define (let-binds e) + (if (and (pair? (cadr e)) + (eq? (car (cadr e)) 'block)) + (cdr (cadr e)) + (list (cadr e)))) + (define (expand-let e) - (let ((ex (cadr e)) - (binds (cddr e))) + (if (length= e 2) + (begin (deprecation-message (string "The form `Expr(:let, ex)` is deprecated. " + "Use `Expr(:let, Expr(:block), ex)` instead." #\newline)) + (return (expand-let `(let (block) ,(cadr e)))))) + (if (length> e 3) + (begin (deprecation-message (string "The form `Expr(:let, ex, binds...)` is deprecated. " + "Use `Expr(:let, Expr(:block, binds...), ex)` instead." #\newline)) + (return (expand-let `(let (block ,@(cddr e)) ,(cadr e)))))) + (let ((ex (caddr e)) + (binds (let-binds e))) (expand-forms (if (null? binds) @@ -1125,7 +1158,7 @@ ;; some kind of assignment (cond ((eventually-call? (cadar binds)) - ;; f()=c + ;; f() = c (let ((asgn (butlast (expand-forms (car binds)))) (name (assigned-name (cadar binds)))) (if (not (symbol? name)) @@ -1133,15 +1166,16 @@ (loop (cdr binds) `(scope-block (block - (local-def ,name) + ,(if (expr-contains-eq name (caddar binds)) + `(local ,name) ;; might need a Box for recursive functions + `(local-def ,name)) ,asgn ,blk))))) ((or (symbol? (cadar binds)) (decl? (cadar binds))) (let ((vname (decl-var (cadar binds)))) (loop (cdr binds) - (if (contains (lambda (x) (eq? x vname)) - (caddar binds)) + (if (expr-contains-eq vname (caddar binds)) (let ((tmp (make-ssavalue))) `(scope-block (block (= ,tmp ,(caddar binds)) @@ -1320,7 +1354,7 @@ (if (null? params) (error (string "empty type parameter list in \"" (deparse `(= (curly ,name) ,type-ex)) "\""))) `(block - (const ,name) + (const-if-global ,name) ,(expand-forms `(= ,name (where ,type-ex ,@params))))) (expand-forms @@ -1397,24 +1431,32 @@ (unnecessary (tuple ,@(reverse elts)))) (let ((L (car lhss)) (R (car rhss))) - (if (and (symbol-like? L) - (or (not (pair? R)) (quoted? R) (equal? R '(null))) - ;; overwrite var immediately if it doesn't occur elsewhere - (not (contains (lambda (e) (eq-sym? e L)) (cdr rhss))) - (not (contains (lambda (e) (eq-sym? e R)) assigned))) - (loop (cdr lhss) - (cons L assigned) - (cdr rhss) - (cons (make-assignment L R) stmts) - after - (cons R elts)) - (let ((temp (if (eventually-call? L) (gensy) (make-ssavalue)))) - (loop (cdr lhss) - (cons L assigned) - (cdr rhss) - (cons (make-assignment temp R) stmts) - (cons (make-assignment L temp) after) - (cons temp elts)))))))) + (cond ((and (symbol-like? L) + (or (not (pair? R)) (quoted? R) (equal? R '(null))) + ;; overwrite var immediately if it doesn't occur elsewhere + (not (contains (lambda (e) (eq-sym? e L)) (cdr rhss))) + (not (contains (lambda (e) (eq-sym? e R)) assigned))) + (loop (cdr lhss) + (cons L assigned) + (cdr rhss) + (cons (make-assignment L R) stmts) + after + (cons R elts))) + ((vararg? R) + (let ((temp (make-ssavalue))) + `(block ,@(reverse stmts) + ,(make-assignment temp (cadr R)) + ,@(reverse after) + (= (tuple ,@lhss) ,temp) + (unnecessary (tuple ,@(reverse elts) (... ,temp)))))) + (else + (let ((temp (if (eventually-call? L) (gensy) (make-ssavalue)))) + (loop (cdr lhss) + (cons L assigned) + (cdr rhss) + (cons (make-assignment temp R) stmts) + (cons (make-assignment L temp) after) + (cons temp elts))))))))) ;; convert (lhss...) = x to tuple indexing (define (lower-tuple-assignment lhss x) @@ -1669,23 +1711,32 @@ (if ,g ,g ,(loop (cdr tail))))))))))) +;; If true, this will warn on all `for` loop variables that overwrite outer variables. +;; If false, this will try to warn only for uses of the last value after the loop. +(define *warn-all-loop-vars* #f) + (define (expand-for while lhs X body) ;; (for (= lhs X) body) - (let ((coll (make-ssavalue)) - (state (gensy))) - `(scope-block - (block (= ,coll ,(expand-forms X)) - (= ,state (call (top start) ,coll)) - ,(expand-forms - `(,while - (call (top !) (call (top done) ,coll ,state)) - (scope-block - (block - ;; NOTE: enable this to force loop-local var - #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) - ,(lower-tuple-assignment (list lhs state) - `(call (top next) ,coll ,state)) - ,body)))))))) + (let* ((coll (make-ssavalue)) + (state (gensy)) + (outer? (and (pair? lhs) (eq? (car lhs) 'outer))) + (lhs (if outer? (cadr lhs) lhs))) + `(block (= ,coll ,(expand-forms X)) + (= ,state (call (top start) ,coll)) + ;; TODO avoid `local declared twice` error from this + ;;,@(if outer? `((local ,lhs)) '()) + ,(expand-forms + `(,while + (call (top !) (call (top done) ,coll ,state)) + (block + ;; NOTE: enable this to force loop-local var + #;,@(map (lambda (v) `(local ,v)) (lhs-vars lhs)) + ,@(if (and (not outer?) (or *depwarn* *deperror*)) + (map (lambda (v) `(warn-if-existing ,v)) (lhs-vars lhs)) + '()) + ,(lower-tuple-assignment (list lhs state) + `(call (top next) ,coll ,state)) + ,body)))))) ;; convert an operator parsed as (op a b) to (call op a b) (define (syntactic-op-to-call e) @@ -1707,6 +1758,7 @@ (if (and (null? splat) (length= expr 3) (eq? (car expr) 'call) (eq? (caddr expr) argname) + (not (dotop? (cadr expr))) (not (expr-contains-eq argname (cadr expr)))) (cadr expr) ;; eta reduce `x->f(x)` => `f` `(-> ,argname (block ,@splat ,expr))))) @@ -1765,7 +1817,7 @@ `(fuse _ ,(cdadr (cadr arg))) oldarg)) fargs args))) - (let ,fbody ,@(reverse (fuse-lets fargs args '())))))) + (let (block ,@(reverse (fuse-lets fargs args '()))) ,fbody)))) (define (dot-to-fuse e) ; convert e == (. f (tuple args)) to (fuse f args) (define (make-fuse f args) ; check for nested (fuse f args) exprs and combine (define (split-kwargs args) ; return (cons keyword-args positional-args) extracted from args @@ -1853,8 +1905,8 @@ (define (expand-where body var) (let* ((bounds (analyze-typevar var)) (v (car bounds))) - `(let (call (core UnionAll) ,v ,body) - (= ,v ,(bounds-to-TypeVar bounds))))) + `(let (= ,v ,(bounds-to-TypeVar bounds)) + (call (core UnionAll) ,v ,body)))) (define (expand-wheres body vars) (if (null? vars) @@ -2012,13 +2064,20 @@ ;; multiple assignment (let ((lhss (cdr lhs)) (x (caddr e))) + (define (sides-match? l r) + ;; l and r either have equal lengths, or r has a trailing ... + (cond ((null? l) (null? r)) + ((null? r) #f) + ((vararg? (car r)) (null? (cdr r))) + (else (sides-match? (cdr l) (cdr r))))) (if (and (pair? x) (pair? lhss) (eq? (car x) 'tuple) - (length= lhss (length (cdr x)))) + (sides-match? lhss (cdr x))) ;; (a, b, ...) = (x, y, ...) (expand-forms (tuple-to-assignments lhss x)) ;; (a, b, ...) = other - (let* ((xx (if (and (symbol? x) (not (memq x lhss))) + (let* ((xx (if (or (and (symbol? x) (not (memq x lhss))) + (ssavalue? x)) x (make-ssavalue))) (ini (if (eq? x xx) '() `((= ,xx ,(expand-forms x))))) (st (gensy))) @@ -2223,18 +2282,16 @@ 'while (lambda (e) - `(scope-block - (break-block loop-exit - (_while ,(expand-forms (cadr e)) - (break-block loop-cont - ,(expand-forms (caddr e))))))) + `(break-block loop-exit + (_while ,(expand-forms (cadr e)) + (break-block loop-cont + (scope-block ,(blockify (expand-forms (caddr e)))))))) 'inner-while (lambda (e) - `(scope-block - (_while ,(expand-forms (cadr e)) - (break-block loop-cont - ,(expand-forms (caddr e)))))) + `(_while ,(expand-forms (cadr e)) + (break-block loop-cont + (scope-block ,(blockify (expand-forms (caddr e))))))) 'break (lambda (e) @@ -2353,7 +2410,7 @@ ,.(apply append rows))) `(call (top typed_vcat) ,t ,@a))))) - '|'| (lambda (e) `(call ctranspose ,(expand-forms (cadr e)))) + '|'| (lambda (e) `(call adjoint ,(expand-forms (cadr e)))) '|.'| (lambda (e) `(call transpose ,(expand-forms (cadr e)))) 'generator @@ -2526,7 +2583,7 @@ ((eq? (car e) 'break-block) (unbound-vars (caddr e) bound tab)) ((eq? (car e) 'with-static-parameters) (unbound-vars (cadr e) bound tab)) (else (for-each (lambda (x) (unbound-vars x bound tab)) - (cdr e)) + (cdr e)) tab))) ;; local variable identification and renaming, derived from: @@ -2538,10 +2595,14 @@ (define (resolve-scopes- e env outerglobals implicitglobals lam renames newlam) (cond ((symbol? e) (let ((r (assq e renames))) (if r (cdr r) e))) ;; return the renaming for e, or e - ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global))) e) + ((or (not (pair? e)) (quoted? e) (memq (car e) '(toplevel global symbolicgoto symboliclabel))) e) ((eq? (car e) 'local) '(null)) ;; remove local decls ((eq? (car e) 'local-def) '(null)) ;; remove local decls ((eq? (car e) 'implicit-global) '(null)) ;; remove implicit-global decls + ((eq? (car e) 'warn-if-existing) + (if (or (memq (cadr e) outerglobals) (memq (cadr e) implicitglobals)) + `(warn-loop-var ,(cadr e)) + '(null))) ((eq? (car e) 'lambda) (let* ((lv (lam:vars e)) (env (append lv env)) @@ -2582,6 +2643,9 @@ vars)))) (need-rename (need-rename? vars)) (need-rename-def (need-rename? vars-def)) + (deprecated-loop-vars + (filter (lambda (v) (and (memq v env) (not (memq v locals-declared)))) + (delete-duplicates (find-decls 'warn-if-existing blok)))) ;; new gensym names for conflicting variables (renamed (map named-gensy need-rename)) (renamed-def (map named-gensy need-rename-def)) @@ -2611,12 +2675,19 @@ (if lam ;; update in-place the list of local variables in lam (set-car! (cddr lam) (append real-new-vars real-new-vars-def (caddr lam)))) - (insert-after-meta ;; return the new, expanded scope-block - (if (and (pair? body) (eq? (car body) 'block)) - body - `(block ,body)) - (append! (map (lambda (v) `(local ,v)) real-new-vars) - (map (lambda (v) `(local-def ,v)) real-new-vars-def))))) + (let* ((warnings (map (lambda (v) `(warn-loop-var ,v)) deprecated-loop-vars)) + (body (if *warn-all-loop-vars* + body + (if (and (pair? body) (eq? (car body) 'block)) + (append body warnings) + `(block ,body ,@warnings))))) + (insert-after-meta ;; return the new, expanded scope-block + (blockify body) + (append! (map (lambda (v) `(local ,v)) real-new-vars) + (map (lambda (v) `(local-def ,v)) real-new-vars-def) + (if *warn-all-loop-vars* + (map (lambda (v) `(warn-loop-var ,v)) deprecated-loop-vars) + '())))))) ((eq? (car e) 'module) (error "module expression not at top level")) ((eq? (car e) 'break-block) @@ -3082,7 +3153,7 @@ f(x) = yt(x) ((atom? e) e) (else (case (car e) - ((quote top core globalref outerref line break inert module toplevel null meta) e) + ((quote top core globalref outerref line break inert module toplevel null meta warn-loop-var) e) ((=) (let ((var (cadr e)) (rhs (cl-convert (caddr e) fname lam namemap toplevel interp))) @@ -3099,11 +3170,12 @@ f(x) = yt(x) (if (vinfo:never-undef vi) '(null) `(newvar ,(cadr e)))))) - ((const) + ((const) e) + ((const-if-global) (if (or (assq (cadr e) (car (lam:vinfo lam))) (assq (cadr e) (cadr (lam:vinfo lam)))) '(null) - e)) + `(const ,(cadr e)))) ((isdefined) ;; convert isdefined expr to function for closure converted variables (let* ((sym (cadr e)) (vi (and (symbol? sym) (assq sym (car (lam:vinfo lam))))) @@ -3203,13 +3275,18 @@ f(x) = yt(x) (capt-vars (diff all-capt-vars capt-sp)) ; remove capt-sp from capt-vars (find-locals-in-method-sig (lambda (methdef) (expr-find-all - (lambda (e) (and (pair? e) (eq? (car e) 'outerref) - (let ((s (cadr e))) + (lambda (e) (and (or (symbol? e) (and (pair? e) (eq? (car e) 'outerref))) + (let ((s (if (symbol? e) e (cadr e)))) (and (symbol? s) (not (eq? name s)) (not (memq s capt-sp)) - (or ;(local? s) ; TODO: error for local variables - (memq s (lam:sp lam))))))) + (if (and (local? s) (length> (lam:args lam) 0)) + ; error for local variables except in toplevel thunks + (error (string "local variable " s + " cannot be used in closure declaration")) + #t) + ; allow captured variables + (memq s (lam:sp lam)))))) (caddr methdef) (lambda (e) (cadr e))))) (sig-locals (simple-sort @@ -3327,6 +3404,11 @@ f(x) = yt(x) (else (for-each linearize (cdr e)))) e) +(define (deprecation-message msg) + (if *deperror* + (error msg) + (io.write *stderr* msg))) + ;; this pass behaves like an interpreter on the given code. ;; to perform stateful operations, it calls `emit` to record that something ;; needs to be done. in value position, it returns an expression computing @@ -3339,6 +3421,7 @@ f(x) = yt(x) (first-line #t) (current-loc #f) (rett #f) + (deprecated-loop-vars (table)) (arg-map #f) ;; map arguments to new names if they are assigned (label-counter 0) ;; counter for generating label addresses (label-map (table)) ;; maps label names to generated addresses @@ -3432,6 +3515,11 @@ f(x) = yt(x) (eq? (cadr e) '_)))) (syntax-deprecation #f (string "_ as an rvalue" (linenode-string current-loc)) "")) + (if (and (not *warn-all-loop-vars*) (has? deprecated-loop-vars e)) + (begin (deprecation-message + (string "Use of final value of loop variable \"" e "\"" (linenode-string current-loc) " " + "is deprecated. In the future the variable will be local to the loop instead." #\newline)) + (del! deprecated-loop-vars e))) (cond (tail (emit-return e1)) (value e1) ((or (eq? e1 'true) (eq? e1 'false)) #f) @@ -3441,19 +3529,33 @@ f(x) = yt(x) (else #f))) (case (car e) ((call new foreigncall) - (let* ((args (if (eq? (car e) 'foreigncall) - ;; NOTE: 2nd to 5th arguments of ccall must be left in place - ;; the 1st should be compiled if an atom. - (append (list) - (cond (atom? (cadr e) (compile-args (list (cadr e)) break-labels linearize-args)) - (else (cadr e))) - (list-head (cddr e) 4) - (compile-args (list-tail e 6) break-labels linearize-args)) - (compile-args (cdr e) break-labels linearize-args))) + (let* ((args + (cond ((eq? (car e) 'foreigncall) + (for-each (lambda (a) + (if (and (length= a 2) (eq? (car a) '&)) + (deprecation-message + (string "Syntax \"&argument\"" (linenode-string current-loc) + " is deprecated. Remove the \"&\" and use a \"Ref\" argument " + "type instead." #\newline)))) + (list-tail e 6)) + ;; NOTE: 2nd to 5th arguments of ccall must be left in place + ;; the 1st should be compiled if an atom. + (append (if (atom? (cadr e)) + (compile-args (list (cadr e)) break-labels linearize-args) + (list (cadr e))) + (list-head (cddr e) 4) + (compile-args (list-tail e 6) break-labels linearize-args))) + ;; TODO: evaluate first argument to cglobal some other way + ((and (length> e 2) + (or (eq? (cadr e) 'cglobal) + (equal? (cadr e) '(outerref cglobal)))) + (list* (cadr e) (caddr e) + (compile-args (cdddr e) break-labels linearize-args))) + (else + (compile-args (cdr e) break-labels linearize-args)))) (callex (cons (car e) args))) (cond (tail (emit-return callex)) (value callex) - ((eq? (car e) 'new) #f) (else (emit callex))))) ((=) (let* ((rhs (compile (caddr e) break-labels #t #f)) @@ -3461,6 +3563,8 @@ f(x) = yt(x) (lhs (if (and arg-map (symbol? lhs)) (get arg-map lhs lhs) lhs))) + (if (and (not *warn-all-loop-vars*) (has? deprecated-loop-vars lhs)) + (del! deprecated-loop-vars lhs)) (if value (let ((rr (if (or (atom? rhs) (ssavalue? rhs) (eq? (car rhs) 'null)) rhs (make-ssavalue)))) @@ -3645,31 +3749,51 @@ f(x) = yt(x) ((local-def) #f) ((local) #f) ((implicit-global) #f) - ((const) (emit e)) + ((const) + (if (or (assq (cadr e) (car (lam:vinfo lam))) + (assq (cadr e) (cadr (lam:vinfo lam)))) + (begin + (syntax-deprecation #f (string "`const` declaration on local variable" (linenode-string current-loc)) + "") + '(null)) + (emit e))) ((isdefined) (if tail (emit-return e) e)) + ((warn-loop-var) + (if (or *warn-all-loop-vars* + (not (or (assq (cadr e) (car (lam:vinfo lam))) + (assq (cadr e) (cadr (lam:vinfo lam)))))) + (deprecation-message + (string "Loop variable \"" (cadr e) "\"" (linenode-string current-loc) " " + "overwrites a variable in an enclosing scope. " + "In the future the variable will be local to the loop instead." #\newline)) + (put! deprecated-loop-vars (cadr e) #t)) + '(null)) + ((boundscheck) (if tail (emit-return e) e)) ;; top level expressions returning values ((abstract_type primitive_type struct_type thunk toplevel module) - (case (car e) - ((abstract_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (cadddr e) break-labels #t #f))) - (emit `(abstract_type ,(cadr e) ,para ,supe)))) - ((primitive_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (list-ref e 4) break-labels #t #f))) - (emit `(primitive_type ,(cadr e) ,para ,(cadddr e) ,supe)))) - ((struct_type) - (let* ((para (compile (caddr e) break-labels #t #f)) - (supe (compile (list-ref e 4) break-labels #t #f)) - ;; struct_type has an unconventional evaluation rule that - ;; needs to do work around the evaluation of the field types, - ;; so the field type expressions need to be kept in place as - ;; much as possible. (part of issue #21923) - (ftys (compile (list-ref e 5) break-labels #t #f #f))) - (emit `(struct_type ,(cadr e) ,para ,(cadddr e) ,supe ,ftys ,@(list-tail e 6))))) - (else - (emit e))) + (with-bindings + ((*very-linear-mode* #f)) ;; type defs use nonstandard evaluation order + (case (car e) + ((abstract_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (cadddr e) break-labels #t #f))) + (emit `(abstract_type ,(cadr e) ,para ,supe)))) + ((primitive_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (list-ref e 4) break-labels #t #f))) + (emit `(primitive_type ,(cadr e) ,para ,(cadddr e) ,supe)))) + ((struct_type) + (let* ((para (compile (caddr e) break-labels #t #f)) + (supe (compile (list-ref e 4) break-labels #t #f)) + ;; struct_type has an unconventional evaluation rule that + ;; needs to do work around the evaluation of the field types, + ;; so the field type expressions need to be kept in place as + ;; much as possible. (part of issue #21923) + (ftys (compile (list-ref e 5) break-labels #t #f #f))) + (emit `(struct_type ,(cadr e) ,para ,(cadddr e) ,supe ,ftys ,@(list-tail e 6))))) + (else + (emit e)))) (if tail (emit-return '(null))) '(null)) @@ -3734,12 +3858,23 @@ f(x) = yt(x) '() arg-map)) body)))) -;; find newvar nodes that are unnecessary because (1) the variable is not +(define (for-each-isdefined f e) + (cond ((or (atom? e) (quoted? e)) #f) + ((and (pair? e) (eq? (car e) 'isdefined)) + (f (cadr e))) + (else + (for-each (lambda (x) (for-each-isdefined f x)) + (cdr e))))) + +;; Find newvar nodes that are unnecessary because (1) the variable is not ;; captured, and (2) the variable is assigned before any branches. -;; this is used to remove newvar nodes that are not needed for re-initializing -;; variables to undefined (see issue #11065). it doesn't look for variable -;; *uses*, because any variables used-before-def that also pass this test -;; are *always* used undefined, and therefore don't need to be *re*-initialized. +;; This is used to remove newvar nodes that are not needed for re-initializing +;; variables to undefined (see issue #11065). +;; It doesn't look for variable *uses*, because any variables used-before-def +;; that also pass this test are *always* used undefined, and therefore don't need +;; to be *re*-initialized. +;; The one exception to that is `@isdefined`, which can observe an undefined +;; variable without throwing an error. (define (definitely-initialized-vars stmts vi) (let ((vars (table)) (di (table))) @@ -3748,6 +3883,8 @@ f(x) = yt(x) di (begin (let ((e (car stmts))) + (for-each-isdefined (lambda (x) (if (has? vars x) (del! vars x))) + e) (cond ((and (pair? e) (eq? (car e) 'newvar)) (let ((vinf (var-info-for (cadr e) vi))) (if (and vinf (not (vinfo:capt vinf))) diff --git a/src/julia.h b/src/julia.h index 83a77a9126810..23e00ceb9c1af 100644 --- a/src/julia.h +++ b/src/julia.h @@ -63,6 +63,7 @@ typedef struct _jl_taggedvalue_t jl_taggedvalue_t; #include "atomics.h" #include "tls.h" #include "julia_threads.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -550,7 +551,6 @@ extern JL_DLLEXPORT jl_datatype_t *jl_float64_type; extern JL_DLLEXPORT jl_datatype_t *jl_floatingpoint_type; extern JL_DLLEXPORT jl_datatype_t *jl_number_type; extern JL_DLLEXPORT jl_datatype_t *jl_void_type; -extern JL_DLLEXPORT jl_unionall_t *jl_complex_type; extern JL_DLLEXPORT jl_datatype_t *jl_signed_type; extern JL_DLLEXPORT jl_datatype_t *jl_voidpointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_pointer_type; @@ -649,6 +649,7 @@ JL_DLLEXPORT jl_value_t *jl_gc_alloc_1w(void); JL_DLLEXPORT jl_value_t *jl_gc_alloc_2w(void); JL_DLLEXPORT jl_value_t *jl_gc_alloc_3w(void); JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz); +JL_DLLEXPORT void jl_gc_use(jl_value_t *a); JL_DLLEXPORT void jl_clear_malloc_data(void); @@ -998,7 +999,7 @@ JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b); jl_value_t *jl_unwrap_unionall(jl_value_t *v); jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u); -#if defined(NDEBUG) && defined(JL_NDEBUG) +#if defined(JL_NDEBUG) STATIC_INLINE int jl_is_leaf_type_(jl_value_t *v) { return jl_is_datatype(v) && ((jl_datatype_t*)v)->isleaftype; @@ -1181,6 +1182,7 @@ JL_DLLEXPORT void jl_set_nth_field(jl_value_t *v, size_t i, JL_DLLEXPORT int jl_field_isdefined(jl_value_t *v, size_t i); JL_DLLEXPORT jl_value_t *jl_get_field(jl_value_t *o, const char *fld); JL_DLLEXPORT jl_value_t *jl_value_ptr(jl_value_t *a); +JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al); // arrays JL_DLLEXPORT jl_array_t *jl_new_array(jl_value_t *atype, jl_value_t *dims); @@ -1672,6 +1674,7 @@ JL_DLLEXPORT void jl_(void *jl_value); // NOTE: This struct needs to be kept in sync with JLOptions type in base/options.jl typedef struct { int8_t quiet; + int8_t banner; const char *julia_home; const char *julia_bin; const char *eval; @@ -1692,6 +1695,7 @@ typedef struct { int8_t debug_level; int8_t check_bounds; int8_t depwarn; + int8_t warn_overwrite; int8_t can_inline; int8_t polly; int8_t fast_math; @@ -1752,6 +1756,9 @@ JL_DLLEXPORT int jl_generating_output(void); #define JL_OPTIONS_DEPWARN_ON 1 #define JL_OPTIONS_DEPWARN_ERROR 2 +#define JL_OPTIONS_WARN_OVERWRITE_OFF 0 +#define JL_OPTIONS_WARN_OVERWRITE_ON 1 + #define JL_OPTIONS_POLLY_ON 1 #define JL_OPTIONS_POLLY_OFF 0 @@ -1799,7 +1806,15 @@ typedef struct { // codegen interface ---------------------------------------------------------- typedef struct { - // to disable a hook: set to NULL or nothing + int cached; // can the compiler use/populate the compilation cache? + + int track_allocations; // can we track allocations? + int code_coverage; // can we measure coverage? + int static_alloc; // is the compiler allowed to allocate statically? + int prefer_specsig; // are specialized function signatures preferred? + + + // hooks // module setup: prepare a module for code emission (data layout, DWARF version, ...) // parameters: LLVMModuleRef as Ptr{Void} @@ -1815,20 +1830,6 @@ typedef struct { // parameters: LLVMBasicBlockRef as Ptr{Void}, LLVMValueRef as Ptr{Void} // return value: none jl_value_t *raise_exception; -} jl_cghooks_t; - -typedef struct { - int cached; // can the compiler use/populate the compilation cache? - - // language features (C-style integer booleans) - int runtime; // can we call into the runtime? - int exceptions; // are exceptions supported (requires runtime)? - int track_allocations; // can we track allocations (don't if disallowed)? - int code_coverage; // can we measure coverage (don't if disallowed)? - int static_alloc; // is the compiler allowed to allocate statically? - int dynamic_alloc; // is the compiler allowed to allocate dynamically (requires runtime)? - - jl_cghooks_t hooks; } jl_cgparams_t; extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; diff --git a/src/julia_assert.h b/src/julia_assert.h new file mode 100644 index 0000000000000..6cf89d0e470a5 --- /dev/null +++ b/src/julia_assert.h @@ -0,0 +1,27 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +// Include this file instead of `assert.h` directly. +// This is necessary because LLVM sometimes has bugs that cause runtime assertion if +// the `NDEBUG` setting is different from the one used to compile LLVM. +// For C++ files, we set `NDEBUG` to match what LLVM expects and use `JL_NDEBUG` to +// enable assertions in julia code. After including this file, the definition of `assert` will +// match the setting given in `JL_NDEBUG` and `NDEBUG` will remain unchanged. +// +// Files that need `assert` should include this file after all other includes. +// All files should also check `JL_NDEBUG` instead of `NDEBUG`. + +#ifdef NDEBUG +# ifndef JL_NDEBUG +# undef NDEBUG +# include +// Set NDEBUG back so that we can include another LLVM header right after +# define NDEBUG +# else +# include +# endif +#else +# ifdef JL_NDEBUG +# undef JL_NDEBUG +# endif +# include +#endif diff --git a/src/julia_internal.h b/src/julia_internal.h index 0970ae02fa611..94297906a56b3 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -475,6 +475,7 @@ jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val); jl_svec_t *jl_outer_unionall_vars(jl_value_t *u); int jl_count_union_components(jl_value_t *v); jl_value_t *jl_nth_union_component(jl_value_t *v, int i); +int jl_find_union_component(jl_value_t *haystack, jl_value_t *needle, unsigned *nth); jl_datatype_t *jl_new_uninitialized_datatype(void); void jl_precompute_memoized_dt(jl_datatype_t *dt); jl_datatype_t *jl_wrap_Type(jl_value_t *t); // x -> Type{x} @@ -995,11 +996,15 @@ extern jl_sym_t *meta_sym; extern jl_sym_t *list_sym; extern jl_sym_t *inert_sym; extern jl_sym_t *static_parameter_sym; extern jl_sym_t *polly_sym; extern jl_sym_t *inline_sym; extern jl_sym_t *propagate_inbounds_sym; -extern jl_sym_t *isdefined_sym; extern jl_sym_t *nospecialize_sym; +extern jl_sym_t *isdefined_sym; +extern jl_sym_t *nospecialize_sym; +extern jl_sym_t *boundscheck_sym; void jl_register_fptrs(uint64_t sysimage_base, const char *base, const int32_t *offsets, jl_method_instance_t **linfos, size_t n); +extern arraylist_t partial_inst; + #ifdef __cplusplus } #endif diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index be872d7f056db..d559de07de7eb 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -16,8 +16,6 @@ #include #include -#include "fix_llvm_assert.h" - #include "codegen_shared.h" #include "julia.h" #include "julia_internal.h" @@ -25,6 +23,8 @@ #include #include +#include "julia_assert.h" + using namespace llvm; extern std::pair tbaa_make_child(const char *name, MDNode *parent=nullptr, bool isConstant=false); @@ -491,6 +491,7 @@ void AllocOpt::replaceUsesWith(Instruction *orig_inst, Instruction *new_inst, } else if (auto call = dyn_cast(user)) { if (ptr_from_objref && ptr_from_objref == call->getCalledFunction()) { + new_i = new PtrToIntInst(new_i, T_size, "", call); call->replaceAllUsesWith(new_i); call->eraseFromParent(); return; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index dc77551d192c3..6c62c73809bc6 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -21,6 +21,7 @@ #include "codegen_shared.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #define DEBUG_TYPE "late_lower_gcroot" @@ -320,6 +321,7 @@ struct LateLowerGCFrame: public FunctionPass { MDNode *tbaa_tag; Function *ptls_getter; Function *gc_flush_func; + Function *gc_use_func; Function *pointer_from_objref_func; Function *alloc_obj_func; Function *pool_alloc_func; @@ -728,8 +730,16 @@ State LateLowerGCFrame::LocalScan(Function &F) { Instruction &I = *it; if (CallInst *CI = dyn_cast(&I)) { if (isa(CI)) { - // Intrinsics are never GC uses/defs - continue; + // Most intrinsics are not gc uses/defs, however some have + // memory operands and could thus be GC uses. To be conservative, + // we only skip processing for those that we know we emit often + // and cannot possibly be GC uses. + IntrinsicInst *II = cast(CI); + if (isa(CI) || + II->getIntrinsicID() == Intrinsic::lifetime_start || + II->getIntrinsicID() == Intrinsic::lifetime_end) { + continue; + } } MaybeNoteDef(S, BBS, CI, BBS.Safepoints); NoteOperandUses(S, BBS, I, BBS.UpExposedUses); @@ -743,9 +753,14 @@ State LateLowerGCFrame::LocalScan(Function &F) { if (CI->canReturnTwice()) { S.ReturnsTwice.push_back(CI); } + if (isa(CI)) { + // Intrinsics are never safepoints. + continue; + } if (auto callee = CI->getCalledFunction()) { // Known functions emitted in codegen that are not safepoints - if (callee == pointer_from_objref_func || callee->getName() == "memcmp") { + if (callee == pointer_from_objref_func || callee == gc_use_func || + callee->getName() == "memcmp") { continue; } } @@ -1137,13 +1152,13 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { } CallingConv::ID CC = CI->getCallingConv(); auto callee = CI->getCalledValue(); - if (gc_flush_func != nullptr && callee == gc_flush_func) { + if ((gc_flush_func != nullptr && callee == gc_flush_func) || + (gc_use_func != nullptr && callee == gc_use_func)) { /* No replacement */ } else if (pointer_from_objref_func != nullptr && callee == pointer_from_objref_func) { - auto *ASCI = new AddrSpaceCastInst(CI->getOperand(0), - CI->getType(), "", CI); - ASCI->takeName(CI); - CI->replaceAllUsesWith(ASCI); + auto *ptr = new PtrToIntInst(CI->getOperand(0), CI->getType(), "", CI); + ptr->takeName(CI); + CI->replaceAllUsesWith(ptr); } else if (alloc_obj_func && callee == alloc_obj_func) { assert(CI->getNumArgOperands() == 3); auto sz = (size_t)cast(CI->getArgOperand(1))->getZExtValue(); @@ -1405,6 +1420,7 @@ static void addRetNoAlias(Function *F) bool LateLowerGCFrame::doInitialization(Module &M) { ptls_getter = M.getFunction("jl_get_ptls_states"); gc_flush_func = M.getFunction("julia.gcroot_flush"); + gc_use_func = M.getFunction("julia.gc_use"); pointer_from_objref_func = M.getFunction("julia.pointer_from_objref"); auto &ctx = M.getContext(); T_size = M.getDataLayout().getIntPtrType(ctx); diff --git a/src/llvm-lower-handlers.cpp b/src/llvm-lower-handlers.cpp index 4ef6f15a02aa5..5caee2b1998bb 100644 --- a/src/llvm-lower-handlers.cpp +++ b/src/llvm-lower-handlers.cpp @@ -1,6 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license -#include #include #include #include @@ -14,6 +13,7 @@ #include "llvm-version.h" #include "julia.h" +#include "julia_assert.h" #define DEBUG_TYPE "lower_handlers" #undef DEBUG diff --git a/src/llvm-muladd.cpp b/src/llvm-muladd.cpp index 267efa0749dc7..a8b635f9c34e8 100644 --- a/src/llvm-muladd.cpp +++ b/src/llvm-muladd.cpp @@ -13,9 +13,9 @@ #include #include #include -#include "fix_llvm_assert.h" #include "julia.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/llvm-ptls.cpp b/src/llvm-ptls.cpp index 391b3096b08e9..5c0e5a8296a71 100644 --- a/src/llvm-ptls.cpp +++ b/src/llvm-ptls.cpp @@ -21,10 +21,10 @@ # include # include #endif -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" using namespace llvm; diff --git a/src/llvm-simdloop.cpp b/src/llvm-simdloop.cpp index 72dbe86ee6beb..87f4d953b5fcd 100644 --- a/src/llvm-simdloop.cpp +++ b/src/llvm-simdloop.cpp @@ -14,10 +14,11 @@ #include #include #include -#include "fix_llvm_assert.h" #include +#include "julia_assert.h" + namespace llvm { // simd loop diff --git a/src/llvm-version.h b/src/llvm-version.h index f164da886287e..fa1dbcf433e21 100644 --- a/src/llvm-version.h +++ b/src/llvm-version.h @@ -1,7 +1,7 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license #include -#include "fix_llvm_assert.h" +#include "julia_assert.h" // The LLVM version used, JL_LLVM_VERSION, is represented as a 5-digit integer // of the form ABBCC, where A is the major version, B is minor, and C is patch. diff --git a/src/locks.h b/src/locks.h index 3212eb3377534..21f41f9d72d07 100644 --- a/src/locks.h +++ b/src/locks.h @@ -3,6 +3,8 @@ #ifndef JL_LOCKS_H #define JL_LOCKS_H +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/src/macroexpand.scm b/src/macroexpand.scm index ba99b97daaffc..a7535920d24b0 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -92,8 +92,8 @@ (cons 'varlist (typevar-names vars))) ;; let - (pattern-lambda (let ex . binds) - (let loop ((binds binds) + (pattern-lambda (let binds ex) + (let loop ((binds (let-binds __)) (vars '())) (if (null? binds) (cons 'varlist vars) @@ -360,24 +360,26 @@ ((let) (let* ((newenv (new-expansion-env-for e env)) - (body (resolve-expansion-vars- (cadr e) newenv m parent-scope inarg))) - `(let ,body - ,@(map - (lambda (bind) - (if (assignment? bind) - (make-assignment - ;; expand binds in old env with dummy RHS - (cadr (resolve-expansion-vars- (make-assignment (cadr bind) 0) - newenv m parent-scope inarg)) - ;; expand initial values in old env - (resolve-expansion-vars- (caddr bind) env m parent-scope inarg)) - bind)) - (cddr e))))) + (body (resolve-expansion-vars- (caddr e) newenv m parent-scope inarg)) + (binds (let-binds e))) + `(let (block + ,@(map + (lambda (bind) + (if (assignment? bind) + (make-assignment + ;; expand binds in old env with dummy RHS + (cadr (resolve-expansion-vars- (make-assignment (cadr bind) 0) + newenv m parent-scope inarg)) + ;; expand initial values in old env + (resolve-expansion-vars- (caddr bind) env m parent-scope inarg)) + bind)) + binds)) + ,body))) ((hygienic-scope) ; TODO: move this lowering to resolve-scopes, instead of reimplementing it here badly (let ((parent-scope (cons (list env m) parent-scope)) (body (cadr e)) (m (caddr e))) - (resolve-expansion-vars-with-new-env body env m parent-scope inarg))) + (resolve-expansion-vars-with-new-env body env m parent-scope inarg #t))) ;; todo: trycatch (else @@ -407,10 +409,30 @@ (and (eq? (car e) '=) (length= e 3) (eventually-call? (cadr e)))))) +;; count hygienic / escape pairs +;; and fold together a list resulting from applying the function to +;; any block at the same hygienic scope +(define (resume-on-escape lam e nblocks) + (if (or (not (pair? e)) (quoted? e)) + '() + (cond ((memq (car e) '(lambda module toplevel)) + '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape lam (cadr e) (+ nblocks 1))) + ((eq? (car e) 'escape) + (if (= nblocks 0) + (lam (cadr e)) + (resume-on-escape lam (cadr e) (- nblocks 1)))) + (else + (foldl (lambda (a l) (append! l (resume-on-escape lam a nblocks))) + '() + (cdr e)))))) + (define (find-declared-vars-in-expansion e decl (outer #t)) (cond ((or (not (pair? e)) (quoted? e)) '()) ((eq? (car e) 'escape) '()) - ((eq? (car e) 'hygienic-scope) '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape (lambda (e) (find-declared-vars-in-expansion e decl outer)) (cadr e) 0)) ((eq? (car e) decl) (map decl-var* (cdr e))) ((and (not outer) (function-def? e)) '()) (else @@ -421,7 +443,8 @@ (define (find-assigned-vars-in-expansion e (outer #t)) (cond ((or (not (pair? e)) (quoted? e)) '()) ((eq? (car e) 'escape) '()) - ((eq? (car e) 'hygienic-scope) '()) + ((eq? (car e) 'hygienic-scope) + (resume-on-escape (lambda (e) (find-assigned-vars-in-expansion e outer)) (cadr e) 0)) ((and (not outer) (function-def? e)) ;; pick up only function name (let ((fname (cond ((eq? (car e) '=) (decl-var* (cadr e))) @@ -453,39 +476,36 @@ ;; and wrap globals in (globalref module var) for macro's home module (resolve-expansion-vars-with-new-env e '() m '() #f #t)) -(define (find-symbolic-labels e) - (let ((defs (table)) - (refs (table))) - (find-symbolic-label-defs e defs) - (find-symbolic-label-refs e refs) - (table.foldl - (lambda (label v labels) - (if (has? refs label) - (cons label labels) - labels)) - '() defs))) - -(define (rename-symbolic-labels- e relabel) +(define (rename-symbolic-labels- e relabels parent-scope) (cond ((or (not (pair? e)) (quoted? e)) e) - ((eq? (car e) 'symbolicgoto) - (let ((newlabel (assq (cadr e) relabel))) - (if newlabel `(symbolicgoto ,(cdr newlabel)) e))) - ((eq? (car e) 'symboliclabel) - (let ((newlabel (assq (cadr e) relabel))) - (if newlabel `(symboliclabel ,(cdr newlabel)) e))) - (else (map (lambda (x) (rename-symbolic-labels- x relabel)) e)))) + ((eq? (car e) 'hygienic-scope) + (let ((parent-scope (list relabels parent-scope)) + (body (cadr e)) + (m (caddr e))) + `(hygienic-scope ,(rename-symbolic-labels- (cadr e) (table) parent-scope) ,m))) + ((and (eq? (car e) 'escape) (not (null? parent-scope))) + `(escape ,(apply rename-symbolic-labels- (cadr e) parent-scope))) + ((or (eq? (car e) 'symbolicgoto) (eq? (car e) 'symboliclabel)) + (let* ((s (cadr e)) + (havelabel (if (or (null? parent-scope) (not (symbol? s))) s (get relabels s #f))) + (newlabel (if havelabel havelabel (named-gensy s)))) + (if (not havelabel) (put! relabels s newlabel)) + `(,(car e) ,newlabel))) + (else + (cons (car e) + (map (lambda (x) (rename-symbolic-labels- x relabels parent-scope)) + (cdr e)))))) (define (rename-symbolic-labels e) - (let* ((labels (find-symbolic-labels e)) - (relabel (pair-with-gensyms labels))) - (rename-symbolic-labels- e relabel))) + (rename-symbolic-labels- e (table) '())) ;; macro expander entry point (define (julia-expand-macros e (max-depth -1)) (julia-expand-macroscopes - (julia-expand-macros- '() e max-depth))) + (rename-symbolic-labels + (julia-expand-macros- '() e max-depth)))) (define (julia-expand-macros- m e max-depth) (cond ((= max-depth 0) e) @@ -501,11 +521,12 @@ (error (string "macro \"" (cadr e) "\" not defined"))) (if (and (pair? form) (eq? (car form) 'error)) (error (cadr form))) - (let ((form (car form)) ;; form is the expression returned from expand-macros - (modu (cdr form))) ;; modu is the macro's def module - `(hygienic-scope - ,(julia-expand-macros- (cons modu m) (rename-symbolic-labels form) (- max-depth 1)) - ,modu)))) + (let* ((modu (cdr form)) ;; modu is the macro's def module + (form (car form)) ;; form is the expression returned from expand-macros + (form (julia-expand-macros- (cons modu m) form (- max-depth 1)))) + (if (and (pair? form) (eq? (car form) 'escape)) + (cadr form) ; immediately fold away (hygienic-scope (escape ...)) + `(hygienic-scope ,form ,modu))))) ((eq? (car e) 'module) e) ((eq? (car e) 'escape) (let ((m (if (null? m) m (cdr m)))) diff --git a/src/method.c b/src/method.c index 6f43a7b95d499..45d39cdbec1f8 100644 --- a/src/method.c +++ b/src/method.c @@ -7,9 +7,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -170,6 +170,8 @@ static void jl_code_info_set_ast(jl_code_info_t *li, jl_expr_t *ast) jl_gc_wb(li, li->slotflags); li->ssavaluetypes = jl_box_long(nssavalue); jl_gc_wb(li, li->ssavaluetypes); + // Flags that need to be copied to slotflags + const uint8_t vinfo_mask = 16 | 32 | 64; int i; for (i = 0; i < nslots; i++) { jl_value_t *vi = jl_array_ptr_ref(vis, i); @@ -187,7 +189,7 @@ static void jl_code_info_set_ast(jl_code_info_t *li, jl_expr_t *ast) } } jl_array_ptr_set(li->slotnames, i, name); - jl_array_uint8_set(li->slotflags, i, jl_unbox_long(jl_array_ptr_ref(vi, 2))); + jl_array_uint8_set(li->slotflags, i, vinfo_mask & jl_unbox_long(jl_array_ptr_ref(vi, 2))); } } diff --git a/src/module.c b/src/module.c index c1ca45cc9a9de..2f3ded651bb08 100644 --- a/src/module.c +++ b/src/module.c @@ -3,9 +3,9 @@ /* modules and top-level bindings */ -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/precompile.c b/src/precompile.c index 9830bb41744b5..db4c2f57792f8 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -6,10 +6,10 @@ */ #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/rtutils.c b/src/rtutils.c index 75ef9e7b565d3..1d0e5417163b5 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -305,6 +305,10 @@ JL_DLLEXPORT jl_value_t *jl_value_ptr(jl_value_t *a) { return a; } +JL_DLLEXPORT void jl_gc_use(jl_value_t *a) +{ + (void)a; +} // parsing -------------------------------------------------------------------- @@ -883,9 +887,12 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_static_show_x(out, *(jl_value_t**)fld_ptr, depth); } else { - n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, - (jl_datatype_t*)jl_field_type(vt, i), - depth); + jl_datatype_t *ft = (jl_datatype_t*)jl_field_type(vt, i); + if (jl_is_uniontype(ft)) { + uint8_t sel = ((uint8_t*)fld_ptr)[jl_field_size(vt, i) - 1]; + ft = (jl_datatype_t*)jl_nth_union_component((jl_value_t*)ft, sel); + } + n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, ft, depth); } if (istuple && tlen == 1) n += jl_printf(out, ","); @@ -1016,26 +1023,6 @@ void jl_depwarn(const char *msg, jl_value_t *sym) JL_GC_POP(); } -JL_DLLEXPORT void jl_depwarn_partial_indexing(size_t n) -{ - static jl_value_t *depwarn_func = NULL; - if (!depwarn_func && jl_base_module) { - depwarn_func = jl_get_global(jl_base_module, jl_symbol("partial_linear_indexing_warning")); - } - if (!depwarn_func) { - jl_safe_printf("WARNING: Partial linear indexing is deprecated. Use " - "`reshape(A, Val(%zd))` to make the dimensionality of the array match " - "the number of indices\n", n); - return; - } - jl_value_t **depwarn_args; - JL_GC_PUSHARGS(depwarn_args, 2); - depwarn_args[0] = depwarn_func; - depwarn_args[1] = jl_box_long(n); - jl_apply(depwarn_args, 2); - JL_GC_POP(); -} - #ifdef __cplusplus } #endif diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index 142ecd5b4620e..49a0640943bca 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -5,9 +5,10 @@ #include #include #include -#include "fix_llvm_assert.h" #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" + using namespace llvm; // --- library symbol lookup --- diff --git a/src/safepoint.c b/src/safepoint.c index 43dad38f4fad4..1d58c26cad9bd 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -9,6 +9,7 @@ #define MAP_ANONYMOUS MAP_ANON #endif #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/signals-mach.c b/src/signals-mach.c index 8534eb6e8bafe..ef38b25b01ff3 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -15,6 +15,8 @@ #include #endif +#include "julia_assert.h" + static void attach_exception_port(thread_port_t thread, int segv_only); #ifdef JULIA_ENABLE_THREADING diff --git a/src/signals-unix.c b/src/signals-unix.c index 2b69f43d6a972..ed3e52542fb30 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -33,6 +33,8 @@ // Should also be enough for parallel GC when we have it =) #define sig_stack_size (8 * 1024 * 1024) +#include "julia_assert.h" + static bt_context_t *jl_to_bt_context(void *sigctx) { #ifdef __APPLE__ diff --git a/src/simplevector.c b/src/simplevector.c index 69247924a6ee9..c3d0dde54e43f 100644 --- a/src/simplevector.c +++ b/src/simplevector.c @@ -3,9 +3,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" JL_DLLEXPORT jl_svec_t *jl_svec(size_t n, ...) { diff --git a/src/stackwalk.c b/src/stackwalk.c index 1f1cd043c4296..26e400e03d8a4 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -8,6 +8,7 @@ #include "julia.h" #include "julia_internal.h" #include "threading.h" +#include "julia_assert.h" // define `jl_unw_get` as a macro, since (like setjmp) // returning from the callee function will invalidate the context diff --git a/src/staticdata.c b/src/staticdata.c index 2a2b00c94cd73..0d431e05ba992 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -5,7 +5,6 @@ */ #include #include -#include #include "julia.h" #include "julia_internal.h" @@ -20,6 +19,7 @@ #else #define RUNNING_ON_VALGRIND 0 #endif +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -589,7 +589,8 @@ static void jl_write_values(jl_serializer_state *s) // make some header modifications in-place jl_array_t *newa = (jl_array_t*)&s->s->buf[reloc_offset]; size_t alen = jl_array_len(ar); - size_t tot = alen * ar->elsize; + size_t extra = (!ar->flags.ptrarray && jl_is_uniontype(jl_tparam0(jl_typeof(ar)))) ? alen : 0; + size_t tot = alen * ar->elsize + extra; if (newa->flags.ndims == 1) newa->maxsize = alen; newa->offset = 0; @@ -820,7 +821,7 @@ static uintptr_t get_reloc_for_item(uintptr_t reloc_item, size_t reloc_offset) } else { // just write the item reloc_id directly -#ifndef NDEBUG +#ifndef JL_NDEBUG assert(reloc_offset == 0 && "offsets for relocations to builtin objects should be precomposed in the reloc_item"); size_t offset = (reloc_item & (((uintptr_t)1 << RELOC_TAG_OFFSET) - 1)); switch (tag) { @@ -1340,7 +1341,6 @@ JL_DLLEXPORT void jl_save_system_image(const char *fname) extern int jl_boot_file_loaded; extern void jl_get_builtins(void); extern void jl_get_builtin_hooks(void); -extern void jl_get_system_hooks(void); extern void jl_gc_set_permalloc_region(void *start, void *end); // Takes in a path of the form "usr/lib/julia/sys.so" (jl_restore_system_image should be passed the same string) @@ -1496,9 +1496,6 @@ static void jl_restore_system_image_from_stream(ios_t *f) jl_get_builtins(); jl_get_builtin_hooks(); - if (jl_base_module) { - jl_get_system_hooks(); - } jl_boot_file_loaded = 1; jl_init_box_caches(); @@ -1515,7 +1512,7 @@ static void jl_restore_system_image_from_stream(ios_t *f) // TODO: need to enforce that the alignment of the buffer is suitable for vectors JL_DLLEXPORT void jl_restore_system_image(const char *fname) { -#ifndef NDEBUG +#ifndef JL_NDEBUG char *dot = fname ? (char*)strrchr(fname, '.') : NULL; int is_ji = (dot && !strcmp(dot, ".ji")); assert((is_ji || jl_sysimg_handle) && "System image file not preloaded"); diff --git a/src/subtype.c b/src/subtype.c index 78a39b6ca481d..eb94e1463b50d 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -21,12 +21,12 @@ */ #include #include -#include #ifdef _OS_WINDOWS_ #include #endif #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -420,9 +420,10 @@ static int subtype_ufirst(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) { if (vb != NULL && param) { - if (param == 2 && e->invdepth > vb->depth0) + // saturate counters at 2; we don't need values bigger than that + if (param == 2 && e->invdepth > vb->depth0 && vb->occurs_inv < 2) vb->occurs_inv++; - else + else if (vb->occurs_cov < 2) vb->occurs_cov++; } } diff --git a/src/support/libsupport.h b/src/support/libsupport.h index f5267baf28e86..880c8560cd23c 100644 --- a/src/support/libsupport.h +++ b/src/support/libsupport.h @@ -7,7 +7,6 @@ #include #include -#include #include "dtypes.h" #include "utils.h" #include "utf8.h" diff --git a/src/symbol.c b/src/symbol.c index 7be4136fa70d1..da17a426dbc82 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -7,9 +7,9 @@ #include #include #include -#include #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/sys.c b/src/sys.c index ea2a6b1e69a93..ce23b4359fb83 100644 --- a/src/sys.c +++ b/src/sys.c @@ -4,16 +4,16 @@ sys.c I/O and operating system utility functions */ -#include "julia.h" -#include "julia_internal.h" #include #include #include -#include #include #include #include +#include "julia.h" +#include "julia_internal.h" + #ifdef _OS_WINDOWS_ #include #else @@ -56,6 +56,8 @@ #include #endif +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif @@ -114,6 +116,7 @@ JL_DLLEXPORT int32_t jl_nb_available(ios_t *s) JL_DLLEXPORT int jl_sizeof_uv_fs_t(void) { return sizeof(uv_fs_t); } JL_DLLEXPORT void jl_uv_fs_req_cleanup(uv_fs_t *req) { uv_fs_req_cleanup(req); } JL_DLLEXPORT char *jl_uv_fs_t_ptr(uv_fs_t *req) { return (char*)req->ptr; } +JL_DLLEXPORT char *jl_uv_fs_t_path(uv_fs_t *req) { return (char*)req->path; } JL_DLLEXPORT int jl_uv_fs_result(uv_fs_t *f) { return f->result; } // --- stat --- diff --git a/src/task.c b/src/task.c index a9ed17c1c28f1..742f325790a50 100644 --- a/src/task.c +++ b/src/task.c @@ -18,13 +18,13 @@ #include #include -#include #include #include #include #include "julia.h" #include "julia_internal.h" #include "threading.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { diff --git a/src/threading.c b/src/threading.c index cce22976db1ad..8dbe9066b9224 100644 --- a/src/threading.c +++ b/src/threading.c @@ -21,6 +21,7 @@ #include "julia.h" #include "julia_internal.h" +#include "julia_assert.h" // Ref https://www.uclibc.org/docs/tls.pdf // For variant 1 JL_ELF_TLS_INIT_SIZE is the size of the thread control block (TCB) diff --git a/src/timing.h b/src/timing.h index dfd676d66e4bd..2eab5e325e312 100644 --- a/src/timing.h +++ b/src/timing.h @@ -7,6 +7,8 @@ #define JL_TIMING(owner) #else +#include "julia_assert.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/src/toplevel.c b/src/toplevel.c index d113857aff4b0..9a56455b7aed8 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #if defined(_OS_WINDOWS_) @@ -19,6 +18,7 @@ #include "julia.h" #include "julia_internal.h" #include "uv.h" +#include "julia_assert.h" #ifdef __cplusplus extern "C" { @@ -120,7 +120,6 @@ static void jl_module_load_time_initialize(jl_module_t *m) } } -extern void jl_get_system_hooks(void); jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex) { jl_ptls_t ptls = jl_get_ptls_states(); diff --git a/src/typemap.c b/src/typemap.c index f86c1c59aede8..62f5e26c8b8bc 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -2,12 +2,12 @@ #include #include -#include #include "julia.h" #include "julia_internal.h" #ifndef _OS_WINDOWS_ #include #endif +#include "julia_assert.h" #define MAX_METHLIST_COUNT 12 // this can strongly affect the sysimg size and speed! #define INIT_CACHE_SIZE 8 // must be a power-of-two diff --git a/test/TestHelpers.jl b/test/TestHelpers.jl index 4f99773aa2a7a..84f8edc7fc7cb 100644 --- a/test/TestHelpers.jl +++ b/test/TestHelpers.jl @@ -24,8 +24,8 @@ function open_fake_pty() error("Unable to create a fake PTY in Windows") end - const O_RDWR = Base.Filesystem.JL_O_RDWR - const O_NOCTTY = Base.Filesystem.JL_O_NOCTTY + O_RDWR = Base.Filesystem.JL_O_RDWR + O_NOCTTY = Base.Filesystem.JL_O_NOCTTY fdm = ccall(:posix_openpt, Cint, (Cint,), O_RDWR|O_NOCTTY) fdm == -1 && error("Failed to open PTY master") @@ -53,7 +53,7 @@ function with_fake_pty(f) end end -function challenge_prompt(code::AbstractString, challenges; timeout::Integer=10, debug::Bool=true) +function challenge_prompt(code::Expr, challenges; timeout::Integer=10, debug::Bool=true) output_file = tempname() wrapped_code = """ result = let @@ -129,8 +129,8 @@ OffsetVector{T,AA<:AbstractArray} = OffsetArray{T,1,AA} OffsetArray(A::AbstractArray{T,N}, offsets::NTuple{N,Int}) where {T,N} = OffsetArray{T,N,typeof(A)}(A, offsets) OffsetArray(A::AbstractArray{T,N}, offsets::Vararg{Int,N}) where {T,N} = OffsetArray(A, offsets) -(::Type{OffsetArray{T,N}})(inds::Indices{N}) where {T,N} = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) -(::Type{OffsetArray{T}})(inds::Indices{N}) where {T,N} = OffsetArray{T,N}(inds) +OffsetArray{T,N}(inds::Indices{N}) where {T,N} = OffsetArray{T,N,Array{T,N}}(Array{T,N}(map(length, inds)), map(indsoffset, inds)) +OffsetArray{T}(inds::Indices{N}) where {T,N} = OffsetArray{T,N}(inds) Base.IndexStyle(::Type{T}) where {T<:OffsetArray} = Base.IndexStyle(parenttype(T)) parenttype(::Type{OffsetArray{T,N,AA}}) where {T,N,AA} = AA diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 5a0fc7ff2204e..394d698e6c9ff 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -16,10 +16,10 @@ A = rand(5,4,3) @test checkbounds(Bool, A, 2, 2, 2, 1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 2) == false @test checkbounds(Bool, A, 1, 1) == true # partial linear indexing (PLI) - # @test checkbounds(Bool, A, 1, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation - # @test checkbounds(Bool, A, 5, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation - @test checkbounds(Bool, A, 1, 13) == false # PLI - # @test checkbounds(Bool, A, 6, 12) == false # PLI TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1, 12) == false + @test checkbounds(Bool, A, 5, 12) == false + @test checkbounds(Bool, A, 1, 13) == false + @test checkbounds(Bool, A, 6, 12) == false end @testset "single CartesianIndex" begin @@ -32,15 +32,15 @@ end @test checkbounds(Bool, A, CartesianIndex((5, 5, 3))) == false @test checkbounds(Bool, A, CartesianIndex((5, 4, 4))) == false @test checkbounds(Bool, A, CartesianIndex((1,))) == true - # @test checkbounds(Bool, A, CartesianIndex((60,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((60,))) == false @test checkbounds(Bool, A, CartesianIndex((61,))) == false @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 1,))) == true @test checkbounds(Bool, A, CartesianIndex((2, 2, 2, 2,))) == false @test checkbounds(Bool, A, CartesianIndex((1, 1,))) == true - # @test checkbounds(Bool, A, CartesianIndex((1, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation - # @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((1, 12,))) == false + @test checkbounds(Bool, A, CartesianIndex((5, 12,))) == false @test checkbounds(Bool, A, CartesianIndex((1, 13,))) == false - # @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, CartesianIndex((6, 12,))) == false end @testset "mix of CartesianIndex and Int" begin @@ -67,9 +67,9 @@ end @test checkbounds(Bool, A, 2, 2, 2, 1:1) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, 1:2) == false @test checkbounds(Bool, A, 1:5, 1:4) == true - # @test checkbounds(Bool, A, 1:5, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1:5, 1:12) == false @test checkbounds(Bool, A, 1:5, 1:13) == false - # @test checkbounds(Bool, A, 1:6, 1:12) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, 1:6, 1:12) == false end @testset "logical" begin @@ -81,9 +81,9 @@ end @test checkbounds(Bool, A, trues(61)) == false @test checkbounds(Bool, A, 2, 2, 2, trues(1)) == true # extra indices @test checkbounds(Bool, A, 2, 2, 2, trues(2)) == false - # @test checkbounds(Bool, A, trues(5), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, trues(5), trues(12)) == false @test checkbounds(Bool, A, trues(5), trues(13)) == false - # @test checkbounds(Bool, A, trues(6), trues(12)) == false # TODO: Re-enable after partial linear indexing deprecation + @test checkbounds(Bool, A, trues(6), trues(12)) == false @test checkbounds(Bool, A, trues(5, 4, 3)) == true @test checkbounds(Bool, A, trues(5, 4, 2)) == false @test checkbounds(Bool, A, trues(5, 12)) == false @@ -145,11 +145,6 @@ end @test sub2ind((0:3,3:5), i-1, j+2) == k @test ind2sub((0:3,3:5), k) == (i-1, j+2) end - @testset "Delete when partial linear indexing is deprecated (#14770)" begin - @test sub2ind((4,3), 7) == 7 - @test sub2ind((1:4,1:3), 7) == 7 - @test sub2ind((0:3,3:5), 7) == 8 - end end @testset "3-dimensional" begin @@ -377,8 +372,8 @@ function test_vector_indexing(::Type{T}, shape, ::Type{TestAbstractArray}) where @test B[vec(idxs)] == A[vec(idxs)] == vec(idxs) @test B[:] == A[:] == collect(1:N) @test B[1:end] == A[1:end] == collect(1:N) - # @test B[:,:] == A[:,:] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation - # @test B[1:end,1:end] == A[1:end,1:end] == reshape(1:N, shape[1], prod(shape[2:end])) # TODO: Re-enable after partial linear indexing deprecation + @test B[:,:] == A[:,:] == B[:,:,1] == A[:,:,1] + B[1:end,1:end] == A[1:end,1:end] == B[1:end,1:end,1] == A[1:end,1:end,1] @testset "Test with containers that aren't Int[]" begin @test B[[]] == A[[]] == [] @@ -395,7 +390,7 @@ function test_vector_indexing(::Type{T}, shape, ::Type{TestAbstractArray}) where @testset "test removing dimensions with 0-d arrays" begin idx0 = reshape([rand(1:size(A, 1))]) @test B[idx0, idx2] == A[idx0, idx2] == reshape(A[idx0[], vec(idx2)], 4, 5) == reshape(B[idx0[], vec(idx2)], 4, 5) - # @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) # TODO: Re-enable after partial linear indexing deprecation + @test B[reshape([end]), reshape([end])] == A[reshape([end]), reshape([end])] == reshape([A[end,end]]) == reshape([B[end,end]]) end mask = bitrand(shape) @@ -840,3 +835,16 @@ end @testset "checkbounds_indices method ambiguities #20989" begin @test Base.checkbounds_indices(Bool, (1:1,), ([CartesianIndex(1)],)) end + +# keys, values, pairs +for A in (rand(2), rand(2,3)) + local A + for (i, v) in pairs(A) + @test A[i] == v + end + @test collect(values(A)) == collect(A) +end + +# nextind +@test nextind(zeros(4), 2) == 3 +@test nextind(zeros(2,3), CartesianIndex(2,1)) == CartesianIndex(1, 2) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 9c1b8d71b148c..9b7d010721d32 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -65,20 +65,28 @@ end ## Other ways of accessing functions # Test that non-ambiguous cases work -io = IOBuffer() -@test precompile(ambig, (Int, Int)) == true -cfunction(ambig, Int, (Int, Int)) -@test length(code_lowered(ambig, (Int, Int))) == 1 -@test length(code_typed(ambig, (Int, Int))) == 1 -code_llvm(io, ambig, (Int, Int)) -code_native(io, ambig, (Int, Int)) +let io = IOBuffer() + @test precompile(ambig, (Int, Int)) == true + cf = cfunction(ambig, Int, Tuple{Int, Int}) + @test ccall(cf, Int, (Int, Int), 1, 2) == 4 + @test length(code_lowered(ambig, (Int, Int))) == 1 + @test length(code_typed(ambig, (Int, Int))) == 1 + code_llvm(io, ambig, (Int, Int)) + code_native(io, ambig, (Int, Int)) +end # Test that ambiguous cases fail appropriately -@test precompile(ambig, (UInt8, Int)) == false -cfunction(ambig, Int, (UInt8, Int)) # test for a crash (doesn't throw an error) -@test_throws ErrorException which(ambig, (UInt8, Int)) -@test_throws ErrorException code_llvm(io, ambig, (UInt8, Int)) -@test_throws ErrorException code_native(io, ambig, (UInt8, Int)) +let io = IOBuffer() + @test precompile(ambig, (UInt8, Int)) == false + cf = cfunction(ambig, Int, Tuple{UInt8, Int}) # test for a crash (doesn't throw an error) + @test_throws MethodError ccall(cf, Int, (UInt8, Int), 1, 2) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + which(ambig, (UInt8, Int))) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + code_llvm(io, ambig, (UInt8, Int))) + @test_throws(ErrorException("no unique matching method found for the specified argument types"), + code_native(io, ambig, (UInt8, Int))) +end # Method overwriting doesn't destroy ambiguities @test_throws MethodError ambig(2, 0x03) diff --git a/test/arrayops.jl b/test/arrayops.jl index 9055e55ba49df..3848aa14e61cc 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -6,7 +6,7 @@ using TestHelpers.OAs @testset "basics" begin @test length([1, 2, 3]) == 3 - @test countnz([1, 2, 3]) == 3 + @test count(!iszero, [1, 2, 3]) == 3 let a = ones(4), b = a+a, c = a-a @test b[1] === 2. && b[2] === 2. && b[3] === 2. && b[4] === 2. @@ -42,7 +42,6 @@ using TestHelpers.OAs @test isequal([1,2,5] .<< [1,2,5], [2,8,160]) @test isequal([10,20,50] .>> [1,2,5], [5,5,1]) - a = ones(2,2) a[1,1] = 1 a[1,2] = 2 @@ -90,6 +89,13 @@ using TestHelpers.OAs @test ndims(a) == 5 @test a[2,1,2,2,1] == b[14] @test a[2,2,2,2,2] == b[end] + + # issue #23107 + a = [1,2,3] + @test typeof(a)(a) !== a + @test Array(a) !== a + @test Array{eltype(a)}(a) !== a + @test Vector(a) !== a end @testset "reshaping SubArrays" begin a = collect(reshape(1:5, 1, 5)) @@ -479,12 +485,12 @@ end @testset "findmin findmax indmin indmax" begin @test indmax([10,12,9,11]) == 2 @test indmin([10,12,9,11]) == 3 - @test findmin([NaN,3.2,1.8]) == (1.8,3) - @test findmax([NaN,3.2,1.8]) == (3.2,2) - @test findmin([NaN,3.2,1.8,NaN]) == (1.8,3) - @test findmax([NaN,3.2,1.8,NaN]) == (3.2,2) - @test findmin([3.2,1.8,NaN,2.0]) == (1.8,2) - @test findmax([3.2,1.8,NaN,2.0]) == (3.2,1) + @test findmin([NaN,3.2,1.8]) === (NaN,1) + @test findmax([NaN,3.2,1.8]) === (NaN,1) + @test findmin([NaN,3.2,1.8,NaN]) === (NaN,1) + @test findmax([NaN,3.2,1.8,NaN]) === (NaN,1) + @test findmin([3.2,1.8,NaN,2.0]) === (NaN,3) + @test findmax([3.2,1.8,NaN,2.0]) === (NaN,3) #14085 @test findmax(4:9) == (9,6) @@ -497,7 +503,7 @@ end @test indmin(5:-2:1) == 3 #23094 - @test findmax(Set(["abc"])) === ("abc", 1) + @test_throws MethodError findmax(Set(["abc"])) @test findmin(["abc", "a"]) === ("a", 2) @test_throws MethodError findmax([Set([1]), Set([2])]) @test findmin([0.0, -0.0]) === (-0.0, 2) @@ -632,7 +638,6 @@ end @test_throws MethodError repmat(1, 2, 3) @test_throws MethodError repmat([1, 2], 1, 2, 3) - R = repeat([1, 2]) @test R == [1, 2] R = repeat([1, 2], inner=1) @@ -1154,7 +1159,6 @@ end @test isempty(eoa) end - @testset "deleteat!" begin for idx in Any[1, 2, 5, 9, 10, 1:0, 2:1, 1:1, 2:2, 1:2, 2:4, 9:8, 10:9, 9:9, 10:10, 8:9, 9:10, 6:9, 7:10] @@ -1210,10 +1214,10 @@ end @testset "eachindexvalue" begin A14 = [11 13; 12 14] R = CartesianRange(indices(A14)) - @test [a for (a,b) in enumerate(IndexLinear(), A14)] == [1,2,3,4] - @test [a for (a,b) in enumerate(IndexCartesian(), A14)] == vec(collect(R)) - @test [b for (a,b) in enumerate(IndexLinear(), A14)] == [11,12,13,14] - @test [b for (a,b) in enumerate(IndexCartesian(), A14)] == [11,12,13,14] + @test [a for (a,b) in pairs(IndexLinear(), A14)] == [1,2,3,4] + @test [a for (a,b) in pairs(IndexCartesian(), A14)] == vec(collect(R)) + @test [b for (a,b) in pairs(IndexLinear(), A14)] == [11,12,13,14] + @test [b for (a,b) in pairs(IndexCartesian(), A14)] == [11,12,13,14] end @testset "reverse" begin @@ -1631,7 +1635,6 @@ R = CartesianRange((3,0)) @test @inferred(eachindex(zeros(3),view(zeros(3,3),1:2,1:2),zeros(2,2,2),zeros(2,2))) == CartesianRange((3,2,2)) @test @inferred(eachindex(zeros(3),zeros(2,2),zeros(2,2,2),zeros(2,2))) == 1:8 - @testset "rotates" begin a = [1 0 0; 0 0 0] @test rotr90(a,1) == [0 1; 0 0; 0 0] @@ -1685,12 +1688,12 @@ end d = ones(Complex,6) @test_throws DimensionMismatch transpose!(a,d) @test_throws DimensionMismatch transpose!(d,a) - @test_throws DimensionMismatch ctranspose!(a,d) - @test_throws DimensionMismatch ctranspose!(d,a) + @test_throws DimensionMismatch adjoint!(a,d) + @test_throws DimensionMismatch adjoint!(d,a) @test_throws DimensionMismatch transpose!(b,c) - @test_throws DimensionMismatch ctranspose!(b,c) + @test_throws DimensionMismatch adjoint!(b,c) @test_throws DimensionMismatch transpose!(c,b) - @test_throws DimensionMismatch ctranspose!(c,b) + @test_throws DimensionMismatch adjoint!(c,b) transpose!(b,a) @test b == ones(Complex,5) b = ones(Complex,5) @@ -1698,10 +1701,10 @@ end transpose!(a,b) @test a == ones(Complex,1,5) b = zeros(Complex,5) - ctranspose!(b,a) + adjoint!(b,a) @test b == ones(Complex,5) a = zeros(Complex,1,5) - ctranspose!(a,b) + adjoint!(a,b) @test a == ones(Complex,1,5) end @@ -1740,13 +1743,12 @@ module RetTypeDecl end # range, range ops -A = 1:5 -B = 1.5:5.5 -@test A + B == 2.5:2.0:10.5 +@test (1:5) + (1.5:5.5) == 2.5:2.0:10.5 @testset "slicedim" begin for A in (reshape(collect(1:20), 4, 5), reshape(1:20, 4, 5)) + local A @test slicedim(A, 1, 2) == collect(2:4:20) @test slicedim(A, 2, 2) == collect(5:8) @test_throws ArgumentError slicedim(A,0,1) @@ -1784,9 +1786,11 @@ S = view(A, :, :) @test isequal(B, A) for (a,b) in zip(A, B) + local a,b @test a == b end for (a,s) in zip(A, S) + local a,s @test a == s end @@ -1810,6 +1814,11 @@ s, si = findmax(S) @test a == b == s @test ai == bi == si +for X in (A, B, S) + @test findmin(X) == findmin(Dict(pairs(X))) + @test findmax(X) == findmax(Dict(pairs(X))) +end + fill!(B, 2) @test all(x->x==2, B) @@ -1921,7 +1930,6 @@ let f = OOB_Functor([1,2]) @test_throws BoundsError map(f, [1,2,3,4,5]) end - # issue 15654 @test cumprod([5], 2) == [5] @test cumprod([1 2; 3 4], 3) == [1 2; 3 4] @@ -2020,6 +2028,10 @@ end # module AutoRetType @test isa(hvcat((2,), densearray, densearray), Array) @test isa(cat((1,2), densearray, densearray), Array) end + @test isa([[1,2,3]'; [1,2,3]'], Matrix{Int}) + @test isa([[1,2,3]' [1,2,3]'], RowVector{Int, Vector{Int}}) + @test isa([Any[1.0, 2]'; Any[2.0, 2]'], Matrix{Any}) + @test isa([Any[1.0, 2]' Any[2.0, 2']'], RowVector{Any, Vector{Any}}) # Test that concatenations of heterogeneous Matrix-Vector pairs yield dense matrices @test isa(hcat(densemat, densevec), Array) @test isa(hcat(densevec, densemat), Array) diff --git a/test/bigfloat.jl b/test/bigfloat.jl index 4966c1b7a1c6e..4fa613ddb3718 100644 --- a/test/bigfloat.jl +++ b/test/bigfloat.jl @@ -7,7 +7,7 @@ for T in [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt @test big(2.0)^T(3) == 8 end -for x in (2f0, pi, 7.8, big(e)) +for x in (2f0, pi, 7.8, big(ℯ)) @test big(typeof(x)) == typeof(big(x)) @test big(typeof(complex(x, x))) == typeof(big(complex(x, x))) end diff --git a/test/bigint.jl b/test/bigint.jl index 341d692bf4d2a..180fca5ee4d33 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -282,8 +282,8 @@ ndigits(big(rand(Int)), rand(63:typemax(Int))) ndigits(big(rand(Int)), big(2)^rand(2:999)) for x in big.([-20:20; rand(Int)]) - for b in -1:1 - @test_throws DomainError ndigits(x, b) + for _base in -1:1 + @test_throws DomainError ndigits(x, _base) end end @@ -344,6 +344,7 @@ end # respect 0-padding on big(0) for f in (bin, oct, dec, hex) + local f @test f(big(0), 0) == "" end @test base(rand(2:62), big(0), 0) == "" @@ -370,3 +371,13 @@ end @test typeof(tan(a)) == BigFloat @test typeof(cos(a)) == BigFloat @test typeof(sin(a)) == BigFloat + +@test BigInt <: Signed +@test big(1) isa Signed + +let x = big(1) + @test signed(x) === x + @test convert(Signed, x) === x + @test Signed(x) === x + @test_throws MethodError convert(Unsigned, x) # could change in the future +end diff --git a/test/bitarray.jl b/test/bitarray.jl index b1b5655e0d459..fc349bf888ec0 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -190,6 +190,11 @@ timesofar("utils") @test Array(one(BitMatrix(2,2))) == eye(2,2) @test_throws DimensionMismatch one(BitMatrix(2,3)) end + + # constructors should copy + a = trues(3) + @test BitArray(a) !== a + @test BitArray{1}(a) !== a end timesofar("constructors") @@ -471,20 +476,20 @@ timesofar("constructors") @check_bit_operation setindex!(b1, true, t1) BitMatrix t1 = bitrand(n1, n2) - b2 = bitrand(countnz(t1)) + b2 = bitrand(count(t1)) @check_bit_operation setindex!(b1, b2, t1) BitMatrix m1 = rand(1:n1) m2 = rand(1:n2) t1 = bitrand(n1) - b2 = bitrand(countnz(t1), m2) + b2 = bitrand(count(t1), m2) k2 = randperm(m2) @check_bit_operation setindex!(b1, b2, t1, 1:m2) BitMatrix @check_bit_operation setindex!(b1, b2, t1, n2-m2+1:n2) BitMatrix @check_bit_operation setindex!(b1, b2, t1, k2) BitMatrix t2 = bitrand(n2) - b2 = bitrand(m1, countnz(t2)) + b2 = bitrand(m1, count(t2)) k1 = randperm(m1) @check_bit_operation setindex!(b1, b2, 1:m1, t2) BitMatrix @check_bit_operation setindex!(b1, b2, n1-m1+1:n1, t2) BitMatrix @@ -885,8 +890,8 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, 1.0im, b2) Matrix{Complex128} @check_bit_operation broadcast(^, 0im, b2) Matrix{Complex{Int}} @check_bit_operation broadcast(^, 1im, b2) Matrix{Complex{Int}} - @check_bit_operation broadcast(^, 0x0im, b2) Matrix{Complex{UInt8}} - @check_bit_operation broadcast(^, 0x1im, b2) Matrix{Complex{UInt8}} + @check_bit_operation broadcast(^, 0x0*im, b2) Matrix{Complex{UInt8}} + @check_bit_operation broadcast(^, 0x1*im, b2) Matrix{Complex{UInt8}} end @testset "Matrix/Number" begin @@ -982,7 +987,7 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, b1, 0.0) Matrix{Float64} @check_bit_operation broadcast(^, b1, 1.0) Matrix{Float64} @check_bit_operation broadcast(^, b1, 0.0im) Matrix{Complex128} - @check_bit_operation broadcast(^, b1, 0x0im) Matrix{Complex128} + @check_bit_operation broadcast(^, b1, 0x0*im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, 0im) Matrix{Complex128} @test_throws DomainError broadcast(^, b1, -1) @@ -991,7 +996,7 @@ timesofar("unary arithmetic") @check_bit_operation broadcast(^, b1, 1.0im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, -1im) Matrix{Complex128} @check_bit_operation broadcast(^, b1, 1im) Matrix{Complex128} - @check_bit_operation broadcast(^, b1, 0x1im) Matrix{Complex128} + @check_bit_operation broadcast(^, b1, 0x1*im) Matrix{Complex128} end end @@ -1035,30 +1040,29 @@ timesofar("binary comparison") @test isequal(b1 >>> m, [ falses(m); b1[1:end-m] ]) @test isequal(b1 << -m, b1 >> m) @test isequal(b1 >>> -m, b1 << m) - @test isequal(rol(b1, m), [ b1[m+1:end]; b1[1:m] ]) - @test isequal(ror(b1, m), [ b1[end-m+1:end]; b1[1:end-m] ]) - @test isequal(ror(b1, m), rol(b1, -m)) - @test isequal(rol(b1, m), ror(b1, -m)) + @test isequal(circshift(b1, -m), [ b1[m+1:end]; b1[1:m] ]) + @test isequal(circshift(b1, m), [ b1[end-m+1:end]; b1[1:end-m] ]) + @test isequal(circshift(b1, m), circshift(b1, m - length(b1))) end b = bitrand(v1) i = bitrand(v1) for m = [rand(1:v1), 63, 64, 65, 191, 192, 193, v1-1] j = rand(1:m) - b1 = ror!(i, b, j) - i1 = ror!(b, j) + b1 = circshift!(i, b, j) + i1 = circshift!(b, j) @test b1 == i1 - b2 = rol!(i1, b1, j) - i2 = rol!(b1, j) + b2 = circshift!(i1, b1, -j) + i2 = circshift!(b1, -j) @test b2 == i2 end end timesofar("datamove") -@testset "countnz & find" begin +@testset "count & find" begin for m = 0:v1, b1 in Any[bitrand(m), trues(m), falses(m)] - @check_bit_operation countnz(b1) Int + @check_bit_operation count(b1) Int @check_bit_operation findfirst(b1) Int @@ -1395,9 +1399,6 @@ timesofar("cat") b1 = bitrand(v1) @check_bit_operation diagm(b1) BitMatrix - b1 = bitrand(n1, n2) - @test_throws DimensionMismatch diagm(b1) - b1 = bitrand(n1, n1) @check_bit_operation diag(b1) end diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index df2deea73cb51..35e61536ddb2d 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -27,16 +27,20 @@ function A1_inbounds() end return r end +A1_wrap() = @inbounds return A1_inbounds() if bc_opt == bc_default @test A1() == 1 - @test A1_inbounds() == 0 + @test A1_inbounds() == 1 + @test A1_wrap() == 0 elseif bc_opt == bc_on @test A1() == 1 @test A1_inbounds() == 1 + @test A1_wrap() == 1 else @test A1() == 0 @test A1_inbounds() == 0 + @test A1_wrap() == 0 end # test for boundscheck block eliminated one layer deep, if the called method is inlined @@ -124,25 +128,31 @@ end # elide a throw cb(x) = x > 0 || throw(BoundsError()) -function B1() - y = [1,2,3] +@inline function B1() + y = [1, 2, 3] @inbounds begin @boundscheck cb(0) end return 0 end +B1_wrap() = @inbounds return B1() -if bc_opt == bc_default || bc_opt == bc_off +if bc_opt == bc_default + @test_throws BoundsError B1() + @test B1_wrap() == 0 +elseif bc_opt == bc_off @test B1() == 0 + @test B1_wrap() == 0 else @test_throws BoundsError B1() + @test_throws BoundsError B1_wrap() end # elide a simple branch cond(x) = x > 0 ? x : -x function B2() - y = [1,2,3] + y = [1, 2, 3] @inbounds begin @boundscheck cond(0) end @@ -219,4 +229,14 @@ else @test inbounds_isassigned(Int[], 2) == false end +# Test that @inbounds annotations don't propagate too far for Array; Issue #20469 +struct BadVector20469{T} <: AbstractVector{Int} + data::T +end +Base.size(X::BadVector20469) = size(X.data) +Base.getindex(X::BadVector20469, i::Int) = X.data[i-1] +if bc_opt != bc_off + @test_throws BoundsError BadVector20469([1,2,3])[:] +end + end diff --git a/test/broadcast.jl b/test/broadcast.jl index aa84edb2c700f..dc69b534234cf 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -247,6 +247,11 @@ let x = [1:4;] @test sin.(f17300kw.(x, y=1)) == sin.(f17300kw.(x; y=1)) == sin.(x .+ 1) end +# issue #23236 +let X = [[true,false],[false,true]] + @test [.!x for x in X] == [[false,true],[true,false]] +end + # splice escaping of @. let x = [4, -9, 1, -16] @test [2, 3, 4, 5] == @.(1 + sqrt($sort(abs(x)))) diff --git a/test/cartesian.jl b/test/cartesian.jl index be2605d200143..cf8ed426468c5 100644 --- a/test/cartesian.jl +++ b/test/cartesian.jl @@ -3,3 +3,5 @@ @test Base.Cartesian.exprresolve(:(1 + 3)) == 4 ex = Base.Cartesian.exprresolve(:(if 5 > 4; :x; else :y; end)) @test ex.args[2] == QuoteNode(:x) + +@test Base.Cartesian.lreplace!("val_col", Base.Cartesian.LReplace{String}(:col, "col", 1)) == "val_1" \ No newline at end of file diff --git a/test/ccall.jl b/test/ccall.jl index 560705055554f..f9c5d53a8d2ba 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -82,7 +82,7 @@ let a, ci_ary, x @test x == a + 1 - 2im @test a == 20 + 51im - x = ccall((:cptest_static, libccalltest), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), &a) + x = ccall((:cptest_static, libccalltest), Ptr{Complex{Int}}, (Ref{Complex{Int}},), a) @test unsafe_load(x) == a Libc.free(convert(Ptr{Void}, x)) end @@ -122,7 +122,6 @@ let a a = 2.84 + 5.2im @test_throws MethodError ccall((:cptest, libccalltest), Ptr{Complex{Int}}, (Ptr{Complex{Int}},), a) - @test_throws MethodError ccall((:cptest, libccalltest), Ptr{Complex{Int}}, (Complex{Int},), &a) end @@ -791,57 +790,57 @@ for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), function $fname(s) @assert false end - b = ccall(cfunction($fname1,Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),a) + b = ccall(cfunction($fname1, Ref{$t}, Tuple{Ref{$t}}), Ref{$t}, (Ref{$t},), a) verbose && println("C: ",b) @test b == $v @test b === a @test b === c - b = ccall(cfunction($fname,$t,($t,)),$t,($t,),a) + b = ccall(cfunction($fname, $t, Tuple{$t}), $t, ($t,), a) verbose && println("C: ",b) @test b == $v if ($(t).mutable) @test !(b === c) @test !(b === a) end - b = ccall(cfunction($fname1,$t,(Ref{$t},)),$t,(Ref{$t},),a) + b = ccall(cfunction($fname1, $t, Tuple{Ref{$t}}), $t, (Ref{$t},), a) verbose && println("C: ",b) @test b == $v if ($(t).mutable) @test !(b === c) @test !(b === a) end - b = ccall(cfunction($fname,Ref{$t},($t,)),Ref{$t},($t,),a) + b = ccall(cfunction($fname, Ref{$t}, Tuple{$t}), Ref{$t}, ($t,), a) verbose && println("C: ",b) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - b = ccall(cfunction($fname,Any,(Ref{$t},)),Any,(Ref{$t},),$v) + b = ccall(cfunction($fname, Any, Tuple{Ref{$t}}), Any, (Ref{$t},), $v) verbose && println("C: ",b) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - b = ccall(cfunction($fname,Any,(Ref{Any},)),Any,(Ref{Any},),$v) + b = ccall(cfunction($fname, Any, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) @test b == $v @test b === c if ($(t).mutable) @test !(b === a) end - @test_throws TypeError ccall(cfunction($fname,Ref{AbstractString},(Ref{Any},)),Any,(Ref{Any},),$v) - @test_throws TypeError ccall(cfunction($fname,AbstractString,(Ref{Any},)),Any,(Ref{Any},),$v) + @test_throws TypeError ccall(cfunction($fname, Ref{AbstractString}, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) + @test_throws TypeError ccall(cfunction($fname, AbstractString, Tuple{Ref{Any}}), Any, (Ref{Any},), $v) end end # issue 13031 foo13031(x) = Cint(1) -foo13031p = cfunction(foo13031, Cint, (Ref{Tuple{}},)) +foo13031p = cfunction(foo13031, Cint, Tuple{Ref{Tuple{}}}) ccall(foo13031p, Cint, (Ref{Tuple{}},), ()) foo13031(x,y,z) = z -foo13031p = cfunction(foo13031, Cint, (Ref{Tuple{}},Ref{Tuple{}},Cint)) +foo13031p = cfunction(foo13031, Cint, Tuple{Ref{Tuple{}}, Ref{Tuple{}}, Cint}) ccall(foo13031p, Cint, (Ref{Tuple{}},Ref{Tuple{}},Cint), (), (), 8) # issue 17219 @@ -993,7 +992,7 @@ if Sys.ARCH === :x86_64 T = NTuple{4, VecElement{s}} @eval function rt_sse(a1::$T, a2::$T, a3::$T, a4::$T) return ccall( - cfunction(foo_ams, $T, ($T, $T, $T, $T)), + cfunction(foo_ams, $T, Tuple{$T, $T, $T, $T}), $T, ($T, $T, $T, $T), a1, a2, a3, a4) @@ -1281,7 +1280,7 @@ end evalf_callback_19805(ci::callinfos_19805{FUNC_FT}) where {FUNC_FT} = ci.f(0.5)::Float64 evalf_callback_c_19805(ci::callinfos_19805{FUNC_FT}) where {FUNC_FT} = cfunction( - evalf_callback_19805, Float64, (callinfos_19805{FUNC_FT},)) + evalf_callback_19805, Float64, Tuple{callinfos_19805{FUNC_FT}}) @test_throws(ErrorException("ccall: the type of argument 1 doesn't correspond to a C type"), evalf_callback_c_19805( callinfos_19805(sin) )) diff --git a/test/channels.jl b/test/channels.jl index fb6e5234edd2f..be01bf389c690 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -49,8 +49,8 @@ c=Channel(32) results=[] @sync begin for i in 1:20 - @async for i in c - push!(results, i) + @async for ii in c + push!(results, ii) end end sleep(1.0) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index d60b44536bcc0..b7160b2da8a46 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -22,8 +22,18 @@ let exename = `$(Base.julia_cmd()) --precompiled=yes --startup-file=no` @test startswith(read(`$exename --help`, String), header) end - # --quiet - # This flag is indirectly tested in test/repl.jl + # --quiet, --banner + let t(q,b) = "Base.JLOptions().quiet == $q && Base.JLOptions().banner == $b" + @test success(`$exename -e $(t(0, -1))`) + @test success(`$exename -q -e $(t(1, 0))`) + @test success(`$exename --quiet -e $(t(1, 0))`) + @test success(`$exename --banner=no -e $(t(0, 0))`) + @test success(`$exename --banner=yes -e $(t(0, 1))`) + @test success(`$exename -q --banner=no -e $(t(1, 0))`) + @test success(`$exename -q --banner=yes -e $(t(1, 1))`) + @test success(`$exename --banner=no -q -e $(t(1, 0))`) + @test success(`$exename --banner=yes -q -e $(t(1, 1))`) + end # --home @test success(`$exename -H $JULIA_HOME`) @@ -429,3 +439,30 @@ for precomp in ("yes", "no") @test length(lno.captures) == 1 @test parse(Int, lno.captures[1]) > 0 end + +# PR #23002 +let exename = `$(Base.julia_cmd()) --startup-file=no` + for (mac, flag, pfix, msg) in [("@test_nowarn", ``, "_1", ""), + ("@test_warn", `--warn-overwrite=yes`, "_2", "\"WARNING: Method definition\"")] + str = """ + using Base.Test + try + # issue #18725 + $mac $msg @eval Main begin + f18725$(pfix)(x) = 1 + f18725$(pfix)(x) = 2 + end + @test Main.f18725$(pfix)(0) == 2 + # PR #23030 + $mac $msg @eval Main module Module23030$(pfix) + f23030$(pfix)(x) = 1 + f23030$(pfix)(x) = 2 + end + catch + exit(-1) + end + exit(0) + """ + run(`$exename $flag -e $str`) + end +end diff --git a/test/codegen.jl b/test/codegen.jl index a5009875ef290..35ac3b09e365e 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -51,13 +51,9 @@ end function test_jl_dump_compiles() tfile = tempname() io = open(tfile, "w") + @eval(test_jl_dump_compiles_internal(x) = x) ccall(:jl_dump_compiles, Void, (Ptr{Void},), io.handle) - eval(@noinline function test_jl_dump_compiles_internal(x) - if x > 0 - test_jl_dump_compiles_internal(x-1) - end - end) - test_jl_dump_compiles_internal(1) + @eval test_jl_dump_compiles_internal(1) ccall(:jl_dump_compiles, Void, (Ptr{Void},), C_NULL) close(io) tstats = stat(tfile) @@ -71,8 +67,9 @@ end function test_jl_dump_compiles_toplevel_thunks() tfile = tempname() io = open(tfile, "w") + topthunk = expand(Main, :(for i in 1:10; end)) ccall(:jl_dump_compiles, Void, (Ptr{Void},), io.handle) - eval(expand(Main, :(for i in 1:10 end))) + Core.eval(Main, topthunk) ccall(:jl_dump_compiles, Void, (Ptr{Void},), C_NULL) close(io) tstats = stat(tfile) @@ -248,3 +245,8 @@ let c = [1,2,3] len2 = issue22582!(c, true) @test len1 == len2 end + +# PR #23595 +@generated f23595(g, args...) = Expr(:call, :g, Expr(:(...), :args)) +x23595 = rand(1) +@test f23595(Core.arrayref, true, x23595, 1) == x23595[] diff --git a/test/codevalidation.jl b/test/codevalidation.jl index 0ed65869dcad5..829df9759c473 100644 --- a/test/codevalidation.jl +++ b/test/codevalidation.jl @@ -105,13 +105,6 @@ errors = Core.Inference.validate_code(c) @test length(errors) == 1 @test errors[1].kind === Core.Inference.SSAVALUETYPES_MISMATCH_UNINFERRED -# INVALID_ASSIGNMENT_SLOTFLAG -c = Core.Inference.copy_code_info(c0) -c.slotflags[8] = 0x00 -errors = Core.Inference.validate_code(c) -@test length(errors) == 1 -@test errors[1].kind === Core.Inference.INVALID_ASSIGNMENT_SLOTFLAG - # SIGNATURE_NARGS_MISMATCH old_sig = mi.def.sig mi.def.sig = Tuple{1,2} diff --git a/test/complex.jl b/test/complex.jl index e1428a826573e..bf131ab6b6f93 100644 --- a/test/complex.jl +++ b/test/complex.jl @@ -901,7 +901,7 @@ end @test round.([1:5;] + 0.5im) == [1.0:5.0;] @test float(Complex(1, 2)) == Complex(1.0, 2.0) - @test round(float(Complex(π, e)),3) == Complex(3.142, 2.718) + @test round(float(Complex(π, ℯ)),3) == Complex(3.142, 2.718) end @testset "Complex32 arithmetic, PR #10003" begin @@ -947,7 +947,7 @@ end @test big(1)/(10+10im) ≈ (5-5im)/big(100) ≈ big"0.05" - big"0.05"*im @testset "Complex Irrationals, issue #21204" begin - for x in (pi, e, catalan) # No need to test all of them + for x in (pi, ℯ, Base.MathConstants.catalan) # No need to test all of them z = Complex(x, x) @test typeof(z) == Complex{typeof(x)} @test exp(z) ≈ exp(x) * cis(x) diff --git a/test/core.jl b/test/core.jl index e760be057451d..9c5d8e37da9d6 100644 --- a/test/core.jl +++ b/test/core.jl @@ -328,6 +328,13 @@ let f = i18408() @test_throws UndefRefError f(0) end +# issue #23558 +c23558(n,k) = + let fact(n) = if (n == 0) 1 else n*fact(n-1) end + fact(n)/fact(k)/fact(n-k) + end +@test c23558(10, 5) == 252 + # variable scope, globals glob_x = 23 function glotest() @@ -438,7 +445,8 @@ function const_implies_local() x = 1 local y let - const x = 0 + # TODO: change back to `const` if that's ever allowed + local x = 0 y = x end x, y @@ -941,7 +949,7 @@ end @test unsafe_pointer_to_objref(ccall(:jl_call1, Ptr{Void}, (Any,Any), x -> x+1, 314158)) == 314159 -@test unsafe_pointer_to_objref(pointer_from_objref(e+pi)) == e+pi +@test unsafe_pointer_to_objref(pointer_from_objref(ℯ+pi)) == ℯ+pi let local a, aa @@ -1099,7 +1107,7 @@ end # issue #2169 let i2169(a::Array{T}) where {T} = typemin(T) - @test invoke(i2169, Tuple{Array} ,Int8[1]) === Int8(-128) + @test invoke(i2169, Tuple{Array}, Int8[1]) === Int8(-128) end # issue #2365 @@ -1268,14 +1276,13 @@ let @test foo(x) == [1.0, 2.0, 3.0] end -# TODO!! # issue #4115 -#mutable struct Foo4115 -#end -#const Foo4115s = NTuple{3,Union{Foo4115,Type{Foo4115}}} -#baz4115(x::Foo4115s) = x -#@test baz4115(convert(Tuple{Type{Foo4115},Type{Foo4115},Foo4115}, -# (Foo4115,Foo4115,Foo4115()))) == (Foo4115,Foo4115,Foo4115()) +mutable struct Foo4115 end +const Foo4115s = NTuple{3, Union{Foo4115, Type{Foo4115}}} +baz4115(x::Foo4115s) = x +let t = (Foo4115, Foo4115, Foo4115()) + @test_throws MethodError baz4115(t) +end # issue #4129 mutable struct Foo4129; end @@ -2055,11 +2062,11 @@ end # issue #8798 let - const npy_typestrs = Dict("b1"=>Bool, - "i1"=>Int8, "u1"=>UInt8, - "i2"=>Int16, "u2"=>UInt16, - "i4"=>Int32, "u4"=>UInt32, - "i8"=>Int64, "u8"=>UInt64) + npy_typestrs = Dict("b1"=>Bool, + "i1"=>Int8, "u1"=>UInt8, + "i2"=>Int16, "u2"=>UInt16, + "i4"=>Int32, "u4"=>UInt32, + "i8"=>Int64, "u8"=>UInt64) sizeof_lookup() = sizeof(npy_typestrs["i8"]) @test sizeof_lookup() == 8 end @@ -2920,6 +2927,13 @@ function f11065() end @test_throws UndefVarError f11065() +# for loop iterator expression should be evaluated in outer scope +let + for i in (local a = 1:2) + end + @test a == 1:2 +end + # issue #11295 function f11295(x...) call = Expr(x...) @@ -3139,6 +3153,37 @@ end @test_throws TypeError MyType8010([3.0;4.0]) @test_throws TypeError MyType8010_ghost([3.0;4.0]) +module TestNewTypeError +using Base.Test + +struct A +end +struct B + a::A +end +@eval function f1() + # Emitting this direction is not recommended but it can come from `convert` that does not + # return the correct type. + $(Expr(:new, B, 1)) +end +@eval function f2() + a = $(Expr(:new, B, 1)) + a = a + return nothing +end +@generated function f3() + quote + $(Expr(:new, B, 1)) + return nothing + end +end +@test_throws TypeError f1() +@test_throws TypeError f2() +@test_throws TypeError f3() +@test_throws TypeError eval(Expr(:new, B, 1)) + +end + # don't allow redefining types if ninitialized changes struct NInitializedTestType a @@ -3757,11 +3802,11 @@ end module M15455 function rpm_provides(r::T) where T - push!([], select(r,T)) + push!([], partialsort(r,T)) end -select(a,b) = 0 +partialsort(a,b) = 0 end -@test M15455.select(1,2)==0 +@test M15455.partialsort(1,2)==0 # check that medium-sized array is 64-byte aligned (#15139) @test Int(pointer(Vector{Float64}(1024))) % 64 == 0 @@ -3771,7 +3816,7 @@ end # `TypeVar`) without crashing let function arrayset_unknown_dim(::Type{T}, n) where T - Base.arrayset(reshape(Vector{T}(1), ones(Int, n)...), 2, 1) + Base.arrayset(true, reshape(Vector{T}(1), ones(Int, n)...), 2, 1) end arrayset_unknown_dim(Any, 1) arrayset_unknown_dim(Any, 2) @@ -3963,16 +4008,13 @@ end function metadata_matches(ast::CodeInfo) inbounds_cnt = Ref(0) - boundscheck_cnt = Ref(0) for ex in ast.code::Array{Any,1} if isa(ex, Expr) ex = ex::Expr count_expr_push(ex, :inbounds, inbounds_cnt) - count_expr_push(ex, :boundscheck, boundscheck_cnt) end end @test inbounds_cnt[] == 0 - @test boundscheck_cnt[] == 0 end function test_metadata_matches(@nospecialize(f), @nospecialize(tt)) @@ -3988,14 +4030,9 @@ function f2() end end # No, don't write code this way... -@eval function f3() - a = $(Expr(:boundscheck, true)) - return 1 - b = $(Expr(:boundscheck, :pop)) -end @noinline function g(a) end -@eval function f4() +@eval function f3() g($(Expr(:inbounds, true))) @goto out g($(Expr(:inbounds, :pop))) @@ -4005,7 +4042,6 @@ end test_metadata_matches(f1, Tuple{}) test_metadata_matches(f2, Tuple{}) test_metadata_matches(f3, Tuple{}) -test_metadata_matches(f4, Tuple{}) end @@ -4396,7 +4432,7 @@ end function f18054() return Cint(0) end -cfunction(f18054, Cint, ()) +cfunction(f18054, Cint, Tuple{}) # issue #18986: the ccall optimization of cfunction leaves JL_TRY stack in bad state dummy18996() = return nothing @@ -4504,9 +4540,9 @@ let x = 1 @noinline g18444(a) = (x += 1; a[]) f18444_1(a) = invoke(sin, Tuple{Int}, g18444(a)) f18444_2(a) = invoke(sin, Tuple{Integer}, g18444(a)) - @test_throws ErrorException f18444_1(Ref{Any}(1.0)) + @test_throws ErrorException("invoke: argument type error") f18444_1(Ref{Any}(1.0)) @test x == 2 - @test_throws ErrorException f18444_2(Ref{Any}(1.0)) + @test_throws ErrorException("invoke: argument type error") f18444_2(Ref{Any}(1.0)) @test x == 3 @test f18444_1(Ref{Any}(1)) === sin(1) @test x == 4 @@ -4514,6 +4550,13 @@ let x = 1 @test x == 5 end +f18095(::Int, ::Number) = 0x21 +f18095(::Number, ::Int) = 0x12 +@test_throws MethodError f18095(1, 2) +@test_throws MethodError invoke(f18095, Tuple{Int, Int}, 1, 2) +@test_throws MethodError invoke(f18095, Tuple{Int, Any}, 1, 2) +@test invoke(f18095, Tuple{Int, Real}, 1, 2) === 0x21 + # issue #10981, long argument lists let a = fill(["sdf"], 2*10^6), temp_vcat(x...) = vcat(x...) # we introduce a new function `temp_vcat` to make sure there is no existing @@ -4680,17 +4723,6 @@ end @test f14893() == 14893 @test M14893.f14893() == 14893 -# issue #18725 -@test_nowarn @eval Main begin - f18725(x) = 1 - f18725(x) = 2 -end -@test Main.f18725(0) == 2 -@test_warn "WARNING: Method definition f18725(Any) in module Module18725" @eval Main module Module18725 - f18725(x) = 1 - f18725(x) = 2 -end - # issue #19599 f19599(x::((S)->Vector{S})(T)...) where {T} = 1 @test f19599([1],[1]) == 1 @@ -4833,6 +4865,12 @@ let a = Array{Core.TypeofBottom, 1}(2) @test a == [Union{}, Union{}] end +@test_throws TypeError(:T17951, "type definition", Type, Vararg) @eval begin + struct T17951 + x::Vararg + end +end + # issue #21178 struct F21178{A,B} end b21178(::F1,::F2) where {B1,B2,F1<:F21178{B1,<:Any},F2<:F21178{B2}} = F1,F2,B1,B2 @@ -4999,7 +5037,7 @@ let a_foo = Foo22256(Bar22256{true}(2)) @test a_foo.bar.inner == 3 end -# macro hygiene scope (#22307) +# macro hygiene scope (#22307, #23239) macro a22307() return esc(:a22307) end @@ -5013,6 +5051,36 @@ end a22307 = 2 @test c22307() == 2 +macro identity23239b(x) + return esc(x) +end +macro identity23239c(x) + return quote + $(esc(x)) + end +end +macro assign23239d(x, v) + return esc(:($x = $v)) +end +macro assign23239e(x, v) + return quote + $(esc(:($x = $v))) + end +end +macro aa23239() + return quote + a = 1 + @identity23239b b = 2 + @identity23239c c = 3 + @assign23239d d 4 + @assign23239e e 5 + (a, b, c, d, e) + end +end +f23239() = @aa23239() +@test @inferred(f23239()) === (1, 2, 3, 4, 5) + + # issue #22026 module M22026 @@ -5097,6 +5165,26 @@ f_isdefined_va(::T...) where {T} = @isdefined T @test !f_isdefined_va() @test f_isdefined_va(1, 2, 3) +# @isdefined in a loop +let a = [] + for i = 1:2 + push!(a, @isdefined(j)) + local j = 1 + end + @test a == [false, false] +end + +# while loop scope +let a = [], i = 0 + while i < (local b = 2) + push!(a, @isdefined(j)) + local j = 1 + i += 1 + end + @test a == [false, false] + @test b == 2 +end + mutable struct MyStruct22929 x::MyStruct22929 MyStruct22929() = new() @@ -5185,3 +5273,336 @@ module GlobalDef18933 @test @isdefined sincos @test sincos === Base.sincos end + +# issue #23218 +let idx = (7,5,9) + (v,) = (idx...,) + @test v == 7 +end + +module UnionOptimizations + +using Base.Test + +const boxedunions = [Union{}, Union{String, Void}] +const unboxedunions = [Union{Int8, Void}, Union{Int8, Float16, Void}, + Union{Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}, + Union{Char, Date, Int}] + +@test !Base.isbitsunion(boxedunions[1]) +@test !Base.isbitsunion(boxedunions[2]) +@test Base.isbitsunion(unboxedunions[1]) +@test Base.isbitsunion(unboxedunions[2]) +@test Base.isbitsunion(unboxedunions[3]) + +@test Base.bitsunionsize(unboxedunions[1]) == 1 +@test Base.bitsunionsize(unboxedunions[2]) == 2 +@test Base.bitsunionsize(unboxedunions[3]) == 16 +@test Base.bitsunionsize(unboxedunions[4]) == 8 + +@test sizeof(unboxedunions[1]) == 1 +@test sizeof(unboxedunions[2]) == 2 +@test sizeof(unboxedunions[3]) == 16 +@test sizeof(unboxedunions[4]) == 8 + +initvalue(::Type{Void}) = nothing +initvalue(::Type{Char}) = '\0' +initvalue(::Type{Date}) = Date(0, 12, 31) +initvalue(::Type{T}) where {T <: Number} = T(0) + +initvalue2(::Type{Void}) = nothing +initvalue2(::Type{Char}) = Char(0x01) +initvalue2(::Type{Date}) = Date(1) +initvalue2(::Type{T}) where {T <: Number} = T(1) + +U = unboxedunions[1] + +mutable struct UnionField + u::U +end + +x = UnionField(initvalue(Base.uniontypes(U)[1])) +@test x.u === initvalue(Base.uniontypes(U)[1]) +x.u = initvalue2(Base.uniontypes(U)[1]) +@test x.u === initvalue2(Base.uniontypes(U)[1]) +x.u = initvalue(Base.uniontypes(U)[2]) +@test x.u === initvalue(Base.uniontypes(U)[2]) + +# PR #23367 +struct A23367 + x::Union{Int8, Int16, NTuple{7, Int8}, Void} +end +struct B23367 + x::Int8 + y::A23367 + z::Int8 +end +@noinline compare(a, b) = (a === b) # test code-generation of `is` +@noinline get_x(a::A23367) = a.x +function constant23367 end +let + b = B23367(91, A23367(ntuple(i -> Int8(i), Val(7))), 23) + @eval @noinline constant23367(a, b) = (a ? b : $b) + b2 = Ref(b)[] # copy b via field assignment + b3 = B23367[b][1] # copy b via array assignment + @test pointer_from_objref(b) == pointer_from_objref(b) + @test pointer_from_objref(b) != pointer_from_objref(b2) + @test pointer_from_objref(b) != pointer_from_objref(b3) + @test pointer_from_objref(b2) != pointer_from_objref(b3) + + @test b === b2 === b3 + @test compare(b, b2) + @test compare(b, b3) + @test object_id(b) === object_id(b2) == object_id(b3) + @test b.x === Int8(91) + @test b.z === Int8(23) + @test b.y === A23367((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7))) + @test sizeof(b) == 12 + @test A23367(Int8(1)).x === Int8(1) + @test A23367(Int8(0)).x === Int8(0) + @test A23367(Int16(1)).x === Int16(1) + @test A23367(nothing).x === nothing + @test sizeof(b.y) == 8 + @test get_x(A23367(Int8(1))) === Int8(1) + + # test code-generation of constants + other = B23367(91, A23367(nothing), 23) + @test constant23367(true, other) === other + @test constant23367(false, other) === b +end + +for U in boxedunions + local U + for N in (1, 2, 3, 4) + A = Array{U}(ntuple(x->0, N)...) + @test isempty(A) + @test sizeof(A) == 0 + + A = Array{U}(ntuple(x->10, N)...) + @test length(A) == 10^N + @test sizeof(A) == sizeof(Int) * (10^N) + @test !isassigned(A, 1) + end +end + +# unsafe_wrap +let + A4 = [1, 2, 3] + @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A4)), 3) + A5 = [1 2 3; 4 5 6] + @test_throws ArgumentError unsafe_wrap(Array, convert(Ptr{Union{Int, Void}}, pointer(A5)), 6) +end + +# copy! +A23567 = Vector{Union{Float64, Void}}(5) +B23567 = collect(Union{Float64, Void}, 1.0:3.0) +copy!(A23567, 2, B23567) +@test A23567[1] === nothing +@test A23567[2] === 1.0 +@test A23567[3] === 2.0 +@test A23567[4] === 3.0 + +# vcat +t2 = deepcopy(A23567) +t3 = deepcopy(A23567) +t4 = vcat(A23567, t2, t3) +@test t4[1:5] == A23567 +@test t4[6:10] == A23567 +@test t4[11:15] == A23567 + +for U in unboxedunions + local U + for N in (1, 2, 3, 4) + A = Array{U}(ntuple(x->0, N)...) + @test isempty(A) + @test sizeof(A) == 0 + + len = ntuple(x->10, N) + mxsz = maximum(sizeof, Base.uniontypes(U)) + A = Array{U}(len) + @test length(A) == prod(len) + @test sizeof(A) == prod(len) * mxsz + @test isassigned(A, 1) + @test isassigned(A, length(A)) + + # arrayref / arrayset + F = Base.uniontypes(U)[1] + @test A[1] === initvalue(F) + A[1] = initvalue2(F) + @test A[1] === initvalue2(F) + + F2 = Base.uniontypes(U)[2] + A[2] = initvalue(F2) + @test A[2] === initvalue(F2) + + for (i, U2) in enumerate(Base.uniontypes(U)) + A[i] = initvalue2(U2) + @test A[i] === initvalue2(U2) + end + + # serialize / deserialize + io = IOBuffer() + serialize(io, A) + seekstart(io) + A2 = deserialize(io) + @test A == A2 + + # reshape + A3 = reshape(A, (div(prod(len), 2), 2)) + @test sizeof(A) == prod(len) * mxsz + @test isassigned(A, 1) + @test A[1] === initvalue2(F) + + # copy + A4 = copy(A) + @test A == A4 + + if N == 1 + ## Dequeue functions + # pop! + F2 = Base.uniontypes(U)[2] + len = len[1] + A = U[initvalue2(F2) for i = 1:len] + for i = 1:len + @test A[end] === initvalue2(F2) + v = pop!(A) + @test v === initvalue2(F2) + end + @test isempty(A) + + # shift! + A = U[initvalue2(F2) for i = 1:len] + for i = 1:len + @test A[1] === initvalue2(F2) + shift!(A) + end + @test isempty(A) + + # empty! + A = U[initvalue2(F2) for i = 1:len] + empty!(A) + @test isempty(A) + + # resize! + A = U[initvalue2(F2) for i = 1:len] + resize!(A, 1) + @test length(A) === 1 + @test A[1] === initvalue2(F2) + resize!(A, len) + @test length(A) === len + @test A[1] === initvalue2(F2) + @test typeof(A[end]) === F + + # deleteat! + F = Base.uniontypes(U)[2] + A = U[rand(F(1):F(len)) for i = 1:len] + # The 2-arg `unique` method works around #22688 + deleteat!(A, map(Int, sort!(unique(identity, A[1:4])))) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, 1:2) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + deleteat!(A, 1:2) + @test length(A) == len - 4 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, length(A)-1:length(A)) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + deleteat!(A, length(A)-1:length(A)) + @test length(A) == len - 4 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, 2:3) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + A = U[initvalue2(F2) for i = 1:len] + deleteat!(A, length(A)-2:length(A)-1) + @test length(A) == len - 2 + @test all(A .== initvalue2(F2)) + + # unshift! + A = U[initvalue2(F2) for i = 1:len] + for i = 1:5 + unshift!(A, initvalue2(F)) + unshift!(A, initvalue(F2)) + @test A[1] === initvalue(F2) + @test A[2] === initvalue2(F) + end + + # push! / append! / prepend! + A = U[initvalue2(F2) for i = 1:len] + push!(A, initvalue2(F)) + @test A[end] === initvalue2(F) + push!(A, initvalue2(F2)) + @test A[end] === initvalue2(F2) + append!(A, [initvalue(F), initvalue2(F)]) + @test A[end] === initvalue2(F) + @test A[end-1] === initvalue(F) + prepend!(A, [initvalue(F), initvalue2(F)]) + @test A[2] === initvalue2(F) + @test A[1] === initvalue(F) + + # insert! + A = U[initvalue2(F2) for i = 1:len] + insert!(A, 2, initvalue2(F)) + @test A[2] === initvalue2(F) + @test A[1] === initvalue2(F2) + @test A[3] === initvalue2(F2) + @test A[end] === initvalue2(F2) + A = U[initvalue2(F2) for i = 1:len] + insert!(A, 8, initvalue2(F)) + @test A[8] === initvalue2(F) + @test A[7] === initvalue2(F2) + @test A[9] === initvalue2(F2) + @test A[end] === initvalue2(F2) + + # splice! + A = U[initvalue2(F2) for i = 1:len] + V = splice!(A, 1:2) + @test length(A) == len - 2 + @test length(V) == 2 + @test V[1] == initvalue2(F2) + @test V[2] == initvalue2(F2) + @test A[1] == initvalue2(F2) + @test A[end] == initvalue2(F2) + + A = U[initvalue2(F2) for i = 1:len] + V = splice!(A, 4:5) + @test length(A) == len - 2 + @test length(V) == 2 + @test V[1] == initvalue2(F2) + @test V[2] == initvalue2(F2) + @test A[1] == initvalue2(F2) + @test A[end] == initvalue2(F2) + end + end +end + +end # module UnionOptimizations + +# issue #6614, argument destructuring +f6614((x, y)) = [x, y] +@test f6614((4, 3)) == [4, 3] +g6614((x, y), (z,), (a, b)) = (x,y,z,a,b) +@test g6614((1, 2), (3,), (4, 5)) === (1,2,3,4,5) +@test_throws MethodError g6614(1, 2) +@test_throws MethodError g6614((1, 2), (3,)) +@test_throws BoundsError g6614((1, 2), (3,), (1,)) +h6614((x, y) = (5, 6)) = (y, x) +@test h6614() == (6, 5) +@test h6614((4, 5)) == (5, 4) +ff6614((x, y)::Tuple{Int, String}) = (x, y) +@test ff6614((1, "")) == (1, "") +@test_throws MethodError ff6614((1, 1)) +gg6614((x, y)::Tuple{Int, String} = (2, " ")) = (x, y) +@test gg6614() == (2, " ") +function hh6614() + x, y = 1, 2 + function g((x,y)) + # make sure x and y are local + end + g((4,5)) + x, y +end +@test hh6614() == (1, 2) diff --git a/test/dates/io.jl b/test/dates/io.jl index c7523277e4535..55e01c6ab3a61 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -378,7 +378,7 @@ end # Issue: https://github.com/quinnj/TimeZones.jl/issues/19 let - const Zulu = String + Zulu = String function Dates.tryparsenext(d::Dates.DatePart{'Z'}, str, i, len) Dates.tryparsenext_word(str, i, len, Dates.min_width(d), Dates.max_width(d)) diff --git a/test/dates/rounding.jl b/test/dates/rounding.jl index e32f592a0f301..57f999c4382cc 100644 --- a/test/dates/rounding.jl +++ b/test/dates/rounding.jl @@ -114,6 +114,7 @@ dt = Dates.DateTime(-1, 12, 29, 19, 19, 19, 19) # Test rounding for dates that should not need rounding for dt in [Dates.DateTime(2016, 1, 1), Dates.DateTime(-2016, 1, 1)] + local dt for p in [Dates.Year, Dates.Month, Dates.Day, Dates.Hour, Dates.Minute, Dates.Second] @test floor(dt, p) == dt @test ceil(dt, p) == dt diff --git a/test/dict.jl b/test/dict.jl index 2f0a43ce9afd6..dbd4771b2dfe1 100644 --- a/test/dict.jl +++ b/test/dict.jl @@ -268,7 +268,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for cols in (12, 40, 80), rows in (2, 10, 24) # Ensure output is limited as requested s = IOBuffer() - io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) + io = Base.IOContext(s, :limit => true, :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), d) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -278,7 +278,7 @@ for d in (Dict("\n" => "\n", "1" => "\n", "\n" => "2"), for f in (keys, values) s = IOBuffer() - io = Base.IOContext(Base.IOContext(s, :limit => true), :displaysize => (rows, cols)) + io = Base.IOContext(s, :limit => true, :displaysize => (rows, cols)) Base.show(io, MIME("text/plain"), f(d)) out = split(String(take!(s)),'\n') for line in out[2:end] @@ -311,7 +311,7 @@ end mutable struct Alpha end Base.show(io::IO, ::Alpha) = print(io,"α") let sbuff = IOBuffer(), - io = Base.IOContext(Base.IOContext(sbuff, :limit => true), :displaysize => (10, 20)) + io = Base.IOContext(sbuff, :limit => true, :displaysize => (10, 20)) Base.show(io, MIME("text/plain"), Dict(Alpha()=>1)) local str = String(take!(sbuff)) @@ -511,7 +511,7 @@ let d = ImmutableDict{String, String}(), end # filtering -let d = Dict(zip(1:1000,1:1000)), f = (k,v) -> iseven(k) +let d = Dict(zip(1:1000,1:1000)), f = p -> iseven(p.first) @test filter(f, d) == filter!(f, copy(d)) == invoke(filter!, Tuple{Function,Associative}, f, copy(d)) == Dict(zip(2:2:1000, 2:2:1000)) @@ -636,7 +636,7 @@ Dict(1 => rand(2,3), 'c' => "asdf") # just make sure this does not trigger a dep @test 4 ∉ values(wkd) @test length(wkd) == 2 @test !isempty(wkd) - wkd = filter!( (k,v) -> k != B, wkd) + wkd = filter!( p -> p.first != B, wkd) @test B ∉ keys(wkd) @test 3 ∉ values(wkd) @test length(wkd) == 1 diff --git a/test/dimensionful.jl b/test/dimensionful.jl index 999f362cc27a7..bb3b5ac2043af 100644 --- a/test/dimensionful.jl +++ b/test/dimensionful.jl @@ -12,7 +12,7 @@ struct Furlong{p,T<:Number} <: Number end Furlong(x::T) where {T<:Number} = Furlong{1,T}(x) (::Type{T})(x::Furlong{p,T}) where {p,T} = x.val -(::Type{Furlong{p}})(v::Number) where {p} = Furlong{p,typeof(v)}(v) +Furlong{p}(v::Number) where {p} = Furlong{p,typeof(v)}(v) Base.convert(::Type{Furlong{p,T}}, x::Furlong{p,S}) where {T,p,S} = Furlong{p,T}(convert(T,x.val)) Base.convert(::Type{Furlong{0,T}}, x::Furlong{0}) where {T} = Furlong{0,T}(convert(T, x.val)) Base.convert(::Type{T}, x::Furlong{0}) where {T<:Number} = convert(T, x.val) diff --git a/test/distributed_exec.jl b/test/distributed_exec.jl index 0b0893d76005f..619a1a3bf5166 100644 --- a/test/distributed_exec.jl +++ b/test/distributed_exec.jl @@ -469,7 +469,7 @@ d[5,1:2:4,8] = 19 AA = rand(4,2) A = @inferred(convert(SharedArray, AA)) B = @inferred(convert(SharedArray, AA')) -@test B*A == ctranspose(AA)*AA +@test B*A == adjoint(AA)*AA d=SharedArray{Int64,2}((10,10); init = D->fill!(D.loc_subarr_1d, myid()), pids=[id_me, id_other]) d2 = map(x->1, d) @@ -525,6 +525,7 @@ function finalize_and_test(r) end for id in [id_me, id_other] + local id finalize_and_test(Future(id)) finalize_and_test((r=Future(id); put!(r, 1); r)) finalize_and_test(RemoteChannel(id)) @@ -536,17 +537,19 @@ finalize(d) @test_throws BoundsError d[1] # Issue 22139 -aorig = a1 = SharedArray{Float64}((3, 3)) -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -@test object_id(aorig) == object_id(a1) -id = a1.id -aorig = nothing -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -gc(); gc() -a1 = remotecall_fetch(fill!, id_other, a1, 1.0) -@test haskey(Base.sa_refs, id) -finalize(a1) -@test !haskey(Base.sa_refs, id) +let + aorig = a1 = SharedArray{Float64}((3, 3)) + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + @test object_id(aorig) == object_id(a1) + id = a1.id + aorig = nothing + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + gc(); gc() + a1 = remotecall_fetch(fill!, id_other, a1, 1.0) + @test haskey(Base.sa_refs, id) + finalize(a1) + @test !haskey(Base.sa_refs, id) +end # Test @parallel load balancing - all processors should get either M or M+1 # iterations out of the loop range for some M. @@ -676,6 +679,7 @@ function test_remoteexception_thrown(expr) end for id in [id_other, id_me] + local id test_remoteexception_thrown() do remotecall_fetch(id) do throw(ErrorException("foobar")) @@ -1248,6 +1252,7 @@ let (p, p2) = filter!(p -> p != myid(), procs()) ex = Any[ (ex::CapturedException).ex for ex in (excpt::CompositeException).exceptions ] end for (p, ex) in zip(procs, ex) + local p if procs isa Int || p != myid() @test (ex::RemoteException).pid == p ex = ((ex::RemoteException).captured::CapturedException).ex @@ -1372,6 +1377,7 @@ end # Test addprocs/rmprocs from master node only for f in [ ()->addprocs(1; exeflags=test_exeflags), ()->rmprocs(workers()) ] + local f try remotecall_fetch(f, id_other) error("Unexpected") diff --git a/test/env.jl b/test/env.jl index 10c75d4da66b8..2a52d3b415d4b 100644 --- a/test/env.jl +++ b/test/env.jl @@ -30,6 +30,7 @@ key = randstring(25) @test !haskey(ENV,key) @test_throws KeyError ENV[key] @test get(ENV,key,"default") == "default" +@test get(() -> "default", ENV, key) == "default" # Test for #17956 @test length(ENV) > 1 diff --git a/test/fastmath.jl b/test/fastmath.jl index bac571c5bf2ab..8aaec484e81e1 100644 --- a/test/fastmath.jl +++ b/test/fastmath.jl @@ -202,3 +202,9 @@ let a = ones(2,2), b = ones(2,2) local c = 0 @test @fastmath(c |= 1) == 1 end + +# issue #23218 +let a = zeros(1), b = ones(1), idx = (1,) + @fastmath a[idx...] += b[idx...] + @test a == b +end diff --git a/test/file.jl b/test/file.jl index be85fab05de7e..f8c9ddf587ac0 100644 --- a/test/file.jl +++ b/test/file.jl @@ -52,6 +52,7 @@ end ####################################################################### # This section tests some of the features of the stat-based file info # ####################################################################### +@test !isfile(Base.Filesystem.StatStruct()) @test isdir(dir) @test !isfile(dir) @test !islink(dir) @@ -196,7 +197,7 @@ function test_timeout(tval) @async test_file_poll(channel, 10, tval) tr = take!(channel) t_elapsed = toq() - @test !ispath(tr[1]) && !ispath(tr[2]) + @test tr[1] === Base.Filesystem.StatStruct() && tr[2] === EOFError() @test tval <= t_elapsed end @@ -268,7 +269,9 @@ test_watch_file_timeout(0.1) test_watch_file_change(6) @test_throws Base.UVError watch_file("____nonexistent_file", 10) -@test_throws Base.UVError poll_file("____nonexistent_file", 2, 10) +@test(@elapsed( + @test(poll_file("____nonexistent_file", 1, 3.1) === + (Base.Filesystem.StatStruct(), EOFError()))) > 3) ############## # mark/reset # @@ -931,6 +934,7 @@ for f in (mkdir, cd, Base.Filesystem.unlink, readlink, rm, touch, readdir, mkpat stat, lstat, ctime, mtime, filemode, filesize, uperm, gperm, operm, touch, isblockdev, ischardev, isdir, isfifo, isfile, islink, ispath, issetgid, issetuid, issocket, issticky, realpath, watch_file, poll_file) + local f @test_throws ArgumentError f("adir\0bad") end @test_throws ArgumentError chmod("ba\0d", 0o222) @@ -1118,22 +1122,35 @@ function test_13559() fn = tempname() run(`mkfifo $fn`) # use subprocess to write 127 bytes to FIFO - writer_cmds = "x=open(\"$fn\", \"w\"); for i=1:127 write(x,0xaa); flush(x); sleep(0.1) end; close(x); quit()" - open(pipeline(`$(Base.julia_cmd()) --startup-file=no -e $writer_cmds`, stderr=STDERR)) - #quickly read FIFO, draining it and blocking but not failing with EOFError yet + writer_cmds = """ + x = open($(repr(fn)), "w") + for i in 1:120 + write(x, 0xaa) + end + flush(x) + Test.@test read(STDIN, Int8) == 31 + for i in 1:7 + write(x, 0xaa) + end + close(x) + """ + p = open(pipeline(`$(Base.julia_cmd()) --startup-file=no -e $writer_cmds`, stderr=STDERR), "w") + # quickly read FIFO, draining it and blocking but not failing with EOFError yet r = open(fn, "r") # 15 proper reads - for i=1:15 - @test read(r, Int64) == -6148914691236517206 + for i in 1:15 + @test read(r, UInt64) === 0xaaaaaaaaaaaaaaaa end - # last read should throw EOFError when FIFO closes, since there are only 7 bytes available. - @test_throws EOFError read(r, Int64) + write(p, 0x1f) + # last read should throw EOFError when FIFO closes, since there are only 7 bytes (or less) available. + @test_throws EOFError read(r, UInt64) close(r) + @test success(p) rm(fn) end test_13559() end -@test_throws ArgumentError mkpath("fakepath",-1) +@test_throws ArgumentError mkpath("fakepath", -1) # issue #22566 if !Sys.iswindows() @@ -1141,13 +1158,21 @@ if !Sys.iswindows() fn = tempname() run(`mkfifo $fn`) - script = "x = open(\"$fn\", \"w\"); close(x)" + script = """ + x = open($(repr(fn)), "w") + write(x, 0x42) + flush(x) + Test.@test read(STDIN, Int8) == 21 + close(x) + """ cmd = `$(Base.julia_cmd()) --startup-file=no -e $script` - open(pipeline(cmd, stderr=STDERR)) + p = open(pipeline(cmd, stderr=STDERR), "w") r = open(fn, "r") + @test read(r, Int8) == 66 + write(p, 0x15) close(r) - + @test success(p) rm(fn) end @@ -1156,3 +1181,65 @@ if !Sys.iswindows() test_22566() end end # !Sys.iswindows + +function test_22922() +<<<<<<< HEAD + def_prefix = "jl_" + tst_prefix = "ABCDEF" +======= + const def_prefix = "jl_" + const tst_prefix = "ABCDEF" +>>>>>>> mktempdir() now supports prefix. + mktempdir() do tmpdir + filename = basename(tmpdir) + @test startswith(filename, def_prefix) + end + mktempdir(; prefix=tst_prefix) do tmpdir + filename = basename(tmpdir) + @test startswith(filename, tst_prefix) + end + # Special character prefix tests + tst_prefix="#!@%^&()" + mktempdir(; prefix=tst_prefix) do tmpdir + filename = basename(tmpdir) + @test startswith(filename, tst_prefix) + end + + # Behavioral differences across OS types + if Sys.iswindows() + @test_throws Base.UVError mktempdir(; prefix="*") + @test_throws Base.UVError mktempdir(; prefix="cdcdccd/") + + # The API accepts "c:/ and c:\\" as valid prefix. The parent directory is ignored. + mkdir("c:/mydir") + + mktempdir("c:/mydir"; prefix="c:/") do tmpdir + filename = basename(tmpdir) + @test length(filename) == 6 + end + mktempdir("c:\\mydir"; prefix="c:\\") do tmpdir + filename = basename(tmpdir) + @test length(filename) == 6 + end + + @test_throws Base.UVError mktempdir(; prefix="cdcdccd/") + else + # '/' is accepted in a prefix but affects the overall path and permissions. + @test_throws Base.UVError mktempdir(; prefix="/") + + # The file created will be of format "/tmp/XXXXXX" + mktempdir("/"; prefix="tmp/") do tmpdir + filename = basename(tmpdir) + @test length(filename) == 6 + end + end + + # Unicode test + tst_prefix="∃x∀y" + mktempdir(; prefix=tst_prefix) do tmpdir + filename = basename(tmpdir) + @test startswith(filename, tst_prefix) + end +end + +test_22922() diff --git a/test/float16.jl b/test/float16.jl index e9a188329c978..81e77c8377b27 100644 --- a/test/float16.jl +++ b/test/float16.jl @@ -128,11 +128,11 @@ end # rounding in conversions let - for f in [.3325f0, -.3325f0] - f16 = Float16(f) + for ff in [.3325f0, -.3325f0] + f16 = Float16(ff) # need to round away from 0. make sure we picked closest number. - @test abs(f-f16) < abs(f-nextfloat(f16)) - @test abs(f-f16) < abs(f-prevfloat(f16)) + @test abs(ff-f16) < abs(ff-nextfloat(f16)) + @test abs(ff-f16) < abs(ff-prevfloat(f16)) end # halfway between and last bit is 1 f = reinterpret(Float32, 0b00111110101010100011000000000000) diff --git a/test/floatfuncs.jl b/test/floatfuncs.jl index c97f077f05438..ed1639b55ef10 100644 --- a/test/floatfuncs.jl +++ b/test/floatfuncs.jl @@ -68,7 +68,7 @@ for elty in (Float32,Float64) end @testset "Types" begin - for x in (Int16(0), 1, 2f0, pi, 3//4, big(5//6), 7.8, big(9), big(e)) + for x in (Int16(0), 1, 2f0, pi, 3//4, big(5//6), 7.8, big(9), big(ℯ)) @test float(typeof(x)) == typeof(float(x)) @test float(typeof(complex(x, x))) == typeof(float(complex(x, x))) end diff --git a/test/goto.jl b/test/goto.jl index 69a181756036f..b56cdf58b2064 100644 --- a/test/goto.jl +++ b/test/goto.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Basic goto tests +expand(x) = Base.expand(@__MODULE__, x) function goto_test1() @goto a @@ -11,8 +12,13 @@ end @test goto_test1() -@test_throws ErrorException eval( - quote +@test eval(:(@label a)) === nothing + +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(:(@goto a)) + +@test Expr(:error, "label \"a\" defined multiple times") == + expand(quote function goto_test2() @goto a @label a @@ -22,17 +28,16 @@ end end) -@test_throws ErrorException eval( - quote +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(quote function goto_test3() @goto a return end end) - -@test_throws ErrorException eval( - quote +@test Expr(:error, "misplaced label") == + expand(quote function goto_test4() @goto a try @@ -43,23 +48,48 @@ end end) -# test that labels in macros are reassigned -macro goto_test5_macro() - @label a +# test that labels in macros are reassigned if unescaped +macro goto_test5_macro1() + return :(@label a) +end +macro goto_test5_macro2() + return :(@goto a) +end +macro goto_test5_macro3() + return esc(:(@label a)) end -@test_throws ErrorException eval( - quote - function goto_test5() +@test Expr(:error, "label \"a\" referenced but not defined") == + expand(quote + function goto_test5_1() @goto a - @goto_test5_macro + @goto_test5_macro1 return end end) +let e = expand(quote + function goto_test5_2() + @goto_test5_macro2 + @label a + return + end + end) + @test (e::Expr).head === :error + @test ismatch(r"label \"#\d+#a\" referenced but not defined", e.args[1]) +end + +function goto_test5_3() + @goto a + return false + @goto_test5_macro3 + return true +end +@test goto_test5_3() + -@test_throws ErrorException eval( - quote +@test Expr(:error, "goto from a try/finally block is not permitted") == + expand(quote function goto_test6() try @goto a diff --git a/test/inference.jl b/test/inference.jl index 0d6458dba76b6..8f11ffb85b138 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -254,35 +254,6 @@ function foo9222() end @test 0.0 == foo9222() -# make sure none of the slottypes are left as Core.Inference.Const objects -function f18679() - for i = 1:2 - if i == 1 - a = ((),) - else - return a[1] - end - end -end -g18679(x::Tuple) = () -g18679() = g18679(any_undef_global::Union{Int,Tuple{}}) -for code in Any[ - @code_typed(f18679())[1] - @code_typed(g18679())[1]] - @test all(x->isa(x, Type), code.slottypes) - local notconst(@nospecialize(other)) = true - notconst(slot::TypedSlot) = @test isa(slot.typ, Type) - function notconst(expr::Expr) - @test isa(expr.typ, Type) - for a in expr.args - notconst(a) - end - end - for e in code.code - notconst(e) - end -end - # branching based on inferrable conditions let f(x) = isa(x,Int) ? 1 : "" @test Base.return_types(f, Tuple{Int}) == [Int] @@ -470,11 +441,62 @@ function test_inferred_static(arrow::Pair) end end +function f18679() + local a + for i = 1:2 + if i == 1 + a = ((),) + else + return a[1] + end + end +end +g18679(x::Tuple) = () +g18679() = g18679(any_undef_global::Union{Int, Tuple{}}) +function h18679() + for i = 1:2 + local a + if i == 1 + a = ((),) + else + @isdefined(a) && return "BAD" + end + end +end + function g19348(x) a, b = x - return a + b + g = 1 + g = 2 + c = Base.indexed_next(x, g, g) + return a + b + c[1] +end + +for codetype in Any[ + @code_typed(f18679()), + @code_typed(g18679()), + @code_typed(h18679()), + @code_typed(g19348((1, 2.0)))] + # make sure none of the slottypes are left as Core.Inference.Const objects + code = codetype[1] + @test all(x->isa(x, Type), code.slottypes) + local notconst(@nospecialize(other)) = true + notconst(slot::TypedSlot) = @test isa(slot.typ, Type) + function notconst(expr::Expr) + @test isa(expr.typ, Type) + for a in expr.args + notconst(a) + end + end + for e in code.code + notconst(e) + end + test_inferred_static(code) end -test_inferred_static(@code_typed g19348((1, 2.0))) +@test f18679() === () +@test_throws UndefVarError(:any_undef_global) g18679() +@test h18679() === nothing + # issue #5575 f5575() = zeros(Type[Float64][1], 1) @@ -795,7 +817,7 @@ end struct NArray_17003{T,N} <: AArray_17003{Nable_17003{T},N} end -(::Type{NArray_17003})(::Array{T,N}) where {T,N} = NArray_17003{T,N}() +NArray_17003(::Array{T,N}) where {T,N} = NArray_17003{T,N}() gl_17003 = [1, 2, 3] @@ -1176,3 +1198,6 @@ g23024(TT::Tuple{DataType}) = f23024(TT[1], v23024) @test Base.return_types(f23024, (DataType, Any)) == Any[Int] @test Base.return_types(g23024, (Tuple{DataType},)) == Any[Int] @test g23024((UInt8,)) === 2 + +@test !Core.Inference.isconstType(Type{typeof(Union{})}) # could be Core.TypeofBottom or Type{Union{}} at runtime +@test Base.return_types(supertype, (Type{typeof(Union{})},)) == Any[Any] diff --git a/test/intset.jl b/test/intset.jl index 735f53ca53559..fbe507747c38d 100644 --- a/test/intset.jl +++ b/test/intset.jl @@ -90,6 +90,10 @@ end # issue #23099 : these tests should not segfault @test_throws ArgumentError symdiff!(IntSet(rand(1:100, 30)), 0) @test_throws ArgumentError symdiff!(IntSet(rand(1:100, 30)), [0, 2, 4]) + + # issue #23557 : + @test_throws MethodError symdiff!(IntSet([1]), ['a']) # should no stack-overflow + @test_throws MethodError symdiff!(IntSet([1, 2]), [[1]]) # should not return IntSet([2]) end @testset "copy, copy!, similar" begin @@ -252,9 +256,7 @@ end @testset "setlike" begin p = IntSet([1,2,5,6]) - resize!(p.bits, 6) q = IntSet([1,3,5,7]) - resize!(q.bits, 8) a = Set(p) b = Set(q) for f in (union, intersect, setdiff, symdiff) diff --git a/test/libgit2-helpers.jl b/test/libgit2-helpers.jl index 5b02c4346cf8d..09fa7d8207f43 100644 --- a/test/libgit2-helpers.jl +++ b/test/libgit2-helpers.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -import Base.LibGit2: AbstractCredentials, UserPasswordCredentials, SSHCredentials, CachedCredentials +import Base.LibGit2: AbstractCredentials, UserPasswordCredentials, SSHCredentials, + CachedCredentials, CredentialPayload, Payload """ Emulates the LibGit2 credential loop to allows testing of the credential_callback function @@ -11,10 +12,10 @@ function credential_loop( url::AbstractString, user::Nullable{<:AbstractString}, allowed_types::UInt32, - cache::CachedCredentials=CachedCredentials()) + payload::CredentialPayload) cb = Base.LibGit2.credentials_cb() libgitcred_ptr_ptr = Ref{Ptr{Void}}(C_NULL) - payload_ptr = Ref(Nullable{AbstractCredentials}(cache)) + payload_ptr = Ref(payload) # Number of times credentials were authenticated against. With the real LibGit2 # credential loop this would be how many times we sent credentials to the remote. @@ -29,7 +30,7 @@ function credential_loop( num_authentications += 1 # Check if the callback provided us with valid credentials - if length(cache.cred) == 1 && first(values(cache.cred)) == valid_credential + if !isnull(payload.credential) && get(payload.credential) == valid_credential break end @@ -38,44 +39,59 @@ function credential_loop( end end - return err, num_authentications + # Note: LibGit2.GitError(0) will not work if an error message has been set. + git_error = if err == 0 + LibGit2.GitError(LibGit2.Error.None, LibGit2.Error.GIT_OK, "No errors") + else + LibGit2.GitError(err) + end + + return git_error, num_authentications end function credential_loop( valid_credential::UserPasswordCredentials, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}()) - credential_loop(valid_credential, url, user, 0x000001) + user::Nullable{<:AbstractString}=Nullable{String}(), + payload::CredentialPayload=CredentialPayload()) + credential_loop(valid_credential, url, user, 0x000001, payload) end function credential_loop( valid_credential::SSHCredentials, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}(); + user::Nullable{<:AbstractString}=Nullable{String}(), + payload::CredentialPayload=CredentialPayload(); use_ssh_agent::Bool=false) - cache = CachedCredentials() if !use_ssh_agent + if isnull(payload.cache) + payload.cache = Nullable(CachedCredentials()) + end + cache = get(payload.cache) + m = match(LibGit2.URL_REGEX, url) default_cred = LibGit2.reset!(SSHCredentials(true), -1) default_cred.usesshagent = "N" LibGit2.get_creds!(cache, "ssh://$(m[:host])", default_cred) end - credential_loop(valid_credential, url, user, 0x000046, cache) + credential_loop(valid_credential, url, user, 0x000046, payload) end function credential_loop( valid_credential::UserPasswordCredentials, url::AbstractString, - user::AbstractString) - credential_loop(valid_credential, url, Nullable(user)) + user::AbstractString, + payload::CredentialPayload=CredentialPayload()) + credential_loop(valid_credential, url, Nullable(user), payload) end function credential_loop( valid_credential::SSHCredentials, url::AbstractString, - user::AbstractString; + user::AbstractString, + payload::CredentialPayload=CredentialPayload(); use_ssh_agent::Bool=false) - credential_loop(valid_credential, url, Nullable(user), use_ssh_agent=use_ssh_agent) + credential_loop(valid_credential, url, Nullable(user), payload, use_ssh_agent=use_ssh_agent) end diff --git a/test/libgit2.jl b/test/libgit2.jl index e63f989a7a7d4..fa306afc4b4c7 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -76,6 +76,8 @@ end a = Base.cconvert(Ptr{LibGit2.StrArrayStruct}, p) b = Base.unsafe_convert(Ptr{LibGit2.StrArrayStruct}, a) @test p == convert(Vector{String}, unsafe_load(b)) + @noinline gcuse(a) = a + gcuse(a) end @testset "Signature" begin @@ -123,7 +125,7 @@ end @test m[:password] == "pass" @test m[:host] == "server.com" @test m[:port] == "80" - @test m[:path] == "/org/project.git" + @test m[:path] == "org/project.git" end @testset "SSH URL" begin @@ -133,7 +135,7 @@ end @test m[:password] == "pass" @test m[:host] == "server" @test m[:port] == "22" - @test m[:path] == "/project.git" + @test m[:path] == "project.git" end @testset "SSH URL, scp-like syntax" begin @@ -165,7 +167,7 @@ end @test m[:password] === nothing @test m[:host] == "github.com" @test m[:port] === nothing - @test m[:path] == "/JuliaLang/Example.jl.git" + @test m[:path] == "JuliaLang/Example.jl.git" end @testset "SSH URL, realistic" begin @@ -216,7 +218,7 @@ end password="pass", host="server.com", port=80, - path="/org/project.git") + path="org/project.git") @test url == "https://user:pass@server.com:80/org/project.git" end @@ -227,7 +229,7 @@ end password="pass", host="server", port="22", - path="/project.git") + path="project.git") @test url == "ssh://user:pass@server:22/project.git" end @@ -244,7 +246,7 @@ end url = LibGit2.git_url( scheme="https", host="github.com", - path="/JuliaLang/Example.jl.git") + path="JuliaLang/Example.jl.git") @test url == "https://github.com/JuliaLang/Example.jl.git" end @@ -273,11 +275,11 @@ end @test url == "user@server.com" end - @testset "HTTP URL, path missing slash prefix" begin + @testset "HTTP URL, path includes slash prefix" begin url = LibGit2.git_url( scheme="http", host="server.com", - path="path") + path="/path") @test url == "http://server.com/path" end @@ -1609,10 +1611,23 @@ mktempdir() do dir # which use the `getpass` function. At the moment we can only fake this on UNIX based # systems. if Sys.isunix() + git_ok = LibGit2.GitError( + LibGit2.Error.None, LibGit2.Error.GIT_OK, + "No errors") + abort_prompt = LibGit2.GitError( LibGit2.Error.Callback, LibGit2.Error.EAUTH, "Aborting, user cancelled credential request.") + incompatible_error = LibGit2.GitError( + LibGit2.Error.Callback, LibGit2.Error.EAUTH, + "The explicitly provided credential is incompatible with the requested " * + "authentication methods.") + + eauth_error = LibGit2.GitError( + LibGit2.Error.None, LibGit2.Error.EAUTH, + "No errors") + @testset "SSH credential prompt" begin url = "git@github.com:test/package.jl" @@ -1622,27 +1637,24 @@ mktempdir() do dir username = "git" passphrase = "secret" - ssh_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "$username") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) - """ + ssh_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url, $username) + end - ssh_p_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "$passphrase", "$valid_p_key", "$valid_p_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "$username") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) - """ + ssh_p_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + credential_loop(valid_cred, $url, $username) + end # SSH requires username - ssh_u_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) - """ + ssh_u_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url) + end # Note: We cannot use the default ~/.ssh/id_rsa for tests since we cannot be # sure a users will actually have these files. Instead we will use the ENV @@ -1650,8 +1662,8 @@ mktempdir() do dir # Default credentials are valid withenv("SSH_KEY_PATH" => valid_key) do - err, auth_attempts = challenge_prompt(ssh_cmd, []) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_ex, []) + @test err == git_ok @test auth_attempts == 1 end @@ -1660,8 +1672,8 @@ mktempdir() do dir challenges = [ "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) + @test err == git_ok @test auth_attempts == 1 # User mistypes passphrase. @@ -1673,15 +1685,15 @@ mktempdir() do dir # "Private key location for 'git@github.com' [$valid_p_key]:" => "\n", "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) + @test err == git_ok @test auth_attempts == 5 # User sends EOF in passphrase prompt which aborts the credential request challenges = [ "Passphrase for $valid_p_key:" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1689,14 +1701,14 @@ mktempdir() do dir challenges = [ "Passphrase for $valid_p_key:" => "\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 end withenv("SSH_KEY_PATH" => valid_p_key, "SSH_KEY_PASS" => passphrase) do - err, auth_attempts = challenge_prompt(ssh_p_cmd, []) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_p_ex, []) + @test err == git_ok @test auth_attempts == 1 end @@ -1706,15 +1718,15 @@ mktempdir() do dir challenges = [ "Username for 'github.com':" => "$username\n", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) + @test err == git_ok @test auth_attempts == 1 # User sends EOF in username prompt which aborts the credential request challenges = [ "Username for 'github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1723,7 +1735,7 @@ mktempdir() do dir "Username for 'github.com':" => "\n", "Username for 'github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 5 # Should ideally be <= 2 @@ -1733,24 +1745,23 @@ mktempdir() do dir "Username for 'github.com' [foo]:" => "\n", "Username for 'github.com' [foo]:" => "\x04", # Need to manually abort ] - err, auth_attempts = challenge_prompt(ssh_u_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_u_ex, challenges) @test err == abort_prompt @test auth_attempts == 6 # Credential callback is given an empty string in the `username_ptr` # instead of the typical C_NULL. - ssh_user_empty_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.SSHCredentials("$username", "", "$valid_key", "$valid_key.pub") - err, auth_attempts = credential_loop(valid_cred, "$url", "") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) - """ + ssh_user_empty_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, "", $valid_key, $(valid_key * ".pub")) + credential_loop(valid_cred, $url, "") + end challenges = [ "Username for 'github.com':" => "$username\n", ] - err, auth_attempts = challenge_prompt(ssh_user_empty_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_user_empty_ex, challenges) + @test err == git_ok @test auth_attempts == 1 end @@ -1770,8 +1781,8 @@ mktempdir() do dir challenges = [ "Private key location for 'git@github.com':" => "$valid_key\n", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_ex, challenges) + @test err == git_ok @test auth_attempts == 1 # User provides valid credentials that requires a passphrase @@ -1779,15 +1790,15 @@ mktempdir() do dir "Private key location for 'git@github.com':" => "$valid_p_key\n", "Passphrase for $valid_p_key:" => "$passphrase\n", ] - err, auth_attempts = challenge_prompt(ssh_p_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_p_ex, challenges) + @test err == git_ok @test auth_attempts == 1 # User sends EOF in private key prompt which aborts the credential request challenges = [ "Private key location for 'git@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1797,7 +1808,7 @@ mktempdir() do dir "Public key location for 'git@github.com' [.pub]:" => "\n", "Private key location for 'git@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) + err, auth_attempts = challenge_prompt(ssh_ex, challenges) @test err == abort_prompt @test auth_attempts == 2 end @@ -1811,8 +1822,8 @@ mktempdir() do dir challenges = [ "Private key location for 'git@github.com' [$invalid_key]:" => "$valid_key\n", ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_ex, challenges) + @test err == git_ok @test auth_attempts == 2 end =# @@ -1828,8 +1839,8 @@ mktempdir() do dir "Private key location for 'git@github.com' [$valid_key]:" => "\n" "Public key location for 'git@github.com':" => "$valid_key.pub\n" ] - err, auth_attempts = challenge_prompt(ssh_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(ssh_ex, challenges) + @test err == git_ok @test auth_attempts == 2 end =# @@ -1841,27 +1852,26 @@ mktempdir() do dir valid_username = "julia" valid_password = randstring(16) - https_cmd = """ - include("$LIBGIT2_HELPER_PATH") - valid_cred = LibGit2.UserPasswordCredentials("$valid_username", "$valid_password") - err, auth_attempts = credential_loop(valid_cred, "$url") - (err < 0 ? LibGit2.GitError(err) : err, auth_attempts) - """ + https_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + credential_loop(valid_cred, $url) + end # User provides a valid username and password challenges = [ "Username for 'https://github.com':" => "$valid_username\n", "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(https_ex, challenges) + @test err == git_ok @test auth_attempts == 1 # User sends EOF in username prompt which aborts the credential request challenges = [ "Username for 'https://github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1870,7 +1880,7 @@ mktempdir() do dir "Username for 'https://github.com':" => "foo\n", "Password for 'https://foo@github.com':" => "\x04", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1880,7 +1890,7 @@ mktempdir() do dir "Username for 'https://github.com':" => "foo\n", "Password for 'https://foo@github.com':" => "\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) + err, auth_attempts = challenge_prompt(https_ex, challenges) @test err == abort_prompt @test auth_attempts == 1 @@ -1892,10 +1902,196 @@ mktempdir() do dir "Username for 'https://github.com' [foo]:" => "$valid_username\n", "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] - err, auth_attempts = challenge_prompt(https_cmd, challenges) - @test err == 0 + err, auth_attempts = challenge_prompt(https_ex, challenges) + @test err == git_ok @test auth_attempts == 5 end + + @testset "SSH explicit credentials" begin + url = "git@github.com:test/package.jl" + + invalid_key = joinpath(KEY_DIR, "invalid") + valid_p_key = joinpath(KEY_DIR, "valid-passphrase") + username = "git" + passphrase = "secret" + + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, $url, $username, payload) + end + + invalid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials($username, $passphrase, $valid_p_key, $(valid_p_key * ".pub")) + invalid_cred = LibGit2.SSHCredentials($username, "", $invalid_key, $(invalid_key * ".pub")) + invalid_cred.usesshagent = "N" # Disable SSH agent use + payload = CredentialPayload(Nullable(invalid_cred)) + credential_loop(valid_cred, $url, $username, payload) + end + + # Explicitly provided credential is correct + err, auth_attempts = challenge_prompt(valid_ex, []) + @test err == git_ok + @test auth_attempts == 1 + + # Explicitly provided credential is incorrect + # TODO: Unless the SSH agent is disabled we may get caught in an infinite loop + err, auth_attempts = challenge_prompt(invalid_ex, []) + @test err == eauth_error + @test auth_attempts == 4 + end + + @testset "HTTPS explicit credentials" begin + url = "https://github.com/test/package.jl" + + valid_username = "julia" + valid_password = randstring(16) + invalid_username = "alice" + invalid_password = randstring(15) + + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, $url, "", payload) + end + + invalid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + invalid_cred = LibGit2.UserPasswordCredentials($invalid_username, $invalid_password) + payload = CredentialPayload(Nullable(invalid_cred)) + credential_loop(valid_cred, $url, "", payload) + end + + # Explicitly provided credential is correct + err, auth_attempts = challenge_prompt(valid_ex, []) + @test err == git_ok + @test auth_attempts == 1 + + # Explicitly provided credential is incorrect + err, auth_attempts = challenge_prompt(invalid_ex, []) + @test err == eauth_error + @test auth_attempts == 4 + end + + @testset "Cached credentials" begin + url = "https://github.com/test/package.jl" + cred_id = "https://github.com" + + valid_username = "julia" + valid_password = randstring(16) + invalid_username = "alice" + invalid_password = randstring(15) + + valid_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + cache = CachedCredentials() + LibGit2.get_creds!(cache, $cred_id, valid_cred) + payload = CredentialPayload(Nullable(cache)) + credential_loop(valid_cred, $url, "", payload) + end + + add_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + cache = CachedCredentials() + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, $url, "", payload) + (err, auth_attempts, cache) + end + + replace_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials($valid_username, $valid_password) + invalid_cred = LibGit2.UserPasswordCredentials($invalid_username, $invalid_password, true) + cache = CachedCredentials() + LibGit2.get_creds!(cache, $cred_id, invalid_cred) + payload = CredentialPayload(Nullable(cache)) + err, auth_attempts = credential_loop(valid_cred, $url, "", payload) + (err, auth_attempts, cache) + end + + # Cache contains a correct credential + err, auth_attempts = challenge_prompt(valid_ex, []) + @test err == git_ok + @test auth_attempts == 1 + + # Add a credential into the cache + challenges = [ + "Username for 'https://github.com':" => "$valid_username\n", + "Password for 'https://$valid_username@github.com':" => "$valid_password\n", + ] + expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) + err, auth_attempts, cache = challenge_prompt(add_ex, challenges) + @test err == git_ok + @test auth_attempts == 1 + @test typeof(cache) == LibGit2.CachedCredentials + @test cache.cred == Dict(cred_id => expected_cred) + + # Replace a credential in the cache + challenges = [ + "Username for 'https://github.com' [alice]:" => "$valid_username\n", + "Password for 'https://$valid_username@github.com':" => "$valid_password\n", + ] + expected_cred = LibGit2.UserPasswordCredentials(valid_username, valid_password) + err, auth_attempts, cache = challenge_prompt(replace_ex, challenges) + @test err == git_ok + @test auth_attempts == 4 + @test typeof(cache) == LibGit2.CachedCredentials + @test cache.cred == Dict(cred_id => expected_cred) + end + + @testset "Incompatible explicit credentials" begin + # User provides a user/password credential where a SSH credential is required. + expect_ssh_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "ssh://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) + end + + # User provides a SSH credential where a user/password credential is required. + expect_https_ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.SSHCredentials("foo", "", "", "") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "https://github.com/repo", Nullable(""), + Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) + end + + err, auth_attempts = challenge_prompt(expect_ssh_ex, []) + @test err == incompatible_error + @test auth_attempts == 1 + + err, auth_attempts = challenge_prompt(expect_https_ex, []) + @test err == incompatible_error + @test auth_attempts == 1 + end + + # A hypothetical scenario where the the allowed authentication can either be + # SSH or username/password. + @testset "SSH & HTTPS authentication" begin + allowed_types = Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY) | + Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT) + + # User provides a user/password credential where a SSH credential is required. + ex = quote + include($LIBGIT2_HELPER_PATH) + valid_cred = LibGit2.UserPasswordCredentials("foo", "bar") + payload = CredentialPayload(Nullable(valid_cred)) + credential_loop(valid_cred, "foo://github.com/repo", Nullable(""), + $allowed_types, payload) + end + + err, auth_attempts = challenge_prompt(ex, []) + @test err == git_ok + @test auth_attempts == 1 + end end #= temporarily disabled until working on the buildbots, ref https://github.com/JuliaLang/julia/pull/17651#issuecomment-238211150 diff --git a/test/linalg/bidiag.jl b/test/linalg/bidiag.jl index 7c9d85b43e5cc..c72e4f353f228 100644 --- a/test/linalg/bidiag.jl +++ b/test/linalg/bidiag.jl @@ -105,7 +105,7 @@ srand(1) @test Array(imag(T)) == imag(diagm(dv)) + imag(diagm(ev, uplo == :U ? 1 : -1)) end - @testset for func in (conj, transpose, ctranspose) + @testset for func in (conj, transpose, adjoint) @test func(func(T)) == T end @@ -286,7 +286,9 @@ end C = Tridiagonal(rand(Float64,9),rand(Float64,10),rand(Float64,9)) @test promote_rule(Matrix{Float64}, Bidiagonal{Float64}) == Matrix{Float64} @test promote(B,A) == (B, convert(Matrix{Float64}, A)) + @test promote(B,A) isa Tuple{Matrix{Float64}, Matrix{Float64}} @test promote(C,A) == (C,Tridiagonal(zeros(Float64,9),convert(Vector{Float64},A.dv),convert(Vector{Float64},A.ev))) + @test promote(C,A) isa Tuple{Tridiagonal, Tridiagonal} end import Base.LinAlg: fillslots!, UnitLowerTriangular diff --git a/test/linalg/blas.jl b/test/linalg/blas.jl index 56ebdde4ab6ff..1c637a10aac0b 100644 --- a/test/linalg/blas.jl +++ b/test/linalg/blas.jl @@ -74,13 +74,16 @@ srand(100) @test BLAS.iamax(z) == indmax(map(x -> abs(real(x)) + abs(imag(x)), z)) end end - @testset "axpy" begin + @testset "axp(b)y" begin if elty <: Real x1 = convert(Vector{elty}, randn(n)) x2 = convert(Vector{elty}, randn(n)) α = rand(elty) - @test BLAS.axpy!(α,copy(x1),copy(x2)) ≈ x2 + α*x1 + β = rand(elty) + @test BLAS.axpy!(α,copy(x1),copy(x2)) ≈ α*x1 + x2 + @test BLAS.axpby!(α,copy(x1),β,copy(x2)) ≈ α*x1 + β*x2 @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), rand(elty, n + 1)) + @test_throws DimensionMismatch BLAS.axpby!(α, copy(x1), β, rand(elty, n + 1)) @test_throws DimensionMismatch BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 1:n) @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 0:div(n,2), copy(x2), 1:(div(n, 2) + 1)) @test_throws ArgumentError BLAS.axpy!(α, copy(x1), 1:div(n,2), copy(x2), 0:(div(n, 2) - 1)) diff --git a/test/linalg/bunchkaufman.jl b/test/linalg/bunchkaufman.jl index d6879e68e5543..30847c6705f31 100644 --- a/test/linalg/bunchkaufman.jl +++ b/test/linalg/bunchkaufman.jl @@ -22,19 +22,14 @@ bimg = randn(n,2)/2 @testset for eltya in (Float32, Float64, Complex64, Complex128, Int) a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) a2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) - @testset for atype in ("Array", "SubArray") - asym = a.'+ a # symmetric indefinite - aher = a' + a # Hermitian indefinite - apd = a' * a # Positive-definite - if atype == "Array" - a = a - a2 = a2 - else - a = view(a , 1:n, 1:n) - a2 = view(a2 , 1:n, 1:n) - aher = view(aher, 1:n, 1:n) - apd = view(apd , 1:n, 1:n) - end + asym = a.'+ a # symmetric indefinite + aher = a' + a # Hermitian indefinite + apd = a' * a # Positive-definite + for (a, a2, aher, apd) in ((a, a2, aher, apd), + (view(a, 1:n, 1:n), + view(a2, 1:n, 1:n), + view(aher, 1:n, 1:n), + view(apd , 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) @testset for eltyb in (Float32, Float64, Complex64, Complex128, Int) @@ -44,13 +39,7 @@ bimg = randn(n,2)/2 @test isa(factorize(asym), LinAlg.BunchKaufman) @test isa(factorize(aher), LinAlg.BunchKaufman) - @testset for btype in ("Array", "SubArray") - if btype == "Array" - b = b - else - b = view(b, 1:n, 1:2) - end - + for b in (b, view(b, 1:n, 1:2)) εb = eps(abs(float(one(eltyb)))) ε = max(εa,εb) @@ -113,13 +102,7 @@ end As3[1, end] -= im for As = (As1, As2, As3) - @testset for Astype in ("Array", "SubArray") - if Astype == "Array" - As = As - else - As = view(As, 1:n, 1:n) - end - + for As in (As, view(As, 1:n, 1:n)) @testset for rook in (false, true) @testset for uplo in (:L, :U) F = bkfact(issymmetric(As) ? Symmetric(As, uplo) : Hermitian(As, uplo), rook) diff --git a/test/linalg/cholesky.jl b/test/linalg/cholesky.jl index cda0c07e8e1e8..3bc0694bb5a78 100644 --- a/test/linalg/cholesky.jl +++ b/test/linalg/cholesky.jl @@ -261,14 +261,14 @@ end end end -@testset "throw if non-Hermitian" begin +@testset "handling of non-Hermitian" begin R = randn(5, 5) C = complex.(R, R) for A in (R, C) - @test_throws ArgumentError cholfact(A) - @test_throws ArgumentError cholfact!(copy(A)) - @test_throws ArgumentError chol(A) - @test_throws ArgumentError Base.LinAlg.chol!(copy(A)) + @test !LinAlg.issuccess(cholfact(A)) + @test !LinAlg.issuccess(cholfact!(copy(A))) + @test_throws PosDefException chol(A) + @test_throws PosDefException Base.LinAlg.chol!(copy(A)) end end diff --git a/test/linalg/dense.jl b/test/linalg/dense.jl index 7eca457de6f2b..ad719ee0fb3cc 100644 --- a/test/linalg/dense.jl +++ b/test/linalg/dense.jl @@ -20,12 +20,7 @@ srand(1234321) ainit = rand(n,n) @testset "for $elty" for elty in (Float32, Float64, Complex64, Complex128) ainit = convert(Matrix{elty}, ainit) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - else - a = view(ainit, 1:n, 1:n) - end + for a in (copy(ainit), view(ainit, 1:n, 1:n)) @test cond(a,1) ≈ 4.837320054554436e+02 atol=0.01 @test cond(a,2) ≈ 1.960057871514615e+02 atol=0.01 @test cond(a,Inf) ≈ 3.757017682707787e+02 atol=0.01 @@ -60,23 +55,14 @@ bimg = randn(n,2)/2 binit = eltyb == Int ? rand(1:5, n, 2) : convert(Matrix{eltyb}, eltyb <: Complex ? complex.(breal, bimg) : breal) εb = eps(abs(float(one(eltyb)))) ε = max(εa,εb) - - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - b = binit - else - a = view(ainit, 1:n, 1:n) - b = view(binit, 1:n, 1:2) - end - + for (a, b) in ((copy(ainit), copy(binit)), (view(ainit, 1:n, 1:n), view(binit, 1:n, 1:2))) @testset "Solve square general system of equations" begin κ = cond(a,1) x = a \ b @test_throws DimensionMismatch b'\b @test_throws DimensionMismatch b\b' @test norm(a*x - b, 1)/norm(b) < ε*κ*n*2 # Ad hoc, revisit! - @test zeros(eltya,n)\ones(eltya,n) ≈ zeros(eltya,n,1)\ones(eltya,n,1) + @test zeros(eltya,n)\ones(eltya,n) ≈ (zeros(eltya,n,1)\ones(eltya,n,1))[1,1] end @testset "Test nullspace" begin @@ -90,15 +76,7 @@ bimg = randn(n,2)/2 end end # for eltyb - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - a2 = ainit2 - else - a = view(ainit, 1:n, 1:n) - a2 = view(ainit2, 1:n, 1:n) - end - + for (a, a2) in ((copy(ainit), copy(ainit2)), (view(ainit, 1:n, 1:n), view(ainit2, 1:n, 1:n))) @testset "Test pinv" begin pinva15 = pinv(a[:,1:n1]) @test a[:,1:n1]*pinva15*a[:,1:n1] ≈ a[:,1:n1] @@ -115,10 +93,10 @@ bimg = randn(n,2)/2 end @testset "Matrix square root" begin - asq = sqrtm(a) + asq = sqrt(a) @test asq*asq ≈ a asym = a'+a # symmetric indefinite - asymsq = sqrtm(asym) + asymsq = sqrt(asym) @test asymsq*asymsq ≈ asym end @@ -139,7 +117,6 @@ bimg = randn(n,2)/2 A = zeros(eltya,1,1) A[1,1] = α @test diagm(α) == A # Test behavior of `diagm` when passed a scalar - @test expm(α) == exp(α) # `expm` should behave like `exp` with scalar argument end @testset "Factorize" begin @@ -170,14 +147,9 @@ end # for eltya @testset "test triu/tril bounds checking" begin ainit = rand(5,7) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - a = ainit - else - a = view(ainit, 1:size(ainit, 1), 1:size(ainit, 2)) - end - @test_throws(ArgumentError,triu(a,8)) + for a in (copy(ainit), view(ainit, 1:size(ainit, 1), 1:size(ainit, 2))) @test_throws(ArgumentError,triu(a,-6)) + @test_throws(ArgumentError,triu(a,8)) @test_throws(ArgumentError,tril(a,8)) @test_throws(ArgumentError,tril(a,-6)) end @@ -254,14 +226,7 @@ end α = elty <: Integer ? randn() : elty <: Complex ? convert(elty, complex(randn(),randn())) : convert(elty, randn()) - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - x = xinit - y = yinit - else - x = view(xinit,1:2:nnorm) - y = view(yinit,1:2:nnorm) - end + for (x, y) in ((copy(xinit), copy(yinit)), (view(xinit,1:2:nnorm), view(yinit,1:2:nnorm))) # Absolute homogeneity @test norm(α*x,-Inf) ≈ abs(α)*norm(x,-Inf) @test norm(α*x,-1) ≈ abs(α)*norm(x,-1) @@ -310,16 +275,7 @@ end α = elty <: Integer ? randn() : elty <: Complex ? convert(elty, complex(randn(),randn())) : convert(elty, randn()) - - for arraytype in ("Array", "SubArray") - if arraytype == "Array" - A = Ainit - B = Binit - else - A = view(Ainit,1:nmat,1:nmat) - B = view(Binit,1:nmat,1:nmat) - end - + for (A, B) in ((copy(Ainit), copy(Binit)), (view(Ainit,1:nmat,1:nmat), view(Binit,1:nmat,1:nmat))) # Absolute homogeneity @test norm(α*A,1) ≈ abs(α)*norm(A,1) elty <: Union{BigFloat,Complex{BigFloat},BigInt} || @test norm(α*A) ≈ abs(α)*norm(A) # two is default @@ -371,10 +327,10 @@ end @testset "issue #2246" begin A = [1 2 0 0; 0 1 0 0; 0 0 0 0; 0 0 0 0] - Asq = sqrtm(A) + Asq = sqrt(A) @test Asq*Asq ≈ A A2 = view(A, 1:2, 1:2) - A2sq = sqrtm(A2) + A2sq = sqrt(A2) @test A2sq*A2sq ≈ A2 N = 3 @@ -416,7 +372,7 @@ end eA1 = convert(Matrix{elty}, [147.866622446369 127.781085523181 127.781085523182; 183.765138646367 183.765138646366 163.679601723179; 71.797032399996 91.8825693231832 111.968106246371]') - @test expm(A1) ≈ eA1 + @test exp(A1) ≈ eA1 A2 = convert(Matrix{elty}, [29.87942128909879 0.7815750847907159 -2.289519314033932; @@ -426,21 +382,21 @@ end [ 5496313853692458.0 -18231880972009236.0 -30475770808580460.0; -18231880972009252.0 60605228702221920.0 101291842930249760.0; -30475770808580480.0 101291842930249728.0 169294411240851968.0]) - @test expm(A2) ≈ eA2 + @test exp(A2) ≈ eA2 A3 = convert(Matrix{elty}, [-131 19 18;-390 56 54;-387 57 52]) eA3 = convert(Matrix{elty}, [-1.50964415879218 -5.6325707998812 -4.934938326092; 0.367879439109187 1.47151775849686 1.10363831732856; 0.135335281175235 0.406005843524598 0.541341126763207]') - @test expm(A3) ≈ eA3 + @test exp(A3) ≈ eA3 A4 = convert(Matrix{elty}, [0.25 0.25; 0 0]) eA4 = convert(Matrix{elty}, [1.2840254166877416 0.2840254166877415; 0 1]) - @test expm(A4) ≈ eA4 + @test exp(A4) ≈ eA4 A5 = convert(Matrix{elty}, [0 0.02; 0 0]) eA5 = convert(Matrix{elty}, [1 0.02; 0 1]) - @test expm(A5) ≈ eA5 + @test exp(A5) ≈ eA5 # Hessenberg @test hessfact(A1)[:H] ≈ convert(Matrix{elty}, @@ -454,20 +410,20 @@ end 1/3 1/4 1/5 1/6; 1/4 1/5 1/6 1/7; 1/5 1/6 1/7 1/8]) - @test expm(logm(A4)) ≈ A4 + @test exp(log(A4)) ≈ A4 A5 = convert(Matrix{elty}, [1 1 0 1; 0 1 1 0; 0 0 1 1; 1 0 0 1]) - @test expm(logm(A5)) ≈ A5 + @test exp(log(A5)) ≈ A5 A6 = convert(Matrix{elty}, [-5 2 0 0 ; 1/2 -7 3 0; 0 1/3 -9 4; 0 0 1/4 -11]) - @test expm(logm(A6)) ≈ A6 + @test exp(log(A6)) ≈ A6 A7 = convert(Matrix{elty}, [1 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1]) - @test expm(logm(A7)) ≈ A7 + @test exp(log(A7)) ≈ A7 end A8 = 100 * [-1+1im 0 0 1e-8; 0 1 0 0; 0 0 1 0; 0 0 0 1] - @test expm(logm(A8)) ≈ A8 + @test exp(log(A8)) ≈ A8 end @testset "issue 5116" begin @@ -476,40 +432,40 @@ end 0.006540706968939 -0.999786072879326 0.0 0.0 0.0 0.0 1.0 0.0 0.013081413937878 -3.999572145758650 0.0 1.0] - @test expm(A9) ≈ eA9 + @test exp(A9) ≈ eA9 A10 = [ 0. 0. 0. 0. ; 0. 0. -im 0.; 0. im 0. 0.; 0. 0. 0. 0.] eA10 = [ 1.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.543080634815244+0.0im 0.0-1.175201193643801im 0.0+0.0im 0.0+0.0im 0.0+1.175201193643801im 1.543080634815243+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im] - @test expm(A10) ≈ eA10 + @test exp(A10) ≈ eA10 end @testset "Additional matrix logarithm tests" for elty in (Float64, Complex{Float64}) A11 = convert(Matrix{elty}, [3 2; -5 -3]) - @test expm(logm(A11)) ≈ A11 + @test exp(log(A11)) ≈ A11 A12 = convert(Matrix{elty}, [1 -1; 1 -1]) - @test typeof(logm(A12)) == Array{Complex{Float64}, 2} + @test typeof(log(A12)) == Array{Complex{Float64}, 2} A1 = convert(Matrix{elty}, [4 2 0; 1 4 1; 1 1 4]) - logmA1 = convert(Matrix{elty}, [1.329661349 0.5302876358 -0.06818951543; + logA1 = convert(Matrix{elty}, [1.329661349 0.5302876358 -0.06818951543; 0.2310490602 1.295566591 0.2651438179; 0.2310490602 0.1969543025 1.363756107]) - @test logm(A1) ≈ logmA1 - @test expm(logm(A1)) ≈ A1 + @test log(A1) ≈ logA1 + @test exp(log(A1)) ≈ A1 A4 = convert(Matrix{elty}, [1/2 1/3 1/4 1/5+eps(); 1/3 1/4 1/5 1/6; 1/4 1/5 1/6 1/7; 1/5 1/6 1/7 1/8]) - logmA4 = convert(Matrix{elty}, [-1.73297159 1.857349738 0.4462766564 0.2414170219; + logA4 = convert(Matrix{elty}, [-1.73297159 1.857349738 0.4462766564 0.2414170219; 1.857349738 -5.335033737 2.994142974 0.5865285289; 0.4462766564 2.994142974 -7.351095988 3.318413247; 0.2414170219 0.5865285289 3.318413247 -5.444632124]) - @test logm(A4) ≈ logmA4 - @test expm(logm(A4)) ≈ A4 + @test log(A4) ≈ logA4 + @test exp(log(A4)) ≈ A4 end @testset "issue #7181" begin @@ -570,18 +526,46 @@ end end for A in (Aa, Ab, Ac, Ad, Ah, ADi) - @test A^(1/2) ≈ sqrtm(A) - @test A^(-1/2) ≈ inv(sqrtm(A)) - @test A^(3/4) ≈ sqrtm(A) * sqrtm(sqrtm(A)) - @test A^(-3/4) ≈ inv(A) * sqrtm(sqrtm(A)) - @test A^(17/8) ≈ A^2 * sqrtm(sqrtm(sqrtm(A))) - @test A^(-17/8) ≈ inv(A^2 * sqrtm(sqrtm(sqrtm(A)))) + @test A^(1/2) ≈ sqrt(A) + @test A^(-1/2) ≈ inv(sqrt(A)) + @test A^(3/4) ≈ sqrt(A) * sqrt(sqrt(A)) + @test A^(-3/4) ≈ inv(A) * sqrt(sqrt(A)) + @test A^(17/8) ≈ A^2 * sqrt(sqrt(sqrt(A))) + @test A^(-17/8) ≈ inv(A^2 * sqrt(sqrt(sqrt(A)))) @test (A^0.2)^5 ≈ A @test (A^(2/3))*(A^(1/3)) ≈ A @test (A^im)^(-im) ≈ A end end +@testset "diagonal integer matrix to real power" begin + A = Matrix(Diagonal([1, 2, 3])) + @test A^2.3 ≈ float(A)^2.3 +end + +@testset "issue #23366 (Int Matrix to Int power)" begin + @testset "Tests for $elty" for elty in (Int128, Int16, Int32, Int64, Int8, + UInt128, UInt16, UInt32, UInt64, UInt8, + BigInt) + @test_throws DomainError elty[1 1;1 0]^-2 + @test (@inferred elty[1 1;1 0]^2) == elty[2 1;1 1] + I_ = elty[1 0;0 1] + @test I_^-1 == I_ + if !(elty<:Unsigned) + @test (@inferred (-I_)^-1) == -I_ + @test (@inferred (-I_)^-2) == I_ + end + # make sure that type promotion for ^(::Matrix{<:Integer}, ::Integer) + # is analogous to type promotion for ^(::Integer, ::Integer) + # e.g. [1 1;1 0]^big(10000) should return Matrix{BigInt}, the same + # way as 2^big(10000) returns BigInt + for elty2 = (Int64, BigInt) + TT = Base.promote_op(^, elty, elty2) + @test (@inferred elty[1 1;1 0]^elty2(1))::Matrix{TT} == [1 1;1 0] + end + end +end + @testset "Least squares solutions" begin a = [ones(20) 1:20 1:20] b = reshape(eye(8, 5), 20, 2) @@ -613,12 +597,46 @@ end end end +function test_rdiv_pinv_consistency(a, b) + @test (a*b)/b ≈ a*(b/b) ≈ (a*b)*pinv(b) ≈ a*(b*pinv(b)) + @test typeof((a*b)/b) == typeof(a*(b/b)) == typeof((a*b)*pinv(b)) == typeof(a*(b*pinv(b))) +end +function test_ldiv_pinv_consistency(a, b) + @test a\(a*b) ≈ (a\a)*b ≈ (pinv(a)*a)*b ≈ pinv(a)*(a*b) + @test typeof(a\(a*b)) == typeof((a\a)*b) == typeof((pinv(a)*a)*b) == typeof(pinv(a)*(a*b)) +end +function test_div_pinv_consistency(a, b) + test_rdiv_pinv_consistency(a, b) + test_ldiv_pinv_consistency(a, b) +end + +@testset "/ and \\ consistency with pinv for vectors" begin + @testset "Tests for type $elty" for elty in (Float32, Float64, Complex64, Complex128) + c = rand(elty, 5) + r = rand(elty, 5)' + cm = rand(elty, 5, 1) + rm = rand(elty, 1, 5) + @testset "inner prodcuts" begin + test_div_pinv_consistency(r, c) + test_div_pinv_consistency(rm, c) + test_div_pinv_consistency(r, cm) + test_div_pinv_consistency(rm, cm) + end + @testset "outer prodcuts" begin + test_div_pinv_consistency(c, r) + test_div_pinv_consistency(cm, rm) + end + @testset "matrix/vector" begin + m = rand(5, 5) + test_ldiv_pinv_consistency(m, c) + test_rdiv_pinv_consistency(r, m) + end + end +end + @testset "test ops on Numbers for $elty" for elty in [Float32,Float64,Complex64,Complex128] a = rand(elty) - @test expm(a) == exp(a) @test isposdef(one(elty)) - @test sqrtm(a) == sqrt(a) - @test logm(a) ≈ log(a) @test lyap(one(elty),a) == -a/2 end diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index b1e8df0bd3ffb..ec7b4f9338a8e 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -8,19 +8,19 @@ n=12 #Size of matrix problem to test srand(1) @testset for relty in (Float32, Float64, BigFloat), elty in (relty, Complex{relty}) - d=convert(Vector{elty}, randn(n)) - v=convert(Vector{elty}, randn(n)) - U=convert(Matrix{elty}, randn(n,n)) + dd=convert(Vector{elty}, randn(n)) + vv=convert(Vector{elty}, randn(n)) + UU=convert(Matrix{elty}, randn(n,n)) if elty <: Complex - d+=im*convert(Vector{elty}, randn(n)) - v+=im*convert(Vector{elty}, randn(n)) - U+=im*convert(Matrix{elty}, randn(n,n)) + dd+=im*convert(Vector{elty}, randn(n)) + vv+=im*convert(Vector{elty}, randn(n)) + UU+=im*convert(Matrix{elty}, randn(n,n)) end - D = Diagonal(d) - DM = diagm(d) + D = Diagonal(dd) + DM = diagm(dd) @testset "constructor" begin - for x in (d, GenericArray(d)) + for x in (dd, GenericArray(dd)) @test Diagonal(x)::Diagonal{elty,typeof(x)} == DM @test Diagonal(x).diag === x @test Diagonal{elty}(x)::Diagonal{elty,typeof(x)} == DM @@ -38,9 +38,9 @@ srand(1) @test Array(abs.(D)) == abs.(DM) @test Array(imag(D)) == imag(DM) - @test parent(D) == d - @test diag(D) == d - @test D[1,1] == d[1] + @test parent(D) == dd + @test diag(D) == dd + @test D[1,1] == dd[1] @test D[1,2] == 0 @test issymmetric(D) @@ -60,74 +60,62 @@ srand(1) @test func(D) ≈ func(DM) atol=n^2*eps(relty)*(1+(elty<:Complex)) end if relty <: BlasFloat - for func in (expm,) + for func in (exp,) @test func(D) ≈ func(DM) atol=n^3*eps(relty) end - @test logm(Diagonal(abs.(D.diag))) ≈ logm(abs.(DM)) atol=n^3*eps(relty) + @test log(Diagonal(abs.(D.diag))) ≈ log(abs.(DM)) atol=n^3*eps(relty) end if elty <: BlasComplex - for func in (logdet, sqrtm) + for func in (logdet, sqrt) @test func(D) ≈ func(DM) atol=n^2*eps(relty)*2 end end end @testset "Linear solve" begin - let vv = v, UU = U - @testset for atype in ("Array", "SubArray") - if atype == "Array" - v = vv - U = UU - else - v = view(vv, 1:n) - U = view(UU, 1:n, 1:2) - end - - @test D*v ≈ DM*v atol=n*eps(relty)*(1+(elty<:Complex)) - @test D*U ≈ DM*U atol=n^2*eps(relty)*(1+(elty<:Complex)) - - @test U.'*D ≈ U.'*Array(D) - @test U'*D ≈ U'*Array(D) - - if relty != BigFloat - atol_two = 2n^2 * eps(relty) * (1 + (elty <: Complex)) - atol_three = 2n^3 * eps(relty) * (1 + (elty <: Complex)) - @test D\v ≈ DM\v atol=atol_two - @test D\U ≈ DM\U atol=atol_three - @test A_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two - @test At_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two - @test Ac_ldiv_B!(conj(D), copy(v)) ≈ DM\v atol=atol_two - @test A_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three - @test At_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three - @test Ac_ldiv_B!(conj(D), copy(U)) ≈ DM\U atol=atol_three - Uc = ctranspose(U) - target = scale!(Uc, inv.(D.diag)) - @test A_rdiv_B!(Uc, D) ≈ target atol=atol_three - @test_throws DimensionMismatch A_rdiv_B!(eye(elty, n-1), D) - @test_throws SingularException A_rdiv_B!(Uc, zeros(D)) - @test A_rdiv_Bt!(Uc, D) ≈ target atol=atol_three - @test A_rdiv_Bc!(Uc, conj(D)) ≈ target atol=atol_three - @test A_ldiv_B!(D, eye(D)) ≈ D\eye(D) atol=atol_three - @test_throws DimensionMismatch A_ldiv_B!(D, ones(elty, n + 1)) - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(relty, n)), copy(v)) - b = rand(elty, n, n) - b = sparse(b) - @test A_ldiv_B!(D, copy(b)) ≈ Array(D)\Array(b) - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), copy(b)) - b = view(rand(elty, n), collect(1:n)) - b2 = copy(b) - c = A_ldiv_B!(D, b) - d = Array(D)\b2 - for i in 1:n - @test c[i] ≈ d[i] - end - @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), b) - b = rand(elty, n+1, n+1) - b = sparse(b) - @test_throws DimensionMismatch A_ldiv_B!(D, copy(b)) - b = view(rand(elty, n+1), collect(1:n+1)) - @test_throws DimensionMismatch A_ldiv_B!(D, b) - end + for (v, U) in ((vv, UU), (view(vv, 1:n), view(UU, 1:n, 1:2))) + @test D*v ≈ DM*v atol=n*eps(relty)*(1+(elty<:Complex)) + @test D*U ≈ DM*U atol=n^2*eps(relty)*(1+(elty<:Complex)) + + @test U.'*D ≈ U.'*Array(D) + @test U'*D ≈ U'*Array(D) + + if relty != BigFloat + atol_two = 2n^2 * eps(relty) * (1 + (elty <: Complex)) + atol_three = 2n^3 * eps(relty) * (1 + (elty <: Complex)) + @test D\v ≈ DM\v atol=atol_two + @test D\U ≈ DM\U atol=atol_three + @test A_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two + @test At_ldiv_B!(D, copy(v)) ≈ DM\v atol=atol_two + @test Ac_ldiv_B!(conj(D), copy(v)) ≈ DM\v atol=atol_two + @test A_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three + @test At_ldiv_B!(D, copy(U)) ≈ DM\U atol=atol_three + @test Ac_ldiv_B!(conj(D), copy(U)) ≈ DM\U atol=atol_three + Uc = adjoint(U) + target = scale!(Uc, inv.(D.diag)) + @test A_rdiv_B!(Uc, D) ≈ target atol=atol_three + @test_throws DimensionMismatch A_rdiv_B!(eye(elty, n-1), D) + @test_throws SingularException A_rdiv_B!(Uc, zeros(D)) + @test A_rdiv_Bt!(Uc, D) ≈ target atol=atol_three + @test A_rdiv_Bc!(Uc, conj(D)) ≈ target atol=atol_three + @test A_ldiv_B!(D, eye(D)) ≈ D\eye(D) atol=atol_three + @test_throws DimensionMismatch A_ldiv_B!(D, ones(elty, n + 1)) + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(relty, n)), copy(v)) + b = rand(elty, n, n) + b = sparse(b) + @test A_ldiv_B!(D, copy(b)) ≈ Array(D)\Array(b) + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), copy(b)) + b = view(rand(elty, n), collect(1:n)) + b2 = copy(b) + c = A_ldiv_B!(D, b) + d = Array(D)\b2 + @test c ≈ d + @test_throws SingularException A_ldiv_B!(Diagonal(zeros(elty, n)), b) + b = rand(elty, n+1, n+1) + b = sparse(b) + @test_throws DimensionMismatch A_ldiv_B!(D, copy(b)) + b = view(rand(elty, n+1), collect(1:n+1)) + @test_throws DimensionMismatch A_ldiv_B!(D, b) end end end @@ -163,15 +151,15 @@ srand(1) @test D\D2 ≈ Diagonal(D2.diag./D.diag) # Performance specialisations for A*_mul_B! - vv = similar(v) - @test (r = full(D) * v ; A_mul_B!(vv, D, v) ≈ r ≈ vv) - @test (r = full(D)' * v ; Ac_mul_B!(vv, D, v) ≈ r ≈ vv) - @test (r = full(D).' * v ; At_mul_B!(vv, D, v) ≈ r ≈ vv) + vvv = similar(vv) + @test (r = full(D) * vv ; A_mul_B!(vvv, D, vv) ≈ r ≈ vvv) + @test (r = full(D)' * vv ; Ac_mul_B!(vvv, D, vv) ≈ r ≈ vvv) + @test (r = full(D).' * vv ; At_mul_B!(vvv, D, vv) ≈ r ≈ vvv) - UU = similar(U) - @test (r = full(D) * U ; A_mul_B!(UU, D, U) ≈ r ≈ UU) - @test (r = full(D)' * U ; Ac_mul_B!(UU, D, U) ≈ r ≈ UU) - @test (r = full(D).' * U ; At_mul_B!(UU, D, U) ≈ r ≈ UU) + UUU = similar(UU) + @test (r = full(D) * UU ; A_mul_B!(UUU, D, UU) ≈ r ≈ UUU) + @test (r = full(D)' * UU ; Ac_mul_B!(UUU, D, UU) ≈ r ≈ UUU) + @test (r = full(D).' * UU ; At_mul_B!(UUU, D, UU) ≈ r ≈ UUU) # make sure that A_mul_B{c,t}! works with B as a Diagonal VV = Array(D) @@ -222,11 +210,11 @@ srand(1) @test transpose(D) == D if elty <: BlasComplex @test Array(conj(D)) ≈ conj(DM) - @test ctranspose(D) == conj(D) + @test adjoint(D) == conj(D) end # Translates to Ac/t_mul_B, which is specialized after issue 21286 - @test(D' * v == conj(D) * v) - @test(D.' * v == D * v) + @test(D' * vv == conj(D) * vv) + @test(D.' * vv == D * vv) end #logdet @@ -381,9 +369,9 @@ end @test ishermitian(Dherm) == true @test ishermitian(Dsym) == false - @test expm(D) == Diagonal([expm([1 2; 3 4]), expm([1 2; 3 4])]) - @test logm(D) == Diagonal([logm([1 2; 3 4]), logm([1 2; 3 4])]) - @test sqrtm(D) == Diagonal([sqrtm([1 2; 3 4]), sqrtm([1 2; 3 4])]) + @test exp(D) == Diagonal([exp([1 2; 3 4]), exp([1 2; 3 4])]) + @test log(D) == Diagonal([log([1 2; 3 4]), log([1 2; 3 4])]) + @test sqrt(D) == Diagonal([sqrt([1 2; 3 4]), sqrt([1 2; 3 4])]) end @testset "multiplication with Symmetric/Hermitian" begin diff --git a/test/linalg/eigen.jl b/test/linalg/eigen.jl index 5e6ec48db16f2..edfa42c7e7991 100644 --- a/test/linalg/eigen.jl +++ b/test/linalg/eigen.jl @@ -19,14 +19,10 @@ aimg = randn(n,n)/2 aa = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) asym = aa'+aa # symmetric indefinite apd = aa'*aa # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - else - a = view(aa, 1:n, 1:n) - asym = view(asym, 1:n, 1:n) - apd = view(apd, 1:n, 1:n) - end + for (a, asym, apd) in ((aa, asym, apd), + (view(aa, 1:n, 1:n), + view(asym, 1:n, 1:n), + view(apd, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) α = rand(eltya) @@ -56,7 +52,7 @@ aimg = randn(n,n)/2 @test_throws DomainError eigmax(a - a') end @testset "symmetric generalized eigenproblem" begin - if atype == "Array" + if isa(a, Array) asym_sg = asym[1:n1, 1:n1] a_sg = a[:,n1+1:n2] else @@ -77,7 +73,7 @@ aimg = randn(n,n)/2 @test v == f[:vectors] end @testset "Non-symmetric generalized eigenproblem" begin - if atype == "Array" + if isa(a, Array) a1_nsg = a[1:n1, 1:n1] a2_nsg = a[n1+1:n2, n1+1:n2] else @@ -110,12 +106,7 @@ end # test a matrix larger than 140-by-140 for #14174 let aa = rand(200, 200) - for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - else - a = view(aa, 1:n, 1:n) - end + for a in (aa, view(aa, 1:n, 1:n)) f = eigfact(a) @test a ≈ f[:vectors] * Diagonal(f[:values]) / f[:vectors] end diff --git a/test/linalg/generic.jl b/test/linalg/generic.jl index 4b92bf758c6fe..aa340bc112947 100644 --- a/test/linalg/generic.jl +++ b/test/linalg/generic.jl @@ -160,8 +160,9 @@ end @testset "generic axpy" begin x = ['a','b','c','d','e'] y = ['a','b','c','d','e'] - α = 'f' + α, β = 'f', 'g' @test_throws DimensionMismatch Base.LinAlg.axpy!(α,x,['g']) + @test_throws DimensionMismatch Base.LinAlg.axpby!(α,x,β,['g']) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(-1:5),y,collect(1:7)) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(1:7),y,collect(-1:5)) @test_throws BoundsError Base.LinAlg.axpy!(α,x,collect(1:7),y,collect(1:7)) @@ -276,12 +277,17 @@ end @test norm(x, 3) ≈ cbrt(sqrt(125)+125) end -@testset "LinAlg.axpy! for element type without commutative multiplication" begin - α = ones(Int, 2, 2) - x = fill([1 0; 1 1], 3) - y = fill(zeros(Int, 2, 2), 3) - @test LinAlg.axpy!(α, x, deepcopy(y)) == x .* Matrix{Int}[α] - @test LinAlg.axpy!(α, x, deepcopy(y)) != Matrix{Int}[α] .* x +@testset "LinAlg.axp(b)y! for element type without commutative multiplication" begin + α = [1 2; 3 4] + β = [5 6; 7 8] + x = fill([ 9 10; 11 12], 3) + y = fill([13 14; 15 16], 3) + axpy = LinAlg.axpy!(α, x, deepcopy(y)) + axpby = LinAlg.axpby!(α, x, β, deepcopy(y)) + @test axpy == x .* [α] .+ y + @test axpy != [α] .* x .+ y + @test axpby == x .* [α] .+ y .* [β] + @test axpby != [α] .* x .+ [β] .* y end @testset "LinAlg.axpy! for x and y of different dimensions" begin @@ -371,6 +377,6 @@ Base.transpose(a::ModInt{n}) where {n} = a # see Issue 20978 end @testset "fallback throws properly for AbstractArrays with dimension > 2" begin - @test_throws ErrorException ctranspose(rand(2,2,2,2)) + @test_throws ErrorException adjoint(rand(2,2,2,2)) @test_throws ErrorException transpose(rand(2,2,2,2)) end diff --git a/test/linalg/givens.jl b/test/linalg/givens.jl index 49e0158491a1d..c13ddd2fcbb3b 100644 --- a/test/linalg/givens.jl +++ b/test/linalg/givens.jl @@ -22,8 +22,8 @@ using Base.Test @test A_mul_B!(G,eye(elty,10,10)) == [G[i,j] for i=1:10,j=1:10] @testset "transposes" begin - @test ctranspose(G)*G*eye(10) ≈ eye(elty, 10) - @test ctranspose(R)*(R*eye(10)) ≈ eye(elty, 10) + @test adjoint(G)*G*eye(10) ≈ eye(elty, 10) + @test adjoint(R)*(R*eye(10)) ≈ eye(elty, 10) @test_throws ErrorException transpose(G) @test_throws ErrorException transpose(R) end @@ -38,9 +38,9 @@ using Base.Test @test norm(R*eye(elty, 10)) ≈ one(elty) G, _ = givens(one(elty),zero(elty),9,10) - @test ctranspose(G*eye(elty,10))*(G*eye(elty,10)) ≈ eye(elty, 10) + @test adjoint(G*eye(elty,10))*(G*eye(elty,10)) ≈ eye(elty, 10) K, _ = givens(zero(elty),one(elty),9,10) - @test ctranspose(K*eye(elty,10))*(K*eye(elty,10)) ≈ eye(elty, 10) + @test adjoint(K*eye(elty,10))*(K*eye(elty,10)) ≈ eye(elty, 10) @testset "Givens * vectors" begin if isa(A, Array) diff --git a/test/linalg/lapack.jl b/test/linalg/lapack.jl index 7d5d92b5204db..91bbf937e40e2 100644 --- a/test/linalg/lapack.jl +++ b/test/linalg/lapack.jl @@ -172,7 +172,7 @@ end @test V' ≈ lVt B = rand(elty,10,10) # xggsvd3 replaced xggsvd in LAPACK 3.6.0 - if LAPACK.laver() < (3, 6, 0) + if LAPACK.version() < v"3.6.0" @test_throws DimensionMismatch LAPACK.ggsvd!('S','S','S',A,B) else @test_throws DimensionMismatch LAPACK.ggsvd3!('S','S','S',A,B) @@ -613,7 +613,7 @@ end # Issue 13976 let A = [NaN 0.0 NaN; 0 0 0; NaN 0 NaN] - @test_throws ArgumentError expm(A) + @test_throws ArgumentError exp(A) end # Issue 14065 (and 14220) diff --git a/test/linalg/lu.jl b/test/linalg/lu.jl index 6730a6cd07fd6..bcb09e2bf9ead 100644 --- a/test/linalg/lu.jl +++ b/test/linalg/lu.jl @@ -69,26 +69,38 @@ dimg = randn(n)/2 lstring = sprint(show,l) ustring = sprint(show,u) @test sprint(show,lua) == "$(typeof(lua)) with factors L and U:\n$lstring\n$ustring\nsuccessful: true" - let Bs = b, Cs = c - @testset for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - c = Cs - else - b = view(Bs, 1:n, 1) - c = view(Cs, 1:n) - end - @test norm(a*(lua\b) - b, 1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a'*(lua'\b) - b, 1) < ε*κ*n*2 # Two because the right hand side has two columns + let Bs = copy(b), Cs = copy(c) + for (bb, cc) in ((Bs, Cs), (view(Bs, 1:n, 1), view(Cs, 1:n))) + @test norm(a*(lua\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns + @test norm(a'*(lua'\bb) - bb, 1) < ε*κ*n*2 # Two because the right hand side has two columns @test norm(a'*(lua'\a') - a', 1) < ε*κ*n^2 - @test norm(a*(lua\c) - c, 1) < ε*κ*n # c is a vector - @test norm(a'*(lua'\c) - c, 1) < ε*κ*n # c is a vector + @test norm(a*(lua\cc) - cc, 1) < ε*κ*n # cc is a vector + @test norm(a'*(lua'\cc) - cc, 1) < ε*κ*n # cc is a vector @test AbstractArray(lua) ≈ a - if eltya <: Real && eltyb <: Real - @test norm(a.'*(lua.'\b) - b,1) < ε*κ*n*2 # Two because the right hand side has two columns - @test norm(a.'*(lua.'\c) - c,1) < ε*κ*n - end + @test norm(a.'*(lua.'\bb) - bb,1) < ε*κ*n*2 # Two because the right hand side has two columns + @test norm(a.'*(lua.'\cc) - cc,1) < ε*κ*n end + + # Test whether Ax_ldiv_B!(y, LU, x) indeed overwrites y + resultT = typeof(oneunit(eltyb) / oneunit(eltya)) + + b_dest = similar(b, resultT) + c_dest = similar(c, resultT) + + A_ldiv_B!(b_dest, lua, b) + A_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua \ b, 1) < ε*κ*2n + @test norm(c_dest - lua \ c, 1) < ε*κ*n + + At_ldiv_B!(b_dest, lua, b) + At_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua.' \ b, 1) < ε*κ*2n + @test norm(c_dest - lua.' \ c, 1) < ε*κ*n + + Ac_ldiv_B!(b_dest, lua, b) + Ac_ldiv_B!(c_dest, lua, c) + @test norm(b_dest - lua' \ b, 1) < ε*κ*2n + @test norm(c_dest - lua' \ c, 1) < ε*κ*n end if eltya <: BlasFloat && eltyb <: BlasFloat e = rand(eltyb,n,n) @@ -109,23 +121,17 @@ dimg = randn(n)/2 @test_throws DimensionMismatch lud.'\f @test_throws DimensionMismatch lud'\f @test_throws DimensionMismatch Base.LinAlg.At_ldiv_B!(lud, f) - let Bs = b - @testset for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - else - b = view(Bs, 1:n, 1) - end - - @test norm(d*(lud\b) - b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + let Bs = copy(b) + for bb in (Bs, view(Bs, 1:n, 1)) + @test norm(d*(lud\bb) - bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns if eltya <: Real - @test norm((lud.'\b) - Array(d.')\b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + @test norm((lud.'\bb) - Array(d.')\bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns if eltya != Int && eltyb != Int - @test norm(Base.LinAlg.At_ldiv_B!(lud, copy(b)) - Array(d.')\b, 1) < ε*κd*n*2 + @test norm(Base.LinAlg.At_ldiv_B!(lud, copy(bb)) - Array(d.')\bb, 1) < ε*κd*n*2 end end if eltya <: Complex - @test norm((lud'\b) - Array(d')\b, 1) < ε*κd*n*2 # Two because the right hand side has two columns + @test norm((lud'\bb) - Array(d')\bb, 1) < ε*κd*n*2 # Two because the right hand side has two columns end end end @@ -182,12 +188,7 @@ end @test l[invperm(p),:]*u ≈ a @test a*inv(lua) ≈ eye(n) let Bs = b - for atype in ("Array", "SubArray") - if atype == "Array" - b = Bs - else - b = view(Bs, 1:n, 1) - end + for b in (Bs, view(Bs, 1:n, 1)) @test a*(lua\b) ≈ b end end diff --git a/test/linalg/matmul.jl b/test/linalg/matmul.jl index 406d306dd49cc..3b8f6d18257e9 100644 --- a/test/linalg/matmul.jl +++ b/test/linalg/matmul.jl @@ -23,16 +23,13 @@ let BB = [5 6; 7 8] AAi = AA+(0.5*im).*BB BBi = BB+(2.5*im).*AA[[2,1],[2,1]] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:2) - B = Btype == "Array" ? BB : view(BB, 1:2, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) @test A*B == [19 22; 43 50] @test At_mul_B(A, B) == [26 30; 38 44] @test A_mul_Bt(A, B) == [17 23; 39 53] @test At_mul_Bt(A, B) == [23 31; 34 46] - - Ai = Atype == "Array" ? AAi : view(AAi, 1:2, 1:2) - Bi = Btype == "Array" ? BBi : view(BBi, 1:2, 1:2) + end + for Ai in (copy(AAi), view(AAi, 1:2, 1:2)), Bi in (copy(BBi), view(BBi, 1:2, 1:2)) @test Ai*Bi == [-21+53.5im -4.25+51.5im; -12+95.5im 13.75+85.5im] @test Ac_mul_B(Ai, Bi) == [68.5-12im 57.5-28im; 88-3im 76.5-25im] @test A_mul_Bc(Ai, Bi) == [64.5+5.5im 43+31.5im; 104-18.5im 80.5+31.5im] @@ -48,16 +45,13 @@ let BB = [1 0 5; 6 -10 3; 2 -4 -1] AAi = AA+(0.5*im).*BB BBi = BB+(2.5*im).*AA[[2,1,3],[2,3,1]] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:3, 1:3) - B = Btype == "Array" ? BB : view(BB, 1:3, 1:3) + for A in (copy(AA), view(AA, 1:3, 1:3)), B in (copy(BB), view(BB, 1:3, 1:3)) @test A*B == [-26 38 -27; 1 -4 -6; 28 -46 15] @test Ac_mul_B(A, B) == [-6 2 -25; 3 -12 -18; 12 -26 -11] @test A_mul_Bc(A, B) == [-14 0 6; 4 -3 -3; 22 -6 -12] @test Ac_mul_Bc(A, B) == [6 -8 -6; 12 -9 -9; 18 -10 -12] - - Ai = Atype == "Array" ? AAi : view(AAi, 1:3, 1:3) - Bi = Btype == "Array" ? BBi : view(BBi, 1:3, 1:3) + end + for Ai in (copy(AAi), view(AAi, 1:3, 1:3)), Bi in (copy(BBi), view(BBi, 1:3, 1:3)) @test Ai*Bi == [-44.75+13im 11.75-25im -38.25+30im; -47.75-16.5im -51.5+51.5im -56+6im; 16.75-4.5im -53.5+52im -15.5im] @test Ac_mul_B(Ai, Bi) == [-21+2im -1.75+49im -51.25+19.5im; 25.5+56.5im -7-35.5im 22+35.5im; -3+12im -32.25+43im -34.75-2.5im] @test A_mul_Bc(Ai, Bi) == [-20.25+15.5im -28.75-54.5im 22.25+68.5im; -12.25+13im -15.5+75im -23+27im; 18.25+im 1.5+94.5im -27-54.5im] @@ -86,26 +80,19 @@ end let AA = [1 2 3; 4 5 6] .- 3 BB = [2 -2; 3 -5; -4 7] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:3) - B = Btype == "Array" ? BB : view(BB, 1:3, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:3)), B in (copy(BB), view(BB, 1:3, 1:2)) @test A*B == [-7 9; -4 9] @test At_mul_Bt(A, B) == [-6 -11 15; -6 -13 18; -6 -15 21] end AA = ones(Int, 2, 100) BB = ones(Int, 100, 3) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:100) - B = Btype == "Array" ? BB : view(BB, 1:100, 1:3) + for A in (copy(AA), view(AA, 1:2, 1:100)), B in (copy(BB), view(BB, 1:100, 1:3)) @test A*B == [100 100 100; 100 100 100] end AA = rand(1:20, 5, 5) .- 10 BB = rand(1:20, 5, 5) .- 10 CC = Array{Int}(size(AA, 1), size(BB, 2)) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5, 1:5) - C = Btype == "Array" ? CC : view(CC, 1:5, 1:5) + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5, 1:5)), C in (copy(CC), view(CC, 1:5, 1:5)) @test At_mul_B(A, B) == A'*B @test A_mul_Bt(A, B) == A*B' # Preallocated @@ -121,9 +108,7 @@ let end vv = [1,2] CC = Array{Int}(2, 2) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - v = vtype == "Array" ? vv : view(vv, 1:2) - C = Ctype == "Array" ? CC : view(CC, 1:2, 1:2) + for v in (copy(vv), view(vv, 1:2)), C in (copy(CC), view(CC, 1:2, 1:2)) @test @inferred(A_mul_Bc!(C, v, v)) == [1 2; 2 4] end end @@ -132,24 +117,18 @@ end let AA = rand(5,5) BB = rand(5) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5) + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5)) @test_throws DimensionMismatch Base.LinAlg.generic_matvecmul!(zeros(6),'N',A,B) @test_throws DimensionMismatch Base.LinAlg.generic_matvecmul!(B,'N',A,zeros(6)) end vv = [1,2,3] CC = Array{Int}(3, 3) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - v = vtype == "Array" ? vv : view(vv, 1:3) - C = Ctype == "Array" ? CC : view(CC, 1:3, 1:3) + for v in (copy(vv), view(vv, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) @test A_mul_Bt!(C, v, v) == v*v' end vvf = map(Float64,vv) CC = Array{Float64}(3, 3) - for vtype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - vf = vtype == "Array" ? vvf : view(vvf, 1:3) - C = Ctype == "Array" ? CC : view(CC, 1:3, 1:3) + for vf in (copy(vvf), view(vvf, 1:3)), C in (copy(CC), view(CC, 1:3, 1:3)) @test A_mul_Bt!(C, vf, vf) == vf*vf' end end @@ -159,10 +138,7 @@ let AA = rand(Float64,6,6) BB = rand(Float64,6,6) CC = zeros(Float64,6,6) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"], Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:6, 1:6) - B = Btype == "Array" ? BB : view(BB, 1:6, 1:6) - C = Ctype == "Array" ? CC : view(CC, 1:6, 1:6) + for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) @test Base.LinAlg.At_mul_Bt!(C,A,B) == A.'*B.' @test Base.LinAlg.A_mul_Bc!(C,A,B) == A*B.' @test Base.LinAlg.Ac_mul_B!(C,A,B) == A.'*B @@ -202,8 +178,7 @@ end let AA = reshape(1:1503, 501, 3).-750.0 res = Float64[135228751 9979252 -115270247; 9979252 10481254 10983256; -115270247 10983256 137236759] - for Atype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:501, 1:3) + for A in (copy(AA), view(AA, 1:501, 1:3)) @test At_mul_B(A, A) == res @test A_mul_Bt(A',A') == res end @@ -224,8 +199,7 @@ end # matmul for types w/o sizeof (issue #1282) let AA = fill(complex(1,1), 10, 10) - for Atype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:10, 1:10) + for A in (copy(AA), view(AA, 1:10, 1:10)) A2 = A^2 @test A2[1,1] == 20im end @@ -235,14 +209,8 @@ let AA = zeros(5, 5) BB = ones(5) CC = rand(5, 6) - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - for Ctype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:5, 1:5) - B = Btype == "Array" ? BB : view(BB, 1:5) - C = Ctype == "Array" ? CC : view(CC, 1:5, 1:6) - - @test_throws DimensionMismatch scale!(A, B, C) - end + for A in (copy(AA), view(AA, 1:5, 1:5)), B in (copy(BB), view(BB, 1:5)), C in (copy(CC), view(CC, 1:5, 1:6)) + @test_throws DimensionMismatch scale!(A, B, C) end end @@ -265,9 +233,7 @@ end vecdot_(x,y) = invoke(vecdot, Tuple{Any,Any}, x,y) # generic vecdot let AA = [1+2im 3+4im; 5+6im 7+8im], BB = [2+7im 4+1im; 3+8im 6+5im] - for Atype = ["Array", "SubArray"], Btype = ["Array", "SubArray"] - A = Atype == "Array" ? AA : view(AA, 1:2, 1:2) - B = Btype == "Array" ? BB : view(BB, 1:2, 1:2) + for A in (copy(AA), view(AA, 1:2, 1:2)), B in (copy(BB), view(BB, 1:2, 1:2)) @test vecdot(A,B) == dot(vec(A),vec(B)) == vecdot_(A,B) == vecdot(float.(A),float.(B)) @test vecdot(Int[], Int[]) == 0 == vecdot_(Int[], Int[]) @test_throws MethodError vecdot(Any[], Any[]) @@ -315,9 +281,7 @@ end let aa = rand(3,3) bb = rand(3,3) - for atype = ["Array", "SubArray"], btype = ["Array", "SubArray"] - a = atype == "Array" ? aa : view(aa, 1:3, 1:3) - b = btype == "Array" ? bb : view(bb, 1:3, 1:3) + for a in (copy(aa), view(aa, 1:3, 1:3)), b in (copy(bb), view(bb, 1:3, 1:3)) @test_throws ArgumentError A_mul_B!(a, a, b) @test_throws ArgumentError A_mul_B!(a, b, a) @test_throws ArgumentError A_mul_B!(a, a, a) @@ -333,14 +297,16 @@ import Base: *, transpose transpose(x::RootInt) = x @test Base.promote_op(*, RootInt, RootInt) === Int -a = [RootInt(3)] -C = [0] -A_mul_Bt!(C, a, a) -@test C[1] == 9 -a = [RootInt(2),RootInt(10)] -@test a*a' == [4 20; 20 100] -A = [RootInt(3) RootInt(5)] -@test A*a == [56] +let + a = [RootInt(3)] + C = [0] + A_mul_Bt!(C, a, a) + @test C[1] == 9 + a = [RootInt(2),RootInt(10)] + @test a*a' == [4 20; 20 100] + A = [RootInt(3) RootInt(5)] + @test A*a == [56] +end function test_mul(C, A, B) A_mul_B!(C, A, B) diff --git a/test/linalg/pinv.jl b/test/linalg/pinv.jl index f95f5472b5f8b..0dd73997c441a 100644 --- a/test/linalg/pinv.jl +++ b/test/linalg/pinv.jl @@ -129,6 +129,18 @@ end a = onediag_sparse(eltya, m) test_pinv(a, m, m, default_tol, default_tol, default_tol) end + @testset "Vector" begin + a = rand(eltya, m) + apinv = @inferred pinv(a) + @test pinv(hcat(a)) ≈ apinv + @test apinv isa RowVector{eltya} + end + @testset "RowVector" begin + a = rand(eltya, m)' + apinv = @inferred pinv(a) + @test pinv(vcat(a)) ≈ apinv + @test apinv isa Vector{eltya} + end end end @@ -141,6 +153,10 @@ end @test a[1] ≈ 0.0 @test a[2] ≈ 0.0 + a = pinv([zero(eltya); zero(eltya)]') + @test a[1] ≈ 0.0 + @test a[2] ≈ 0.0 + a = pinv(Diagonal([zero(eltya); zero(eltya)])) @test a.diag[1] ≈ 0.0 @test a.diag[2] ≈ 0.0 diff --git a/test/linalg/qr.jl b/test/linalg/qr.jl index 282321086fd1a..25728ee0c8572 100644 --- a/test/linalg/qr.jl +++ b/test/linalg/qr.jl @@ -150,11 +150,11 @@ end @testset "transpose errors" begin @test_throws ErrorException transpose(qrfact(randn(3,3))) - @test_throws ErrorException ctranspose(qrfact(randn(3,3))) + @test_throws ErrorException adjoint(qrfact(randn(3,3))) @test_throws ErrorException transpose(qrfact(randn(3,3), Val(false))) - @test_throws ErrorException ctranspose(qrfact(randn(3,3), Val(false))) + @test_throws ErrorException adjoint(qrfact(randn(3,3), Val(false))) @test_throws ErrorException transpose(qrfact(big.(randn(3,3)))) - @test_throws ErrorException ctranspose(qrfact(big.(randn(3,3)))) + @test_throws ErrorException adjoint(qrfact(big.(randn(3,3)))) end @testset "Issue 7304" begin diff --git a/test/linalg/schur.jl b/test/linalg/schur.jl index 953c250c8ff3c..53350074e2245 100644 --- a/test/linalg/schur.jl +++ b/test/linalg/schur.jl @@ -19,14 +19,10 @@ aimg = randn(n,n)/2 a = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(areal, aimg) : areal) asym = a'+a # symmetric indefinite apd = a'*a # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = a - else - a = view(a, 1:n, 1:n) - asym = view(asym, 1:n, 1:n) - apd = view(apd, 1:n, 1:n) - end + for (a, asym, apd) in ((a, asym, apd), + (view(a, 1:n, 1:n), + view(asym, 1:n, 1:n), + view(apd, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) d,v = eig(a) @@ -67,7 +63,7 @@ aimg = randn(n,n)/2 @test O[:Schur] ≈ SchurNew[:Schur] end - if atype == "Array" + if isa(a, Array) a1_sf = a[1:n1, 1:n1] a2_sf = a[n1+1:n2, n1+1:n2] else @@ -109,4 +105,10 @@ aimg = randn(n,n)/2 @test NS[:Z] ≈ sZ end end + @testset "0x0 matrix" for A in (zeros(eltya, 0, 0), view(rand(eltya, 2, 2), 1:0, 1:0)) + T, Z, λ = Base.LinAlg.schur(A) + @test T == A + @test Z == A + @test λ == zeros(0) + end end diff --git a/test/linalg/special.jl b/test/linalg/special.jl index e65a7c14309b8..1742a27a138e7 100644 --- a/test/linalg/special.jl +++ b/test/linalg/special.jl @@ -1,132 +1,130 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base.Test -debug = false n= 10 #Size of matrix to test srand(1) -debug && println("Test interconversion between special matrix types") -let a=[1.0:n;] - A=Diagonal(a) - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") +@testset "Interconversion between special matrix types" begin + a = [1.0:n;] + A = Diagonal(a) + @testset for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end + @test full(convert(newtype, Diagonal(GenericArray(a)))) == full(A) + end - for isupper in (true, false) - debug && println("isupper is $(isupper)") - A=Bidiagonal(a, [1.0:n-1;], ifelse(isupper, :U, :L)) - for newtype in [Bidiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") + @testset for isupper in (true, false) + A = Bidiagonal(a, [1.0:n-1;], ifelse(isupper, :U, :L)) + for newtype in [Bidiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) @test full(newtype(A)) == full(A) - end - @test_throws ArgumentError convert(SymTridiagonal, A) - tritype = isupper ? UpperTriangular : LowerTriangular - @test full(tritype(A)) == full(A) + end + @test_throws ArgumentError convert(SymTridiagonal, A) + tritype = isupper ? UpperTriangular : LowerTriangular + @test full(tritype(A)) == full(A) - A=Bidiagonal(a, zeros(n-1), ifelse(isupper, :U, :L)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] - debug && println("newtype is $(newtype)") + A = Bidiagonal(a, zeros(n-1), ifelse(isupper, :U, :L)) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) @test full(newtype(A)) == full(A) - end - @test full(tritype(A)) == full(A) - end + end + @test full(tritype(A)) == full(A) + end - A = SymTridiagonal(a, [1.0:n-1;]) - for newtype in [Tridiagonal, Matrix] + A = SymTridiagonal(a, [1.0:n-1;]) + for newtype in [Tridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - for newtype in [Diagonal, Bidiagonal] + end + for newtype in [Diagonal, Bidiagonal] @test_throws ArgumentError convert(newtype,A) - end - A = SymTridiagonal(a, zeros(n-1)) - @test full(convert(Bidiagonal,A)) == full(A) + end + A = SymTridiagonal(a, zeros(n-1)) + @test full(convert(Bidiagonal,A)) == full(A) - A = Tridiagonal(zeros(n-1), [1.0:n;], zeros(n-1)) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Matrix] + A = Tridiagonal(zeros(n-1), [1.0:n;], zeros(n-1)) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - A = Tridiagonal(ones(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal - for newtype in [SymTridiagonal, Matrix] + end + A = Tridiagonal(ones(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal + for newtype in [SymTridiagonal, Matrix] @test full(convert(newtype, A)) == full(A) - end - for newtype in [Diagonal, Bidiagonal] - @test_throws ArgumentError convert(newtype,A) - end - A = Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal - @test full(convert(Bidiagonal, A)) == full(A) - A = UpperTriangular(Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1))) - @test full(convert(Bidiagonal, A)) == full(A) - A = Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1)) #not morally Diagonal - @test full(convert(Bidiagonal, A)) == full(A) - A = LowerTriangular(Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1))) - @test full(convert(Bidiagonal, A)) == full(A) - @test_throws ArgumentError convert(SymTridiagonal,A) + end + for newtype in [Diagonal, Bidiagonal] + @test_throws ArgumentError convert(newtype,A) + end + A = Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1)) #not morally Diagonal + @test full(convert(Bidiagonal, A)) == full(A) + A = UpperTriangular(Tridiagonal(zeros(n-1), [1.0:n;], ones(n-1))) + @test full(convert(Bidiagonal, A)) == full(A) + A = Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1)) #not morally Diagonal + @test full(convert(Bidiagonal, A)) == full(A) + A = LowerTriangular(Tridiagonal(ones(n-1), [1.0:n;], zeros(n-1))) + @test full(convert(Bidiagonal, A)) == full(A) + @test_throws ArgumentError convert(SymTridiagonal,A) - A = LowerTriangular(full(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, LowerTriangular, Matrix] - @test full(convert(newtype, A)) == full(A) - end - A = UpperTriangular(full(Diagonal(a))) #morally Diagonal - for newtype in [Diagonal, Bidiagonal, SymTridiagonal, UpperTriangular, Matrix] - @test full(convert(newtype, A)) == full(A) - end - A = UpperTriangular(triu(rand(n,n))) - for newtype in [Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal] - @test_throws ArgumentError convert(newtype,A) - end + A = LowerTriangular(full(Diagonal(a))) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, LowerTriangular, Matrix] + @test full(convert(newtype, A)) == full(A) + end + A = UpperTriangular(full(Diagonal(a))) #morally Diagonal + for newtype in [Diagonal, Bidiagonal, SymTridiagonal, UpperTriangular, Matrix] + @test full(convert(newtype, A)) == full(A) + end + A = UpperTriangular(triu(rand(n,n))) + for newtype in [Diagonal, Bidiagonal, Tridiagonal, SymTridiagonal] + @test_throws ArgumentError convert(newtype,A) + end end -# Binary ops among special types -let a=[1.0:n;] - A=Diagonal(a) - Spectypes = [Diagonal, Bidiagonal, Tridiagonal, Matrix] - for (idx, type1) in enumerate(Spectypes) - for type2 in Spectypes +@testset "Binary ops among special types" begin + a=[1.0:n;] + A=Diagonal(a) + Spectypes = [Diagonal, Bidiagonal, Tridiagonal, Matrix] + for (idx, type1) in enumerate(Spectypes) + for type2 in Spectypes B = convert(type1,A) C = convert(type2,A) @test full(B + C) ≈ full(A + A) @test full(B - C) ≈ full(A - A) end - end - B = SymTridiagonal(a, ones(n-1)) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test full(B + convert(Spectype,A)) ≈ full(B + A) - @test full(convert(Spectype,A) + B) ≈ full(B + A) - @test full(B - convert(Spectype,A)) ≈ full(B - A) - @test full(convert(Spectype,A) - B) ≈ full(A - B) - end + end + B = SymTridiagonal(a, ones(n-1)) + for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] + @test full(B + convert(Spectype,A)) ≈ full(B + A) + @test full(convert(Spectype,A) + B) ≈ full(B + A) + @test full(B - convert(Spectype,A)) ≈ full(B - A) + @test full(convert(Spectype,A) - B) ≈ full(A - B) + end - C = rand(n,n) - for TriType in [Base.LinAlg.UnitLowerTriangular, Base.LinAlg.UnitUpperTriangular, UpperTriangular, LowerTriangular] - D = TriType(C) - for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] - @test full(D + convert(Spectype,A)) ≈ full(D + A) - @test full(convert(Spectype,A) + D) ≈ full(A + D) - @test full(D - convert(Spectype,A)) ≈ full(D - A) - @test full(convert(Spectype,A) - D) ≈ full(A - D) - end - end + C = rand(n,n) + for TriType in [Base.LinAlg.UnitLowerTriangular, Base.LinAlg.UnitUpperTriangular, UpperTriangular, LowerTriangular] + D = TriType(C) + for Spectype in [Diagonal, Bidiagonal, Tridiagonal, Matrix] + @test full(D + convert(Spectype,A)) ≈ full(D + A) + @test full(convert(Spectype,A) + D) ≈ full(A + D) + @test full(D - convert(Spectype,A)) ≈ full(D - A) + @test full(convert(Spectype,A) - D) ≈ full(A - D) + end + end end -#Triangular Types and QR -for typ in [UpperTriangular,LowerTriangular,Base.LinAlg.UnitUpperTriangular,Base.LinAlg.UnitLowerTriangular] - a = rand(n,n) - atri = typ(a) - b = rand(n,n) - qrb = qrfact(b,Val(true)) - @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' - @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' - qrb = qrfact(b,Val(false)) - @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' - @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' +@testset "Triangular Types and QR" begin + for typ in [UpperTriangular,LowerTriangular,Base.LinAlg.UnitUpperTriangular,Base.LinAlg.UnitLowerTriangular] + a = rand(n,n) + atri = typ(a) + b = rand(n,n) + qrb = qrfact(b,Val(true)) + @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' + @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' + qrb = qrfact(b,Val(false)) + @test Base.LinAlg.A_mul_Bc(atri,qrb[:Q]) ≈ full(atri) * qrb[:Q]' + @test Base.LinAlg.A_mul_Bc!(copy(atri),qrb[:Q]) ≈ full(atri) * qrb[:Q]' + end end -# Test that concatenations of combinations of special and other matrix types yield sparse arrays -let N = 4 +# should all yield sparse arrays +@testset "concatenations of combinations of special and other matrix types" begin + N = 4 # Test concatenating pairwise combinations of special matrices diagmat = Diagonal(ones(N)) bidiagmat = Bidiagonal(ones(N), ones(N-1), :U) @@ -169,7 +167,7 @@ end # TODO: As with the associated code, these tests should be moved to a more appropriate # location, particularly some future equivalent of base/linalg/special.jl dedicated to # intereactions between a broader set of matrix types -let +@testset "concatenations of annotated types" begin N = 4 # The tested annotation types testfull = Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) diff --git a/test/linalg/svd.jl b/test/linalg/svd.jl index d85d4eae26f6e..aa135c8448793 100644 --- a/test/linalg/svd.jl +++ b/test/linalg/svd.jl @@ -47,14 +47,7 @@ a2img = randn(n,n)/2 aa2 = eltya == Int ? rand(1:7, n, n) : convert(Matrix{eltya}, eltya <: Complex ? complex.(a2real, a2img) : a2real) asym = aa'+aa # symmetric indefinite apd = aa'*aa # symmetric positive-definite - @testset for atype in ("Array", "SubArray") - if atype == "Array" - a = aa - a2 = aa2 - else - a = view(aa, 1:n, 1:n) - a2 = view(aa2, 1:n, 1:n) - end + for (a, a2) in ((aa, aa2), (view(aa, 1:n, 1:n), view(aa2, 1:n, 1:n))) ε = εa = eps(abs(float(one(eltya)))) usv = svdfact(a) diff --git a/test/linalg/symmetric.jl b/test/linalg/symmetric.jl index 54cfb2753af27..d5d7164f80d63 100644 --- a/test/linalg/symmetric.jl +++ b/test/linalg/symmetric.jl @@ -12,18 +12,18 @@ end @testset "Hermitian matrix exponential/log" begin A1 = randn(4,4) + im*randn(4,4) A2 = A1 + A1' - @test expm(A2) ≈ expm(Hermitian(A2)) - @test logm(A2) ≈ logm(Hermitian(A2)) + @test exp(A2) ≈ exp(Hermitian(A2)) + @test log(A2) ≈ log(Hermitian(A2)) A3 = A1 * A1' # posdef - @test expm(A3) ≈ expm(Hermitian(A3)) - @test logm(A3) ≈ logm(Hermitian(A3)) + @test exp(A3) ≈ exp(Hermitian(A3)) + @test log(A3) ≈ log(Hermitian(A3)) A1 = randn(4,4) A3 = A1 * A1' A4 = A1 + A1.' - @test expm(A4) ≈ expm(Symmetric(A4)) - @test logm(A3) ≈ logm(Symmetric(A3)) - @test logm(A3) ≈ logm(Hermitian(A3)) + @test exp(A4) ≈ exp(Symmetric(A4)) + @test log(A3) ≈ log(Symmetric(A3)) + @test log(A3) ≈ log(Hermitian(A3)) end @testset "Core functionality" begin @@ -131,16 +131,16 @@ end end end - @testset "transpose, ctranspose" begin + @testset "transpose, adjoint" begin S = Symmetric(asym) H = Hermitian(aherm) @test transpose(S) === S == asym - @test ctranspose(H) === H == aherm + @test adjoint(H) === H == aherm if eltya <: Real - @test ctranspose(S) === S == asym + @test adjoint(S) === S == asym @test transpose(H) === H == aherm else - @test ctranspose(S) == Symmetric(conj(asym)) + @test adjoint(S) == Symmetric(conj(asym)) @test transpose(H) == Hermitian(transpose(aherm)) end end @@ -245,10 +245,18 @@ end @testset "pow" begin # Integer power @test (asym)^2 ≈ (Symmetric(asym)^2)::Symmetric - @test (asym)^-2 ≈ (Symmetric(asym)^-2)::Symmetric + if eltya <: Integer && !isone(asym) && !isone(-asym) + @test_throws DomainError (asym)^-2 + else + @test (asym)^-2 ≈ (Symmetric(asym)^-2)::Symmetric + end @test (aposs)^2 ≈ (Symmetric(aposs)^2)::Symmetric @test (aherm)^2 ≈ (Hermitian(aherm)^2)::Hermitian - @test (aherm)^-2 ≈ (Hermitian(aherm)^-2)::Hermitian + if eltya <: Integer && !isone(aherm) && !isone(-aherm) + @test_throws DomainError (aherm)^-2 + else + @test (aherm)^-2 ≈ (Hermitian(aherm)^-2)::Hermitian + end @test (apos)^2 ≈ (Hermitian(apos)^2)::Hermitian # integer floating point power @test (asym)^2.0 ≈ (Symmetric(asym)^2.0)::Symmetric diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index f6dbe41552cc0..ff03cbcd2f26c 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -22,7 +22,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa (UnitLowerTriangular, :L)) # Construct test matrix - A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo1 == :U ? t : ctranspose(t))) + A1 = t1(elty1 == Int ? rand(1:7, n, n) : convert(Matrix{elty1}, (elty1 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo1 == :U ? t : adjoint(t))) debug && println("elty1: $elty1, A1: $t1") @@ -132,15 +132,15 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa # transpose @test full(A1.') == full(A1).' @test full(viewA1.') == full(viewA1).' - # ctranspose + # adjoint @test full(A1') == full(A1)' @test full(viewA1') == full(viewA1)' # transpose! @test transpose!(copy(A1)) == A1.' @test transpose!(t1(view(copy(A1).data, vrange, vrange))) == viewA1.' - # ctranspose! - @test ctranspose!(copy(A1)) == A1' - @test ctranspose!(t1(view(copy(A1).data, vrange, vrange))) == viewA1' + # adjoint! + @test adjoint!(copy(A1)) == A1' + @test adjoint!(t1(view(copy(A1).data, vrange, vrange))) == viewA1' end # diag @@ -174,9 +174,9 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test B == viewA1.' end - #expm/logm + #exp/log if (elty1 == Float64 || elty1 == Complex128) && (t1 == UpperTriangular || t1 == LowerTriangular) - @test expm(full(logm(A1))) ≈ full(A1) + @test exp(full(log(A1))) ≈ full(A1) end # scale @@ -237,7 +237,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test ladb ≈ fladb atol=sqrt(eps(real(float(one(elty1)))))*n*n # Matrix square root - @test sqrtm(A1) |> t -> t*t ≈ A1 + @test sqrt(A1) |> t -> t*t ≈ A1 # naivesub errors @test_throws DimensionMismatch naivesub!(A1,ones(elty1,n+1)) @@ -274,7 +274,7 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa debug && println("elty1: $elty1, A1: $t1, elty2: $elty2") - A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo2 == :U ? t : ctranspose(t))) + A2 = t2(elty2 == Int ? rand(1:7, n, n) : convert(Matrix{elty2}, (elty2 <: Complex ? complex.(randn(n, n), randn(n, n)) : randn(n, n)) |> t -> chol(t't) |> t -> uplo2 == :U ? t : adjoint(t))) # Convert if elty1 <: Real && !(elty2 <: Integer) @@ -342,9 +342,11 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa @test B'A1' ≈ B'full(A1)' if eltyB == elty1 - @test A_mul_B!(zeros(B),A1,B) ≈ A1*B + @test A_mul_B!(zeros(B),A1,B) ≈ A1*B @test A_mul_Bc!(zeros(B),A1,B) ≈ A1*B' @test A_mul_Bt!(zeros(B),A1,B) ≈ A1*B.' + @test Ac_mul_B!(zeros(B),A1,B) ≈ A1'*B + @test At_mul_B!(zeros(B),A1,B) ≈ A1.'*B end #error handling @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(A1, ones(eltyB,n+1)) @@ -389,10 +391,10 @@ end # Matrix square root Atn = UpperTriangular([-1 1 2; 0 -2 2; 0 0 -3]) Atp = UpperTriangular([1 1 2; 0 2 2; 0 0 3]) -@test sqrtm(Atn) |> t->t*t ≈ Atn -@test typeof(sqrtm(Atn)[1,1]) <: Complex -@test sqrtm(Atp) |> t->t*t ≈ Atp -@test typeof(sqrtm(Atp)[1,1]) <: Real +@test sqrt(Atn) |> t->t*t ≈ Atn +@test typeof(sqrt(Atn)[1,1]) <: Complex +@test sqrt(Atp) |> t->t*t ≈ Atp +@test typeof(sqrt(Atp)[1,1]) <: Real Areal = randn(n, n)/2 Aimg = randn(n, n)/2 @@ -509,5 +511,5 @@ end isdefined(Main, :TestHelpers) || @eval Main include("../TestHelpers.jl") using TestHelpers.Furlong let A = UpperTriangular([Furlong(1) Furlong(4); Furlong(0) Furlong(1)]) - @test sqrtm(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) + @test sqrt(A) == Furlong{1//2}.(UpperTriangular([1 2; 0 1])) end diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index 6027c7981fe9c..28d6e00655a97 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -1,210 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# basic tridiagonal operations -n = 5 - -srand(123) - -d = 1 .+ rand(n) -dl = -rand(n-1) -du = -rand(n-1) -v = randn(n) -B = randn(n,2) - -@testset for elty in (Float32, Float64, Complex64, Complex128, Int) - if elty == Int - srand(61516384) - d = rand(1:100, n) - dl = -rand(0:10, n-1) - du = -rand(0:10, n-1) - v = rand(1:100, n) - B = rand(1:100, n, 2) - else - d = convert(Vector{elty}, d) - dl = convert(Vector{elty}, dl) - du = convert(Vector{elty}, du) - v = convert(Vector{elty}, v) - B = convert(Matrix{elty}, B) - end - ε = eps(abs2(float(one(elty)))) - T = Tridiagonal(dl, d, du) - Ts = SymTridiagonal(d, dl) - F = diagm(d) - for i = 1:n-1 - F[i,i+1] = du[i] - F[i+1,i] = dl[i] - end - - @testset "constructor" begin - for (x, y) in ((d, dl), (GenericArray(d), GenericArray(dl))) - ST = (SymTridiagonal(x, y))::SymTridiagonal{elty, typeof(x)} - @test ST == Matrix(ST) - @test ST.dv === x - @test ST.ev === y - end - # enable when deprecations for 0.7 are dropped - # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) - # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) - end - - @testset "size and Array" begin - @test_throws ArgumentError size(Ts,0) - @test size(Ts,3) == 1 - @test size(T, 1) == n - @test size(T) == (n, n) - @test Array(T) == F - end - - @testset "elementary operations" begin - @test conj(T) == Tridiagonal(conj(dl), conj(d), conj(du)) - @test transpose(T) == Tridiagonal(du, d, dl) - @test ctranspose(T) == Tridiagonal(conj(du), conj(d), conj(dl)) - - @test abs.(T) == Tridiagonal(abs.(dl),abs.(d),abs.(du)) - if elty <: Real - @test round.(T) == Tridiagonal(round.(dl),round.(d),round.(du)) - @test isa(round.(T), Tridiagonal) - @test trunc.(T) == Tridiagonal(trunc.(dl),trunc.(d),trunc.(du)) - @test isa(trunc.(T), Tridiagonal) - @test floor.(T) == Tridiagonal(floor.(dl),floor.(d),floor.(du)) - @test isa(floor.(T), Tridiagonal) - @test ceil.(T) == Tridiagonal(ceil.(dl),ceil.(d),ceil.(du)) - @test isa(ceil.(T), Tridiagonal) - end - @test real(T) == Tridiagonal(real(dl),real(d),real(du)) - @test imag(T) == Tridiagonal(imag(dl),imag(d),imag(du)) - @test abs.(Ts) == SymTridiagonal(abs.(d),abs.(dl)) - if elty <: Real - @test round.(Ts) == SymTridiagonal(round.(d),round.(dl)) - @test isa(round.(Ts), SymTridiagonal) - @test trunc.(Ts) == SymTridiagonal(trunc.(d),trunc.(dl)) - @test isa(trunc.(Ts), SymTridiagonal) - @test floor.(Ts) == SymTridiagonal(floor.(d),floor.(dl)) - @test isa(floor.(Ts), SymTridiagonal) - @test ceil.(Ts) == SymTridiagonal(ceil.(d),ceil.(dl)) - @test isa(ceil.(Ts), SymTridiagonal) - end - @test real(Ts) == SymTridiagonal(real(d),real(dl)) - @test imag(Ts) == SymTridiagonal(imag(d),imag(dl)) - end - - @testset "interconversion of Tridiagonal and SymTridiagonal" begin - @test Tridiagonal(dl, d, dl) == SymTridiagonal(d, dl) - @test SymTridiagonal(d, dl) == Tridiagonal(dl, d, dl) - @test Tridiagonal(dl, d, du) + Tridiagonal(du, d, dl) == SymTridiagonal(2d, dl+du) - @test SymTridiagonal(d, dl) + Tridiagonal(dl, d, du) == Tridiagonal(dl + dl, d+d, dl+du) - @test convert(SymTridiagonal,Tridiagonal(Ts)) == Ts - @test Array(convert(SymTridiagonal{Complex64},Tridiagonal(Ts))) == convert(Matrix{Complex64}, Ts) - end - if elty == Int - vs = rand(1:100, n) - Bs = rand(1:100, n, 2) - else - vs = convert(Vector{elty}, v) - Bs = convert(Matrix{elty}, B) - end - - @testset "tridiagonal linear algebra" begin - for (BB, vv) in ((Bs, vs), (view(Bs, 1:n, 1), view(vs, 1:n))) - @test T*vv ≈ F*vv - invFv = F\vv - @test T\vv ≈ invFv - # @test Base.solve(T,v) ≈ invFv - # @test Base.solve(T, B) ≈ F\B - Tlu = factorize(T) - x = Tlu\vv - @test x ≈ invFv - end - end - @test det(T) ≈ det(F) - - @testset "Matmul with Triangular types" begin - @test T*Base.LinAlg.UnitUpperTriangular(eye(n)) ≈ F*eye(n) - @test T*Base.LinAlg.UnitLowerTriangular(eye(n)) ≈ F*eye(n) - @test T*UpperTriangular(eye(n)) ≈ F*eye(n) - @test T*LowerTriangular(eye(n)) ≈ F*eye(n) - end - if elty <: Real - Ts = SymTridiagonal(d, dl) - Fs = Array(Ts) - Tldlt = factorize(Ts) - @testset "symmetric tridiagonal" begin - @test_throws DimensionMismatch Tldlt\rand(elty,n+1) - @test size(Tldlt) == size(Ts) - if elty <: AbstractFloat - @test typeof(convert(Base.LinAlg.LDLt{Float32},Tldlt)) == - Base.LinAlg.LDLt{Float32,SymTridiagonal{elty,Vector{elty}}} - end - for vv in (vs, view(vs, 1:n)) - invFsv = Fs\vv - x = Ts\vv - @test x ≈ invFsv - @test Array(AbstractArray(Tldlt)) ≈ Fs - end - - @testset "similar" begin - @test isa(similar(Ts), SymTridiagonal{elty}) - @test isa(similar(Ts, Int), SymTridiagonal{Int}) - @test isa(similar(Ts, Int, (3,2)), Matrix{Int}) - end - end - end - - @testset "eigenvalues/eigenvectors of symmetric tridiagonal" begin - if elty === Float32 || elty === Float64 - DT, VT = @inferred eig(Ts) - @inferred eig(Ts, 2:4) - @inferred eig(Ts, 1.0, 2.0) - D, Vecs = eig(Fs) - @test DT ≈ D - @test abs.(VT'Vecs) ≈ eye(elty, n) - @test eigvecs(Ts) == eigvecs(Fs) - #call to LAPACK.stein here - Test.test_approx_eq_modphase(eigvecs(Ts,eigvals(Ts)),eigvecs(Fs)) - elseif elty != Int - # check that undef is determined accurately even if type inference - # bails out due to the number of try/catch blocks in this code. - @test_throws UndefVarError Fs - end - end - - if elty != Int - @testset "issue #1490" begin - @test det(ones(elty,3,3)) ≈ zero(elty) atol=3*eps(real(one(elty))) - - @test det(SymTridiagonal(elty[],elty[])) == one(elty) - end - end - @testset "tril/triu" begin - @test_throws ArgumentError tril!(SymTridiagonal(d,dl),n+1) - @test_throws ArgumentError tril!(Tridiagonal(dl,d,du),n+1) - @test tril(SymTridiagonal(d,dl)) == Tridiagonal(dl,d,zeros(dl)) - @test tril(SymTridiagonal(d,dl),1) == Tridiagonal(dl,d,dl) - @test tril(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,zeros(d),zeros(dl)) - @test tril(SymTridiagonal(d,dl),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) - @test tril(Tridiagonal(dl,d,du)) == Tridiagonal(dl,d,zeros(du)) - @test tril(Tridiagonal(dl,d,du),1) == Tridiagonal(dl,d,du) - @test tril(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,zeros(d),zeros(du)) - @test tril(Tridiagonal(dl,d,du),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) - - @test_throws ArgumentError triu!(SymTridiagonal(d,dl),n+1) - @test_throws ArgumentError triu!(Tridiagonal(dl,d,du),n+1) - @test triu(SymTridiagonal(d,dl)) == Tridiagonal(zeros(dl),d,dl) - @test triu(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,d,dl) - @test triu(SymTridiagonal(d,dl),1) == Tridiagonal(zeros(dl),zeros(d),dl) - @test triu(SymTridiagonal(d,dl),2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) - @test triu(Tridiagonal(dl,d,du)) == Tridiagonal(zeros(dl),d,du) - @test triu(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,d,du) - @test triu(Tridiagonal(dl,d,du),1) == Tridiagonal(zeros(dl),zeros(d),du) - @test triu(Tridiagonal(dl,d,du),2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) - - @test !istril(SymTridiagonal(d,dl)) - @test !istriu(SymTridiagonal(d,dl)) - @test istriu(Tridiagonal(zeros(dl),d,du)) - @test istril(Tridiagonal(dl,d,zeros(du))) - end -end - #Test equivalence of eigenvectors/singular vectors taking into account possible phase (sign) differences function test_approx_eq_vecs(a::StridedVecOrMat{S}, b::StridedVecOrMat{T}, error=nothing) where {S<:Real,T<:Real} n = size(a, 1) @@ -221,118 +16,177 @@ end let n = 12 #Size of matrix problem to test srand(123) - @testset "SymTridiagonal (symmetric tridiagonal) matrices" begin - for relty in (Float32, Float64), elty in (relty, Complex{relty}) - a = convert(Vector{elty}, randn(n)) - b = convert(Vector{elty}, randn(n-1)) + @testset for elty in (Float32, Float64, Complex64, Complex128, Int) + if elty == Int + srand(61516384) + d = rand(1:100, n) + dl = -rand(0:10, n-1) + du = -rand(0:10, n-1) + v = rand(1:100, n) + B = rand(1:100, n, 2) + a = rand(1:100, n-1) + b = rand(1:100, n) + c = rand(1:100, n-1) + else + d = convert(Vector{elty}, 1. + randn(n)) + dl = convert(Vector{elty}, randn(n - 1)) + du = convert(Vector{elty}, randn(n - 1)) + v = convert(Vector{elty}, randn(n)) + B = convert(Matrix{elty}, randn(n, 2)) + a = convert(Vector{elty}, randn(n - 1)) + b = convert(Vector{elty}, randn(n)) + c = convert(Vector{elty}, randn(n - 1)) if elty <: Complex - a += im*convert(Vector{elty}, randn(n)) - b += im*convert(Vector{elty}, randn(n-1)) + a += im*convert(Vector{elty}, randn(n - 1)) + b += im*convert(Vector{elty}, randn(n)) + c += im*convert(Vector{elty}, randn(n - 1)) end + end + @test_throws DimensionMismatch SymTridiagonal(dl, ones(elty, n + 1)) + @test_throws ArgumentError SymTridiagonal(rand(n, n)) + @test_throws ArgumentError Tridiagonal(dl, dl, dl) + @test_throws ArgumentError convert(SymTridiagonal{elty}, Tridiagonal(dl, d, du)) + + if elty != Int + @testset "issue #1490" begin + @test det(ones(elty,3,3)) ≈ zero(elty) atol=3*eps(real(one(elty))) + @test det(SymTridiagonal(elty[],elty[])) == one(elty) + end + end - @test_throws DimensionMismatch SymTridiagonal(a, ones(elty, n+1)) - @test_throws ArgumentError SymTridiagonal(rand(n,n)) + @testset "constructor" begin + for (x, y) in ((d, dl), (GenericArray(d), GenericArray(dl))) + ST = (SymTridiagonal(x, y))::SymTridiagonal{elty, typeof(x)} + @test ST == Matrix(ST) + @test ST.dv === x + @test ST.ev === y + TT = (Tridiagonal(y, x, y))::Tridiagonal{elty, typeof(x)} + @test TT == Matrix(TT) + @test TT.dl === y + @test TT.d === x + @test TT.du === y + end + # enable when deprecations for 0.7 are dropped + # @test_throws MethodError SymTridiagonal(dv, GenericArray(ev)) + # @test_throws MethodError SymTridiagonal(GenericArray(dv), ev) + # @test_throws MethodError Tridiagonal(GenericArray(ev), dv, GenericArray(ev)) + # @test_throws MethodError Tridiagonal(ev, GenericArray(dv), ev) + end + @testset "interconversion of Tridiagonal and SymTridiagonal" begin + @test Tridiagonal(dl, d, dl) == SymTridiagonal(d, dl) + @test Tridiagonal(dl, d, du) + Tridiagonal(du, d, dl) == SymTridiagonal(2d, dl+du) + @test SymTridiagonal(d, dl) + Tridiagonal(dl, d, du) == Tridiagonal(dl + dl, d+d, dl+du) + @test convert(SymTridiagonal,Tridiagonal(SymTridiagonal(d, dl))) == SymTridiagonal(d, dl) + @test Array(convert(SymTridiagonal{Complex64},Tridiagonal(SymTridiagonal(d, dl)))) == convert(Matrix{Complex64}, SymTridiagonal(d, dl)) + end + @testset "tril/triu" begin + @test_throws ArgumentError tril!(SymTridiagonal(d,dl),n+1) + @test_throws ArgumentError tril!(Tridiagonal(dl,d,du),n+1) + @test tril(SymTridiagonal(d,dl)) == Tridiagonal(dl,d,zeros(dl)) + @test tril(SymTridiagonal(d,dl),1) == Tridiagonal(dl,d,dl) + @test tril(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,zeros(d),zeros(dl)) + @test tril(SymTridiagonal(d,dl),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) + @test tril(Tridiagonal(dl,d,du)) == Tridiagonal(dl,d,zeros(du)) + @test tril(Tridiagonal(dl,d,du),1) == Tridiagonal(dl,d,du) + @test tril(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,zeros(d),zeros(du)) + @test tril(Tridiagonal(dl,d,du),-2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) + + @test_throws ArgumentError triu!(SymTridiagonal(d,dl),n+1) + @test_throws ArgumentError triu!(Tridiagonal(dl,d,du),n+1) + @test triu(SymTridiagonal(d,dl)) == Tridiagonal(zeros(dl),d,dl) + @test triu(SymTridiagonal(d,dl),-1) == Tridiagonal(dl,d,dl) + @test triu(SymTridiagonal(d,dl),1) == Tridiagonal(zeros(dl),zeros(d),dl) + @test triu(SymTridiagonal(d,dl),2) == Tridiagonal(zeros(dl),zeros(d),zeros(dl)) + @test triu(Tridiagonal(dl,d,du)) == Tridiagonal(zeros(dl),d,du) + @test triu(Tridiagonal(dl,d,du),-1) == Tridiagonal(dl,d,du) + @test triu(Tridiagonal(dl,d,du),1) == Tridiagonal(zeros(dl),zeros(d),du) + @test triu(Tridiagonal(dl,d,du),2) == Tridiagonal(zeros(dl),zeros(d),zeros(du)) + + @test !istril(SymTridiagonal(d,dl)) + @test !istriu(SymTridiagonal(d,dl)) + @test istriu(Tridiagonal(zeros(dl),d,du)) + @test istril(Tridiagonal(dl,d,zeros(du))) + end - A = SymTridiagonal(a, b) + @testset for mat_type in (Tridiagonal, SymTridiagonal) + A = mat_type == Tridiagonal ? mat_type(dl, d, du) : mat_type(d, dl) fA = map(elty <: Complex ? Complex128 : Float64, Array(A)) - + @testset "similar, size, and copy!" begin + B = similar(A) + @test size(B) == size(A) + if mat_type == Tridiagonal # doesn't work for SymTridiagonal yet + copy!(B, A) + @test B == A + end + @test isa(similar(A), mat_type{elty}) + @test isa(similar(A, Int), mat_type{Int}) + @test isa(similar(A, Int, (3, 2)), Matrix{Int}) + @test size(A, 3) == 1 + @test size(A, 1) == n + @test size(A) == (n, n) + @test_throws ArgumentError size(A, 0) + end @testset "getindex" begin - @test_throws BoundsError A[n+1,1] - @test_throws BoundsError A[1,n+1] - @test A[1,n] == convert(elty,0.0) - @test A[1,1] == a[1] + @test_throws BoundsError A[n + 1, 1] + @test_throws BoundsError A[1, n + 1] + @test A[1, n] == convert(elty, 0.0) + @test A[1, 1] == d[1] end @testset "setindex!" begin @test_throws BoundsError A[n + 1, 1] = 0 # test bounds check @test_throws BoundsError A[1, n + 1] = 0 # test bounds check - @test ((A[3, 3] = A[3, 3]) == A[3, 3]; A == fA) # test assignment on the main diagonal - @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal - @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal - @test_throws ArgumentError A[1, 3] = 1 # test assignment off the main/sub/super diagonal + @test_throws ArgumentError A[1, 3] = 1 # test assignment off the main/sub/super diagonal + if mat_type == Tridiagonal + @test (A[3, 3] = A[3, 3]; A == fA) # test assignment on the main diagonal + @test (A[3, 2] = A[3, 2]; A == fA) # test assignment on the subdiagonal + @test (A[2, 3] = A[2, 3]; A == fA) # test assignment on the superdiagonal + @test ((A[1, 3] = 0) == 0; A == fA) # test zero assignment off the main/sub/super diagonal + else # mat_type is SymTridiagonal + @test ((A[3, 3] = A[3, 3]) == A[3, 3]; A == fA) # test assignment on the main diagonal + @test_throws ArgumentError A[3, 2] = 1 # test assignment on the subdiagonal + @test_throws ArgumentError A[2, 3] = 1 # test assignment on the superdiagonal + end end @testset "Diagonal extraction" begin - @test diag(A,1) == b - @test diag(A,-1) == b - @test diag(A,0) == a - @test diag(A,n-1) == zeros(elty,1) - @test_throws ArgumentError diag(A,n+1) + @test diag(A, 1) === (mat_type == Tridiagonal ? du : dl) + @test diag(A, -1) === dl + @test diag(A, 0) === d + @test diag(A) === d + @test diag(A, n - 1) == zeros(elty, 1) + @test_throws ArgumentError diag(A, n + 1) end @testset "Idempotent tests" begin - for func in (conj, transpose, ctranspose) + for func in (conj, transpose, adjoint) @test func(func(A)) == A end end - @testset "Simple unary functions" begin - for func in (det, inv) - @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(relty)) + if elty != Int + @testset "Simple unary functions" begin + for func in (det, inv) + @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(real(one(elty)))) + end end end - + ds = mat_type == Tridiagonal ? (dl, d, du) : (d, dl) + for f in (real, imag) + @test f(A)::mat_type == mat_type(map(f, ds)...) + end if elty <: Real - @testset "Rounding to Ints" begin - @test round.(Int,A) == round.(Int,fA) - @test isa(round.(Int,A), SymTridiagonal) - @test trunc.(Int,A) == trunc.(Int,fA) - @test isa(trunc.(Int,A), SymTridiagonal) - @test ceil.(Int,A) == ceil.(Int,fA) - @test isa(ceil.(Int,A), SymTridiagonal) - @test floor.(Int,A) == floor.(Int,fA) - @test isa(floor.(Int,A), SymTridiagonal) + for f in (round, trunc, floor, ceil) + fds = [f.(d) for d in ds] + @test f.(A)::mat_type == mat_type(fds...) + @test f.(Int, A)::mat_type == f.(Int, fA) end end - - @testset "Tridiagonal/SymTridiagonal mixing ops" begin - B = convert(Tridiagonal{elty},A) - @test B == A - @test B + A == A + B - @test B - A == A - B - end + fds = [abs.(d) for d in ds] + @test abs.(A)::mat_type == mat_type(fds...) @testset "Multiplication with strided matrix/vector" begin @test A*ones(n) ≈ Array(A)*ones(n) @test A*ones(n, 2) ≈ Array(A)*ones(n, 2) end - if elty <: Real - @testset "Eigensystems" begin - zero, infinity = convert(elty, 0), convert(elty, Inf) - @testset "stebz! and stein!" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, a, b) - evecs = LAPACK.stein!(a, b, w) - - (e, v) = eig(SymTridiagonal(a, b)) - @test e ≈ w - test_approx_eq_vecs(v, evecs) - end - @testset "stein! call using iblock and isplit" begin - w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, a, b) - evecs = LAPACK.stein!(a, b, w, iblock, isplit) - test_approx_eq_vecs(v, evecs) - end - @testset "stegr! call with index range" begin - F = eigfact(SymTridiagonal(a, b),1:2) - fF = eigfact(Symmetric(Array(SymTridiagonal(a, b))),1:2) - Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) - @test F[:values] ≈ fF[:values] - end - @testset "stegr! call with value range" begin - F = eigfact(SymTridiagonal(a, b),0.0,1.0) - fF = eigfact(Symmetric(Array(SymTridiagonal(a, b))),0.0,1.0) - Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) - @test F[:values] ≈ fF[:values] - end - end - end - @testset "Binary operations" begin - a = convert(Vector{elty}, randn(n)) - b = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n)) - b += im*convert(Vector{elty}, randn(n - 1)) - end - - B = SymTridiagonal(a, b) + B = mat_type == Tridiagonal ? mat_type(a, b, c) : mat_type(b, a) fB = map(elty <: Complex ? Complex128 : Float64, Array(B)) - for op in (+, -, *) @test Array(op(A, B)) ≈ op(fA, fB) end @@ -341,109 +195,112 @@ let n = 12 #Size of matrix problem to test @test Array(A*α) ≈ Array(A)*α @test Array(A/α) ≈ Array(A)/α + @testset "Matmul with Triangular types" begin + @test A*Base.LinAlg.UnitUpperTriangular(eye(n)) ≈ fA*eye(n) + @test A*Base.LinAlg.UnitLowerTriangular(eye(n)) ≈ fA*eye(n) + @test A*UpperTriangular(eye(n)) ≈ fA*eye(n) + @test A*LowerTriangular(eye(n)) ≈ fA*eye(n) + end @testset "A_mul_B! errors" begin + @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n,n+1)) + @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n+1,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n,n),B,ones(elty,n+1,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n+1,n),B,ones(elty,n,n)) @test_throws DimensionMismatch A_mul_B!(zeros(elty,n,n+1),B,ones(elty,n,n)) end end - end - end - @testset "Tridiagonal matrices" begin - for relty in (Float32, Float64), elty in (relty, Complex{relty}) - a = convert(Vector{elty}, randn(n - 1)) - b = convert(Vector{elty}, randn(n)) - c = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n - 1)) - b += im*convert(Vector{elty}, randn(n)) - c += im*convert(Vector{elty}, randn(n - 1)) - end - - @test_throws ArgumentError Tridiagonal(a,a,a) - A = Tridiagonal(a, b, c) - fA = map(elty <: Complex ? Complex128 : Float64, Array(A)) - - @testset "similar, size, and copy!" begin - B = similar(A) - @test size(B) == size(A) - copy!(B,A) - @test B == A - @test isa(similar(A), Tridiagonal{elty}) - @test isa(similar(A, Int), Tridiagonal{Int}) - @test isa(similar(A, Int, (3,2)), Matrix{Int}) - @test size(A,3) == 1 - @test_throws ArgumentError size(A,0) - end - @testset "Diagonal extraction" begin - @test diag(A,-1) == a - @test diag(A,0) == b - @test diag(A,1) == c - @test diag(A,n-1) == zeros(elty,1) - @test_throws ArgumentError diag(A,n+1) - end - @testset "Simple unary functions" begin - for func in (det, inv) - @test func(A) ≈ func(fA) atol=n^2*sqrt(eps(relty)) + if mat_type == SymTridiagonal + @testset "Tridiagonal/SymTridiagonal mixing ops" begin + B = convert(Tridiagonal{elty}, A) + @test B == A + @test B + A == A + B + @test B - A == A - B end - end - if elty <: Real - @testset "Rounding to Ints" begin - @test round.(Int,A) == round.(Int,fA) - @test isa(round.(Int,A), Tridiagonal) - @test trunc.(Int,A) == trunc.(Int,fA) - @test isa(trunc.(Int,A), Tridiagonal) - @test ceil.(Int,A) == ceil.(Int,fA) - @test isa(ceil.(Int,A), Tridiagonal) - @test floor.(Int,A) == floor.(Int,fA) - @test isa(floor.(Int,A), Tridiagonal) + if elty <: Base.LinAlg.BlasReal + @testset "Eigensystems" begin + zero, infinity = convert(elty, 0), convert(elty, Inf) + @testset "stebz! and stein!" begin + w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) + evecs = LAPACK.stein!(b, a, w) + + (e, v) = eig(SymTridiagonal(b, a)) + @test e ≈ w + test_approx_eq_vecs(v, evecs) + end + @testset "stein! call using iblock and isplit" begin + w, iblock, isplit = LAPACK.stebz!('V', 'B', -infinity, infinity, 0, 0, zero, b, a) + evecs = LAPACK.stein!(b, a, w, iblock, isplit) + test_approx_eq_vecs(v, evecs) + end + @testset "stegr! call with index range" begin + F = eigfact(SymTridiagonal(b, a),1:2) + fF = eigfact(Symmetric(Array(SymTridiagonal(b, a))),1:2) + Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) + @test F[:values] ≈ fF[:values] + end + @testset "stegr! call with value range" begin + F = eigfact(SymTridiagonal(b, a),0.0,1.0) + fF = eigfact(Symmetric(Array(SymTridiagonal(b, a))),0.0,1.0) + Test.test_approx_eq_modphase(F[:vectors], fF[:vectors]) + @test F[:values] ≈ fF[:values] + end + @testset "eigenvalues/eigenvectors of symmetric tridiagonal" begin + if elty === Float32 || elty === Float64 + DT, VT = @inferred eig(A) + @inferred eig(A, 2:4) + @inferred eig(A, 1.0, 2.0) + D, Vecs = eig(fA) + @test DT ≈ D + @test abs.(VT'Vecs) ≈ eye(elty, n) + @test eigvecs(A) ≈ eigvecs(fA) + #call to LAPACK.stein here + Test.test_approx_eq_modphase(eigvecs(A,eigvals(A)),eigvecs(A)) + elseif elty != Int + # check that undef is determined accurately even if type inference + # bails out due to the number of try/catch blocks in this code. + @test_throws UndefVarError fA + end + end + end end - end - @testset "Binary operations" begin - a = convert(Vector{elty}, randn(n - 1)) - b = convert(Vector{elty}, randn(n)) - c = convert(Vector{elty}, randn(n - 1)) - if elty <: Complex - a += im*convert(Vector{elty}, randn(n - 1)) - b += im*convert(Vector{elty}, randn(n)) - c += im*convert(Vector{elty}, randn(n - 1)) + if elty <: Real + Ts = SymTridiagonal(d, dl) + Fs = Array(Ts) + Tldlt = factorize(Ts) + @testset "symmetric tridiagonal" begin + @test_throws DimensionMismatch Tldlt\rand(elty,n+1) + @test size(Tldlt) == size(Ts) + if elty <: AbstractFloat + @test typeof(convert(Base.LinAlg.LDLt{Float32},Tldlt)) == + Base.LinAlg.LDLt{Float32,SymTridiagonal{elty,Vector{elty}}} + end + for vv in (copy(v), view(v, 1:n)) + invFsv = Fs\vv + x = Ts\vv + @test x ≈ invFsv + @test Array(AbstractArray(Tldlt)) ≈ Fs + end + + @testset "similar" begin + @test isa(similar(Ts), SymTridiagonal{elty}) + @test isa(similar(Ts, Int), SymTridiagonal{Int}) + @test isa(similar(Ts, Int, (3,2)), Matrix{Int}) + end + end end - end - - @testset "Multiplication with strided matrix/vector" begin - @test A*ones(n) ≈ Array(A)*ones(n) - @test A*ones(n, 2) ≈ Array(A)*ones(n, 2) - end - - B = Tridiagonal(a, b, c) - fB = map(elty <: Complex ? Complex128 : Float64, Array(B)) - @testset "Binary ops" begin - for op in (+, -, *) - @test Array(op(A, B)) ≈ op(fA, fB) + else # mat_type is Tridiagonal + @testset "tridiagonal linear algebra" begin + for (BB, vv) in ((copy(B), copy(v)), (view(B, 1:n, 1), view(v, 1:n))) + @test A*vv ≈ fA*vv + invFv = fA\vv + @test A\vv ≈ invFv + # @test Base.solve(T,v) ≈ invFv + # @test Base.solve(T, B) ≈ F\B + Tlu = factorize(A) + x = Tlu\vv + @test x ≈ invFv + end end - α = rand(elty) - @test Array(α*A) ≈ α*Array(A) - @test Array(A*α) ≈ Array(A)*α - @test Array(A/α) ≈ Array(A)/α - end - @test_throws ArgumentError convert(SymTridiagonal{elty},A) - - @testset "A_mul_B! errors" begin - @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n,n+1)) - @test_throws DimensionMismatch Base.LinAlg.A_mul_B!(zeros(fA),A,ones(elty,n+1,n)) - end - @testset "getindex" begin - @test_throws BoundsError A[n+1,1] - @test_throws BoundsError A[1,n+1] - end - @testset "setindex!" begin - @test_throws BoundsError A[n + 1, 1] = 0 # test bounds check - @test_throws BoundsError A[1, n + 1] = 0 # test bounds check - @test (A[3, 3] = A[3, 3]; A == fA) # test assignment on the main diagonal - @test (A[3, 2] = A[3, 2]; A == fA) # test assignment on the subdiagonal - @test (A[2, 3] = A[2, 3]; A == fA) # test assignment on the superdiagonal - @test ((A[1, 3] = 0) == 0; A == fA) # test zero assignment off the main/sub/super diagonal - @test_throws ArgumentError A[1, 3] = 1 # test non-zero assignment off the main/sub/super diagonal end end end diff --git a/test/linalg/uniformscaling.jl b/test/linalg/uniformscaling.jl index 9bdde7b7470e8..88fb277b7b201 100644 --- a/test/linalg/uniformscaling.jl +++ b/test/linalg/uniformscaling.jl @@ -43,6 +43,15 @@ end @test UniformScaling(α)./α == UniformScaling(1.0) end +@testset "det and logdet" begin + @test det(I) === 1 + @test det(1.0I) === 1.0 + @test det(0I) === 0 + @test det(0.0I) === 0.0 + @test logdet(I) == 0 + @test_throws ArgumentError det(2I) +end + @test copy(UniformScaling(one(Float64))) == UniformScaling(one(Float64)) @test sprint(show,UniformScaling(one(Complex128))) == "UniformScaling{Complex{Float64}}\n(1.0 + 0.0im)*I" @test sprint(show,UniformScaling(one(Float32))) == "UniformScaling{Float32}\n1.0*I" @@ -67,15 +76,7 @@ B = bitrand(2,2) @testset "binary ops with matrices" begin let AA = randn(2, 2) for SS in (sprandn(3,3, 0.5), speye(Int, 3)) - @testset for atype in ("Array", "SubArray") - if atype == "Array" - A = AA - S = SS - else - A = view(AA, 1:2, 1:2) - S = view(SS, 1:3, 1:3) - end - + for (A, S) in ((AA, SS), (view(AA, 1:2, 1:2), view(SS, 1:3, 1:3))) @test @inferred(A + I) == A + eye(A) @test @inferred(I + A) == A + eye(A) @test @inferred(I - I) === UniformScaling(0) @@ -104,7 +105,7 @@ B = bitrand(2,2) @test @inferred(I/λ) === UniformScaling(1/λ) @test @inferred(I\J) === J - if atype == "Array" + if isa(A, Array) T = LowerTriangular(randn(3,3)) else T = LowerTriangular(view(randn(3,3), 1:3, 1:3)) @@ -115,7 +116,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = LinAlg.UnitLowerTriangular(randn(3,3)) else T = LinAlg.UnitLowerTriangular(view(randn(3,3), 1:3, 1:3)) @@ -126,7 +127,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = UpperTriangular(randn(3,3)) else T = UpperTriangular(view(randn(3,3), 1:3, 1:3)) @@ -137,7 +138,7 @@ B = bitrand(2,2) @test @inferred(J - T) == J - full(T) @test @inferred(T\I) == inv(T) - if atype == "Array" + if isa(A, Array) T = LinAlg.UnitUpperTriangular(randn(3,3)) else T = LinAlg.UnitUpperTriangular(view(randn(3,3), 1:3, 1:3)) diff --git a/test/lineedit.jl b/test/lineedit.jl index ee558e91db875..db08ac64c2bc4 100644 --- a/test/lineedit.jl +++ b/test/lineedit.jl @@ -1,9 +1,35 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license using Base.LineEdit +using Base.LineEdit: edit_insert, buffer, content, setmark, getmark + isdefined(Main, :TestHelpers) || @eval Main include(joinpath(dirname(@__FILE__), "TestHelpers.jl")) using TestHelpers +# no need to have animation in tests +LineEdit.REGION_ANIMATION_DURATION[] = 0.001 + +## helper functions + +function new_state() + term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) + LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) +end + +charseek(buf, i) = seek(buf, Base.unsafe_chr2ind(content(buf), i+1)-1) +charpos(buf, pos=position(buf)) = Base.unsafe_ind2chr(content(buf), pos+1)-1 + +function transform!(f, s, i = -1) # i is char-based (not bytes) buffer position + buf = buffer(s) + i >= 0 && charseek(buf, i) + action = f(s) + if s isa LineEdit.MIState && action isa Symbol + s.last_action = action # simulate what happens in LineEdit.prompt! + end + content(s), charpos(buf), charpos(buf, getmark(buf)) +end + + function run_test(d,buf) global a_foo, b_foo, a_bar, b_bar a_foo = b_foo = a_bar = b_bar = 0 @@ -274,82 +300,112 @@ seek(buf,0) buf = IOBuffer("type X\n ") seekend(buf) -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test position(buf) == 5 @test buf.size == 5 -@test String(buf.data[1:buf.size]) == "type " +@test content(buf) == "type " buf = IOBuffer("4 +aaa+ x") seek(buf,8) -@test LineEdit.edit_delete_prev_word(buf) +@test !isempty(LineEdit.edit_delete_prev_word(buf)) @test position(buf) == 3 @test buf.size == 4 -@test String(buf.data[1:buf.size]) == "4 +x" +@test content(buf) == "4 +x" buf = IOBuffer("x = func(arg1,arg2 , arg3)") seekend(buf) LineEdit.char_move_word_left(buf) @test position(buf) == 21 -@test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = func(arg1,arg3)" -@test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = func(arg3)" -@test LineEdit.edit_delete_prev_word(buf) -@test String(buf.data[1:buf.size]) == "x = arg3)" +@test !isempty(LineEdit.edit_delete_prev_word(buf)) +@test content(buf) == "x = func(arg1,arg3)" +@test !isempty(LineEdit.edit_delete_prev_word(buf)) +@test content(buf) == "x = func(arg3)" +@test !isempty(LineEdit.edit_delete_prev_word(buf)) +@test content(buf) == "x = arg3)" # Unicode combining characters let buf = IOBuffer() - LineEdit.edit_insert(buf, "â") + edit_insert(buf, "â") LineEdit.edit_move_left(buf) @test position(buf) == 0 LineEdit.edit_move_right(buf) @test nb_available(buf) == 0 LineEdit.edit_backspace(buf) - @test String(buf.data[1:buf.size]) == "a" + @test content(buf) == "a" end -## edit_transpose ## +## edit_transpose_chars ## let buf = IOBuffer() - LineEdit.edit_insert(buf, "abcde") + edit_insert(buf, "abcde") seek(buf,0) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "abcde" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "abcde" LineEdit.char_move_right(buf) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "bacde" - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "bcade" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "bacde" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "bcade" seekend(buf) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "bcaed" - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "bcade" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "bcaed" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "bcade" seek(buf, 0) LineEdit.edit_clear(buf) - LineEdit.edit_insert(buf, "αβγδε") + edit_insert(buf, "αβγδε") seek(buf,0) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "αβγδε" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "αβγδε" LineEdit.char_move_right(buf) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "βαγδε" - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "βγαδε" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "βαγδε" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "βγαδε" seekend(buf) - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "βγαεδ" - LineEdit.edit_transpose(buf) - @test String(buf.data[1:buf.size]) == "βγαδε" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "βγαεδ" + LineEdit.edit_transpose_chars(buf) + @test content(buf) == "βγαδε" end -let - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer()) - s = LineEdit.init_state(term, ModalInterface([Prompt("test> ")])) - buf = LineEdit.buffer(s) +@testset "edit_word_transpose" begin + buf = IOBuffer() + mode = Ref{Symbol}() + transpose!(i) = transform!(buf -> LineEdit.edit_transpose_words(buf, mode[]), + buf, i)[1:2] + + mode[] = :readline + edit_insert(buf, "àbç def gh ") + @test transpose!(0) == ("àbç def gh ", 0) + @test transpose!(1) == ("àbç def gh ", 1) + @test transpose!(2) == ("àbç def gh ", 2) + @test transpose!(3) == ("def àbç gh ", 7) + @test transpose!(4) == ("àbç def gh ", 7) + @test transpose!(5) == ("def àbç gh ", 7) + @test transpose!(6) == ("àbç def gh ", 7) + @test transpose!(7) == ("àbç gh def ", 11) + @test transpose!(10) == ("àbç def gh ", 11) + @test transpose!(11) == ("àbç gh def", 12) + edit_insert(buf, " ") + @test transpose!(13) == ("àbç def gh", 13) + + take!(buf) + mode[] = :emacs + edit_insert(buf, "àbç def gh ") + @test transpose!(0) == ("def àbç gh ", 7) + @test transpose!(4) == ("àbç def gh ", 7) + @test transpose!(5) == ("àbç gh def ", 11) + @test transpose!(10) == ("àbç def gh", 12) + edit_insert(buf, " ") + @test transpose!(13) == ("àbç gh def", 13) +end + +let s = new_state() + buf = buffer(s) - LineEdit.edit_insert(s,"first line\nsecond line\nthird line") - @test String(buf.data[1:buf.size]) == "first line\nsecond line\nthird line" + edit_insert(s,"first line\nsecond line\nthird line") + @test content(buf) == "first line\nsecond line\nthird line" ## edit_move_line_start/end ## seek(buf, 0) @@ -378,11 +434,11 @@ let s.key_repeats = 1 # Manually flag a repeated keypress LineEdit.edit_kill_line(s) s.key_repeats = 0 - @test String(buf.data[1:buf.size]) == "second line\nthird line" + @test content(buf) == "second line\nthird line" LineEdit.move_line_end(s) LineEdit.edit_move_right(s) LineEdit.edit_yank(s) - @test String(buf.data[1:buf.size]) == "second line\nfirst line\nthird line" + @test content(buf) == "second line\nfirst line\nthird line" end # Issue 7845 @@ -403,16 +459,16 @@ let end @testset "function prompt indentation" begin - term = TestHelpers.FakeTerminal(IOBuffer(), IOBuffer(), IOBuffer(), false) + s = new_state() + term = Base.LineEdit.terminal(s) # default prompt: PromptState.indent should not be set to a final fixed value - s = LineEdit.init_state(term, ModalInterface([Prompt("julia> ")])) ps::LineEdit.PromptState = s.mode_state[s.current_mode] @test ps.indent == -1 # the prompt is modified afterwards to a function ps.p.prompt = let i = 0 () -> ["Julia is Fun! > ", "> "][mod1(i+=1, 2)] # lengths are 16 and 2 end - buf = LineEdit.buffer(ps) + buf = buffer(ps) write(buf, "begin\n julia = :fun\nend") outbuf = IOBuffer() termbuf = Base.Terminals.TerminalBuffer(outbuf) @@ -422,8 +478,274 @@ end "\r\e[16C julia = :fun\n" * "\r\e[16Cend\r\e[19C" LineEdit.refresh_multi_line(termbuf, term, ps) - @test String(take!(copy(outbuf))) == + @test String(take!(outbuf)) == "\r\e[0K\e[1A\r\e[0K\e[1A\r\e[0K\e[1m> \e[0m\r\e[2Cbegin\n" * "\r\e[2C julia = :fun\n" * "\r\e[2Cend\r\e[5C" end + +@testset "tab/backspace alignment feature" begin + s = new_state() + move_left(s, n) = for x = 1:n + LineEdit.edit_move_left(s) + end + + edit_insert(s, "for x=1:10\n") + LineEdit.edit_tab(s) + @test content(s) == "for x=1:10\n " + LineEdit.edit_backspace(s, true, false) + @test content(s) == "for x=1:10\n" + edit_insert(s, " ") + @test position(s) == 13 + LineEdit.edit_tab(s) + @test content(s) == "for x=1:10\n " + edit_insert(s, " ") + LineEdit.edit_backspace(s, true, false) + @test content(s) == "for x=1:10\n " + edit_insert(s, "éé=3 ") + LineEdit.edit_tab(s) + @test content(s) == "for x=1:10\n éé=3 " + LineEdit.edit_backspace(s, true, false) + @test content(s) == "for x=1:10\n éé=3" + edit_insert(s, "\n 1∉x ") + LineEdit.edit_tab(s) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_backspace(s, false, false) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_backspace(s, true, false) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.edit_move_word_left(s) + LineEdit.edit_tab(s) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + LineEdit.move_line_start(s) + @test position(s) == 22 + LineEdit.edit_tab(s, true) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 30 + LineEdit.edit_move_left(s) + @test position(s) == 29 + LineEdit.edit_backspace(s, true, true) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 26 + LineEdit.edit_tab(s, false) # same as edit_tab(s, true) here + @test position(s) == 30 + move_left(s, 6) + @test position(s) == 24 + LineEdit.edit_backspace(s, true, true) + @test content(s) == "for x=1:10\n éé=3\n 1∉x " + @test position(s) == 22 + LineEdit.edit_kill_line(s) + edit_insert(s, ' '^10) + move_left(s, 7) + @test content(s) == "for x=1:10\n éé=3\n " + @test position(s) == 25 + LineEdit.edit_tab(s, true, false) + @test position(s) == 32 + move_left(s, 7) + LineEdit.edit_tab(s, true, true) + @test position(s) == 26 + @test content(s) == "for x=1:10\n éé=3\n " + # test again the same, when there is a next line + edit_insert(s, " \nend") + move_left(s, 11) + @test position(s) == 25 + LineEdit.edit_tab(s, true, false) + @test position(s) == 32 + move_left(s, 7) + LineEdit.edit_tab(s, true, true) + @test position(s) == 26 + @test content(s) == "for x=1:10\n éé=3\n \nend" +end + +@testset "newline alignment feature" begin + s = new_state() + edit_insert(s, "for x=1:10\n é = 1") + LineEdit.edit_insert_newline(s) + @test content(s) == "for x=1:10\n é = 1\n " + edit_insert(s, " b = 2") + LineEdit.edit_insert_newline(s) + @test content(s) == "for x=1:10\n é = 1\n b = 2\n " + # after an empty line, should still insert the expected number of spaces + LineEdit.edit_insert_newline(s) + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n " + LineEdit.edit_insert_newline(s, 0) + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n \n" + LineEdit.edit_insert_newline(s, 2) + @test content(s) == "for x=1:10\n é = 1\n b = 2\n \n \n\n " + # test when point before first letter of the line + for i=6:10 + LineEdit.edit_clear(s) + edit_insert(s, "begin\n x") + seek(LineEdit.buffer(s), i) + LineEdit.edit_insert_newline(s) + @test content(s) == "begin\n" * ' '^(i-6) * "\n x" + end +end + +@testset "change case on the right" begin + buf = IOBuffer() + edit_insert(buf, "aa bb CC") + seekstart(buf) + LineEdit.edit_upper_case(buf) + LineEdit.edit_title_case(buf) + @test String(take!(copy(buf))) == "AA Bb CC" + @test position(buf) == 5 + LineEdit.edit_lower_case(buf) + @test String(take!(copy(buf))) == "AA Bb cc" +end + +@testset "kill ring" begin + s = new_state() + buf = buffer(s) + edit_insert(s, "ça ≡ nothing") + @test transform!(LineEdit.edit_copy_region, s) == ("ça ≡ nothing", 12, 0) + @test s.kill_ring[end] == "ça ≡ nothing" + @test transform!(LineEdit.edit_exchange_point_and_mark, s)[2:3] == (0, 12) + charseek(buf, 8); setmark(s) + charseek(buf, 1) + @test transform!(LineEdit.edit_kill_region, s) == ("çhing", 1, 1) + @test s.kill_ring[end] == "a ≡ not" + charseek(buf, 0) + @test transform!(LineEdit.edit_yank, s) == ("a ≡ notçhing", 7, 0) + s.last_action = :unknown + # next action will fail, as yank-pop doesn't know a yank was just issued + @test transform!(LineEdit.edit_yank_pop, s) == ("a ≡ notçhing", 7, 0) + s.last_action = :edit_yank + # now this should work: + @test transform!(LineEdit.edit_yank_pop, s) == ("ça ≡ nothingçhing", 12, 0) + @test s.kill_idx == 1 + LineEdit.edit_kill_line(s) + @test s.kill_ring[end] == "çhing" + @test s.kill_idx == 3 + # repetition (concatenation of killed strings + edit_insert(s, "A B C") + LineEdit.edit_delete_prev_word(s) + s.key_repeats = 1 + LineEdit.edit_delete_prev_word(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" + LineEdit.edit_yank(s) + LineEdit.edit_werase(s) + @test s.kill_ring[end] == "C" + s.key_repeats = 1 + LineEdit.edit_werase(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" + LineEdit.edit_yank(s) + LineEdit.edit_move_word_left(s) + LineEdit.edit_move_word_left(s) + LineEdit.edit_delete_next_word(s) + @test s.kill_ring[end] == "B" + s.key_repeats = 1 + LineEdit.edit_delete_next_word(s) + s.key_repeats = 0 + @test s.kill_ring[end] == "B C" +end + +@testset "undo" begin + s = new_state() + edit!(f) = transform!(f, s)[1] + edit_undo! = LineEdit.edit_undo! + edit_redo! = LineEdit.edit_redo! + + edit_insert(s, "one two three") + + @test edit!(LineEdit.edit_delete_prev_word) == "one two " + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one two " + @test edit!(edit_undo!) == "one two three" + + edit_insert(s, " four") + @test edit!(s->edit_insert(s, " five")) == "one two three four five" + @test edit!(edit_undo!) == "one two three four" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one two three four" + @test edit!(edit_redo!) == "one two three four five" + @test edit!(edit_undo!) == "one two three four" + @test edit!(edit_undo!) == "one two three" + + @test edit!(LineEdit.edit_clear) == "" + @test edit!(edit_undo!) == "one two three" + + @test edit!(LineEdit.edit_insert_newline) == "one two three\n" + @test edit!(edit_undo!) == "one two three" + + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + @test edit!(LineEdit.edit_transpose_chars) == "one two there" + @test edit!(edit_undo!) == "one two three" + @test edit!(LineEdit.edit_transpose_words) == "one three two" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_start(s) + @test edit!(LineEdit.edit_kill_line) == "" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_start(s) + LineEdit.edit_kill_line(s) + LineEdit.edit_yank(s) + @test edit!(LineEdit.edit_yank) == "one two threeone two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == "" + @test edit!(edit_undo!) == "one two three" + + LineEdit.setmark(s) + LineEdit.edit_move_word_right(s) + @test edit!(LineEdit.edit_kill_region) == " two three" + @test edit!(LineEdit.edit_yank) == "one two three" + @test edit!(LineEdit.edit_yank_pop) == "one two three two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == " two three" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_end(s) + LineEdit.edit_backspace(s) + LineEdit.edit_backspace(s) + @test edit!(LineEdit.edit_backspace) == "one two th" + @test edit!(edit_undo!) == "one two thr" + @test edit!(edit_undo!) == "one two thre" + @test edit!(edit_undo!) == "one two three" + + LineEdit.push_undo(s) # TODO: incorporate push_undo into edit_splice! ? + LineEdit.edit_splice!(s, 4 => 7, "stott") + @test content(s) == "one stott three" + s.last_action = :not_undo + @test edit!(edit_undo!) == "one two three" + + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + LineEdit.edit_move_left(s) + @test edit!(LineEdit.edit_delete) == "one two thee" + @test edit!(edit_undo!) == "one two three" + + LineEdit.edit_move_word_left(s) + LineEdit.edit_werase(s) + @test edit!(LineEdit.edit_delete_next_word) == "one " + @test edit!(edit_undo!) == "one three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_redo!) == "one three" + @test edit!(edit_redo!) == "one " + @test edit!(edit_redo!) == "one " # nothing more to redo (this "beeps") + @test edit!(edit_undo!) == "one three" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_start(s) + @test edit!(LineEdit.edit_upper_case) == "ONE two three" + LineEdit.move_line_start(s) + @test edit!(LineEdit.edit_lower_case) == "one two three" + @test edit!(LineEdit.edit_title_case) == "one Two three" + @test edit!(edit_undo!) == "one two three" + @test edit!(edit_undo!) == "ONE two three" + @test edit!(edit_undo!) == "one two three" + + LineEdit.move_line_end(s) + edit_insert(s, " ") + @test edit!(LineEdit.edit_tab) == "one two three " + @test edit!(edit_undo!) == "one two three " + @test edit!(edit_undo!) == "one two three" + # TODO: add tests for complete_line, which don't work directly + + # pop initial insert of "one two three" + @test edit!(edit_undo!) == "" + @test edit!(edit_undo!) == "" # nothing more to undo (this "beeps") +end diff --git a/test/llvmpasses/gcroots.ll b/test/llvmpasses/gcroots.ll index 835a6e72d71c5..7a92e64a0e434 100644 --- a/test/llvmpasses/gcroots.ll +++ b/test/llvmpasses/gcroots.ll @@ -192,7 +192,7 @@ define %jl_value_t addrspace(10)* @no_redundant_rerooting(i64 %a, i1 %cond) { top: %ptls = call %jl_value_t*** @jl_get_ptls_states() %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) -; CHECK: store %jl_value_t addrspace(10)* %aboxed +; CHECK: store %jl_value_t addrspace(10)* %aboxed ; CHECK-NEXT: call void @jl_safepoint() call void @jl_safepoint() br i1 %cond, label %blocka, label %blockb @@ -207,3 +207,19 @@ blockb: call void @jl_safepoint() ret %jl_value_t addrspace(10)* %aboxed } + +declare void @llvm.memcpy.p064.p10i8.i64(i64*, i8 addrspace(10)*, i64, i32, i1) + +define void @memcpy_use(i64 %a, i64 *%aptr) { +; CHECK-LABEL: @memcpy_use +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 3 +top: + %ptls = call %jl_value_t*** @jl_get_ptls_states() + %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) +; CHECK: store %jl_value_t addrspace(10)* %aboxed + call void @jl_safepoint() + %acast = bitcast %jl_value_t addrspace(10)* %aboxed to i8 addrspace(10)* + call void @llvm.memcpy.p064.p10i8.i64(i64* %aptr, i8 addrspace(10)* %acast, i64 8, i32 1, i1 false) + ret void +} + diff --git a/test/math.jl b/test/math.jl index cebe2fa0ea4e2..f3d31b1782939 100644 --- a/test/math.jl +++ b/test/math.jl @@ -23,16 +23,16 @@ end @testset "constants" begin - @test pi != e - @test e != 1//2 - @test 1//2 <= e - @test e <= 15//3 - @test big(1//2) < e - @test e < big(20//6) - @test e^pi == exp(pi) - @test e^2 == exp(2) - @test e^2.4 == exp(2.4) - @test e^(2//3) == exp(2//3) + @test pi != ℯ + @test ℯ != 1//2 + @test 1//2 <= ℯ + @test ℯ <= 15//3 + @test big(1//2) < ℯ + @test ℯ < big(20//6) + @test ℯ^pi == exp(pi) + @test ℯ^2 == exp(2) + @test ℯ^2.4 == exp(2.4) + @test ℯ^(2//3) == exp(2//3) @test Float16(3.0) < pi @test pi < Float16(4.0) @@ -171,19 +171,19 @@ end @test isequal(cos(T(0)), T(1)) @test cos(T(pi)/2) ≈ T(0) atol=eps(T) @test isequal(cos(T(pi)), T(-1)) - @test exp(T(1)) ≈ T(e) atol=10*eps(T) + @test exp(T(1)) ≈ T(ℯ) atol=10*eps(T) @test isequal(exp10(T(1)), T(10)) @test isequal(exp2(T(1)), T(2)) @test isequal(expm1(T(0)), T(0)) - @test expm1(T(1)) ≈ T(e)-1 atol=10*eps(T) + @test expm1(T(1)) ≈ T(ℯ)-1 atol=10*eps(T) @test isequal(hypot(T(3),T(4)), T(5)) @test isequal(log(T(1)), T(0)) - @test isequal(log(e,T(1)), T(0)) - @test log(T(e)) ≈ T(1) atol=eps(T) + @test isequal(log(ℯ,T(1)), T(0)) + @test log(T(ℯ)) ≈ T(1) atol=eps(T) @test isequal(log10(T(1)), T(0)) @test isequal(log10(T(10)), T(1)) @test isequal(log1p(T(0)), T(0)) - @test log1p(T(e)-1) ≈ T(1) atol=eps(T) + @test log1p(T(ℯ)-1) ≈ T(1) atol=eps(T) @test isequal(log2(T(1)), T(0)) @test isequal(log2(T(2)), T(1)) @test isequal(sin(T(0)), T(0)) @@ -402,7 +402,7 @@ end end @testset "Irrational args to sinpi/cospi/sinc/cosc" begin - for x in (pi, e, golden) + for x in (pi, ℯ, Base.MathConstants.golden) @test sinpi(x) ≈ Float64(sinpi(big(x))) @test cospi(x) ≈ Float64(cospi(big(x))) @test sinc(x) ≈ Float64(sinc(big(x))) @@ -662,8 +662,8 @@ end @testset "test fallback definitions" begin @test exp10(5) ≈ exp10(5.0) @test exp10(50//10) ≈ exp10(5.0) - @test log10(exp10(e)) ≈ e - @test log(e) === 1 + @test log10(exp10(ℯ)) ≈ ℯ + @test log(ℯ) === 1 @test exp2(Float16(2.0)) ≈ exp2(2.0) @test exp2(Float16(1.0)) === Float16(exp2(1.0)) @test exp10(Float16(1.0)) === Float16(exp10(1.0)) @@ -683,3 +683,23 @@ let x = 2.0 @test exp2(Float22716(x)) === 2^x @test exp10(Float22716(x)) === 10^x end + +@testset "asin #23088" begin + for T in (Float32, Float64) + @test asin(zero(T)) === zero(T) + @test asin(-zero(T)) === -zero(T) + @test asin(nextfloat(zero(T))) === nextfloat(zero(T)) + @test asin(prevfloat(zero(T))) === prevfloat(zero(T)) + @test asin(one(T)) === T(pi)/2 + @test asin(-one(T)) === -T(pi)/2 + for x in (0.45, 0.6, 0.98) + by = T(asin(big(x))) + @test abs(asin(T(x)) - by)/eps(by) <= one(T) + bym = T(asin(big(-x))) + @test abs(asin(T(-x)) - bym)/eps(bym) <= one(T) + end + @test_throws DomainError asin(-T(Inf)) + @test_throws DomainError asin(T(Inf)) + @test asin(T(NaN)) === T(NaN) + end +end diff --git a/test/misc.jl b/test/misc.jl index 7ca7084a9fd77..8692d41cf122a 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -239,7 +239,7 @@ let redir_err = "redirect_stderr(STDOUT)" exename = Base.julia_cmd() script = "$redir_err; module A; f() = 1; end; A.f() = 1" - warning_str = read(`$exename --startup-file=no -e $script`, String) + warning_str = read(`$exename --warn-overwrite=yes --startup-file=no -e $script`, String) @test contains(warning_str, "f()") end @@ -574,9 +574,9 @@ if Sys.iswindows() x+y end - let addr = cfunction(WeVirtualProtectThisToRWX, UInt64, (UInt64, UInt64)) + let addr = cfunction(WeVirtualProtectThisToRWX, UInt64, Tuple{UInt64, UInt64}) addr = addr-(UInt64(addr)%4096) - const PAGE_EXECUTE_READWRITE = 0x40 + PAGE_EXECUTE_READWRITE = 0x40 oldPerm = Ref{UInt32}() err18083 = ccall(:VirtualProtect,stdcall,Cint, (Ptr{Void}, Csize_t, UInt32, Ptr{UInt32}), diff --git a/test/mpfr.jl b/test/mpfr.jl index 3344b4cc4a9bb..6fe67be343b16 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -382,17 +382,14 @@ setprecision(406) do @test string(nextfloat(BigFloat(1))) == str end setprecision(21) do - @test string(zero(BigFloat)) == "0.0000000" @test string(parse(BigFloat, "0.1")) == "1.0000002e-01" @test string(parse(BigFloat, "-9.9")) == "-9.9000015" end setprecision(40) do - @test string(zero(BigFloat)) == "0.0000000000000" @test string(parse(BigFloat, "0.1")) == "1.0000000000002e-01" @test string(parse(BigFloat, "-9.9")) == "-9.8999999999942" end setprecision(123) do - @test string(zero(BigFloat)) == "0.00000000000000000000000000000000000000" @test string(parse(BigFloat, "0.1")) == "9.99999999999999999999999999999999999953e-02" @test string(parse(BigFloat, "-9.9")) == "-9.8999999999999999999999999999999999997" end @@ -880,15 +877,43 @@ for prec in (10, 100, 1000) end end -setprecision(256) do - @test string(big(Inf)) == "BigFloat(Inf, 256)" - @test string(big(-Inf)) == "BigFloat(-Inf, 256)" - @test string(big(NaN)) == "BigFloat(NaN, 256)" -end - # issue #22758 -if MPFR.get_version() > v"3.1.5" || "r11590" in MPFR.get_patches() +if MPFR.version() > v"3.1.5" || "r11590" in MPFR.version().build setprecision(2_000_000) do @test abs(sin(big(pi)/6) - 0.5) < ldexp(big(1.0),-1_999_000) end end + +@testset "show BigFloat" begin + function test_show_bigfloat(x::BigFloat; contains_e::Bool=true, + ends::String="", + starts::String="") + sx = sprint(show, x) + scx = sprint(showcompact, x) + strx = string(x) + @test sx == strx + @test length(scx) < 20 + @test length(scx) <= length(sx) + @test x == parse(BigFloat, sx) + @test ≈(x, parse(BigFloat, scx), rtol=1e-4) + for s in (sx, scx) + @test contains(s, 'e') == contains_e + @test startswith(s, starts) + @test endswith(s, ends) + end + end + + test_show_bigfloat(big"1.23456789", contains_e=false, starts="1.23") + test_show_bigfloat(big"-1.23456789", contains_e=false, starts="-1.23") + test_show_bigfloat(big"2.3457645687563543266576889678956787e10000", starts="2.345", ends="e+10000") + test_show_bigfloat(big"-2.3457645687563543266576889678956787e-10000", starts="-2.345", ends="e-10000") + + for to_string in [string, + x->sprint(show, x), + x->sprint(showcompact,x)] + @test to_string(big"0.0") == "0.0" + @test to_string(big"-0.0") == "-0.0" + @test to_string(big"1.0") == "1.0" + @test to_string(big"-1.0") == "-1.0" + end +end diff --git a/test/numbers.jl b/test/numbers.jl index 45603503c66bf..2452ac50172df 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -1,5 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +using Base.MathConstants const ≣ = isequal # convenient for comparing NaNs # basic booleans @@ -815,8 +816,8 @@ function _cmp_(x::Union{Int64,UInt64}, y::Float64) error("invalid: _cmp_($x,$y)") end -for x=Int64(2)^53-2:Int64(2)^53+5, - y=[2.0^53-2 2.0^53-1 2.0^53 2.0^53+2 2.0^53+4] +for x = Int64(2)^53-2:Int64(2)^53+5, + y = [2.0^53-2 2.0^53-1 2.0^53 2.0^53+2 2.0^53+4] u = UInt64(x) @test y == Float64(trunc(Int64,y)) @@ -1991,34 +1992,38 @@ for x in (12345.6789, 0, -12345.6789) @test y == floor(x, 1000) @test y == ceil(x, 1000) end -x = 12345.6789 -@test 0.0 == trunc(x, -1000) -@test 0.0 == round(x, -1000) -@test 0.0 == floor(x, -1000) -@test Inf == ceil(x, -1000) -x = -12345.6789 -@test -0.0 == trunc(x, -1000) -@test -0.0 == round(x, -1000) -@test -Inf == floor(x, -1000) -@test -0.0 == ceil(x, -1000) -x = 0.0 -@test 0.0 == trunc(x, -1000) -@test 0.0 == round(x, -1000) -@test 0.0 == floor(x, -1000) -@test 0.0 == ceil(x, -1000) +let x = 12345.6789 + @test 0.0 == trunc(x, -1000) + @test 0.0 == round(x, -1000) + @test 0.0 == floor(x, -1000) + @test Inf == ceil(x, -1000) +end +let x = -12345.6789 + @test -0.0 == trunc(x, -1000) + @test -0.0 == round(x, -1000) + @test -Inf == floor(x, -1000) + @test -0.0 == ceil(x, -1000) +end +let x = 0.0 + @test 0.0 == trunc(x, -1000) + @test 0.0 == round(x, -1000) + @test 0.0 == floor(x, -1000) + @test 0.0 == ceil(x, -1000) +end # rounding in other bases @test approx_eq(round(pi,2,2), 3.25) @test approx_eq(round(pi,3,2), 3.125) @test approx_eq(round(pi,3,5), 3.144) # vectorized trunc/round/floor/ceil with digits/base argument -a = rand(2, 2, 2) -for f in (round, trunc, floor, ceil) - @test f.(a[:, 1, 1], 2) == map(x->f(x, 2), a[:, 1, 1]) - @test f.(a[:, :, 1], 2) == map(x->f(x, 2), a[:, :, 1]) - @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) - @test f.(a[:, 1, 1], 9, 2) == map(x->f(x, 9, 2), a[:, 1, 1]) - @test f.(a[:, :, 1], 9, 2) == map(x->f(x, 9, 2), a[:, :, 1]) - @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) +let a = rand(2, 2, 2) + for f in (round, trunc, floor, ceil) + @test f.(a[:, 1, 1], 2) == map(x->f(x, 2), a[:, 1, 1]) + @test f.(a[:, :, 1], 2) == map(x->f(x, 2), a[:, :, 1]) + @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) + @test f.(a[:, 1, 1], 9, 2) == map(x->f(x, 9, 2), a[:, 1, 1]) + @test f.(a[:, :, 1], 9, 2) == map(x->f(x, 9, 2), a[:, :, 1]) + @test f.(a, 9, 2) == map(x->f(x, 9, 2), a) + end end # significant digits (would be nice to have a smart vectorized # version of signif) @@ -2487,7 +2492,7 @@ z2 = read(zbuf, Complex128) @test bswap(z2) === 3.5 - 4.5im #isreal(x::Real) = true -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test isreal(x) == true end @@ -2522,7 +2527,7 @@ let number_types = Set() end #getindex(x::Number) = x -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test getindex(x) == x @test getindex(x, 1, 1) == x end @@ -2533,7 +2538,7 @@ end #getindex(x::Array,-1) throws BoundsError #getindex(x::Array,0 throws BoundsError #getindex(x::Array,length(x::Array)+1) throws BoundsError -for x in [1.23, 7, e, 4//5] #[FP, Int, Irrational, Rat] +for x in [1.23, 7, ℯ, 4//5] #[FP, Int, Irrational, Rat] @test_throws BoundsError getindex(x,-1) @test_throws BoundsError getindex(x,0) @test_throws BoundsError getindex(x,2) @@ -2545,8 +2550,8 @@ end # copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, x) # flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, x) -for x in [1.23, 7, e, 4//5] - for y in [1.23, 7, e, 4//5] +for x in [1.23, 7, ℯ, 4//5] + for y in [1.23, 7, ℯ, 4//5] @test copysign(x, y) == x @test copysign(x, -y) == -x @test copysign(-x, y) == x @@ -2566,7 +2571,7 @@ end #in(x::Number, y::Number) = x == y @test in(3,3) == true #Int @test in(2.0,2.0) == true #FP -@test in(e,e) == true #Const +@test in(ℯ,ℯ) == true #Const @test in(4//5,4//5) == true #Rat @test in(1+2im, 1+2im) == true #Imag @test in(3, 3.0) == true #mixed @@ -2948,7 +2953,7 @@ end end @test !iszero(nextfloat(BigFloat(0))) @test !isone(nextfloat(BigFloat(1))) - for x in (π, e, γ, catalan, φ) + for x in (π, ℯ, γ, catalan, φ) @test !iszero(x) @test !isone(x) end @@ -2975,3 +2980,63 @@ f20065(B, i) = UInt8(B[i]) end @test inv(3//4) === 4//3 === 1 / (3//4) === 1 // (3//4) + +# issues #23244 & #23250 +@testset "convert preserves NaN payloads" begin + @testset "smallest NaNs" begin + @test convert(Float32, NaN16) === NaN32 + @test convert(Float32, -NaN16) === -NaN32 + @test convert(Float64, NaN16) === NaN64 + @test convert(Float64, -NaN16) === -NaN64 + @test convert(Float16, NaN32) === NaN16 + @test convert(Float16, -NaN32) === -NaN16 + @test convert(Float64, NaN32) === NaN64 + @test convert(Float64, -NaN32) === -NaN64 + @test convert(Float32, NaN64) === NaN32 + @test convert(Float32, -NaN64) === -NaN32 + @test convert(Float16, NaN64) === NaN16 + @test convert(Float16, -NaN64) === -NaN16 + end + + @testset "largest NaNs" begin + @test convert(Float32, reinterpret(Float16, typemax(UInt16))) === + reinterpret(Float32, typemax(UInt32) >> 13 << 13) + @test convert(Float64, reinterpret(Float16, typemax(UInt16))) === + reinterpret(Float64, typemax(UInt64) >> 42 << 42) + @test convert(Float16, reinterpret(Float32, typemax(UInt32))) === + reinterpret(Float16, typemax(UInt16) >> 00 << 00) + @test convert(Float64, reinterpret(Float32, typemax(UInt32))) === + reinterpret(Float64, typemax(UInt64) >> 29 << 29) + @test convert(Float32, reinterpret(Float64, typemax(UInt64))) === + reinterpret(Float32, typemax(UInt32) >> 00 << 00) + @test convert(Float16, reinterpret(Float64, typemax(UInt64))) === + reinterpret(Float16, typemax(UInt16) >> 00 << 00) + end + + @testset "random NaNs" begin + nans = AbstractFloat[NaN16, NaN32, NaN64] + F = [Float16, Float32, Float64] + U = [UInt16, UInt32, UInt64] + sig = [11, 24, 53] + for i = 1:length(F), j = 1:length(F) + for _ = 1:100 + nan = reinterpret(F[i], rand(U[i]) | reinterpret(U[i], nans[i])) + z = sig[i] - sig[j] + nan′ = i <= j ? nan : reinterpret(F[i], reinterpret(U[i], nan) >> z << z) + @test convert(F[i], convert(F[j], nan)) === nan′ + end + end + end +end + +@testset "printing non finite floats" for T in subtypes(AbstractFloat) + for (x, sx) in [(T(NaN), "NaN"), + (-T(NaN), "NaN"), + (T(Inf), "Inf"), + (-T(Inf), "-Inf")] + @assert x isa T + @test string(x) == sx + @test sprint(io -> show(IOContext(io, :compact => true), x)) == sx + @test sprint(print, x) == sx + end +end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 46ace5061822f..b2c642cdfced3 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -147,7 +147,7 @@ smry = summary(v) @test contains(smry, "OffsetArray{Float64,1") @test contains(smry, "with indices -1:1") function cmp_showf(printfunc, io, A) - ioc = IOContext(IOContext(io, :limit => true), :compact => true) + ioc = IOContext(io, :limit => true, :compact => true) printfunc(ioc, A) str1 = String(take!(io)) printfunc(ioc, parent(A)) diff --git a/test/operators.jl b/test/operators.jl index 72effdbf33f51..e661968bf8e43 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -49,8 +49,6 @@ p = 1=>:foo @test xor(2) == 2 @test (⊻)(2) == 2 -# @test ctranspose('a') == 'a' # (c)transpose of Chars no longer supported - @test_throws ArgumentError Base.scalarmin(['a','b'],['c','d']) @test_throws ArgumentError Base.scalarmin('a',['c','d']) @test_throws ArgumentError Base.scalarmin(['a','b'],'c') diff --git a/test/parse.jl b/test/parse.jl index befe1913f55fc..a2a623c50d123 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -201,6 +201,7 @@ macro test999_str(args...); args; end # Issue 20587 for T in vcat(subtypes(Signed), subtypes(Unsigned)) + T === BigInt && continue # TODO: make BigInt pass this test for s in ["", " ", " "] # Without a base (handles things like "0x00001111", etc) result = @test_throws ArgumentError parse(T, s) @@ -514,7 +515,7 @@ let b = IOBuffer(""" end f() """) - @test Base.parse_input_line(b) == Expr(:let, Expr(:block, LineNumberNode(2, :none), :x), Expr(:(=), :x, :x)) + @test Base.parse_input_line(b) == Expr(:let, Expr(:(=), :x, :x), Expr(:block, LineNumberNode(2, :none), :x)) @test Base.parse_input_line(b) == Expr(:call, :f) @test Base.parse_input_line(b) === nothing end @@ -565,10 +566,10 @@ add_method_to_glob_fn!() @test expand(Main, :(f(d:Int...) = nothing)) == Expr(:error, "\"d:Int\" is not a valid function argument name") # issue #16517 -@test (try error(); catch 0; end) === 0 -@test (try error(); catch false; end) === false # false and true are Bool literals, not variables -@test (try error(); catch true; end) === true -f16517() = try error(); catch 0; end +@test (try error(); catch; 0; end) === 0 +@test (try error(); catch; false; end) === false # false and true are Bool literals, not variables +@test (try error(); catch; true; end) === true +f16517() = try error(); catch; 0; end @test f16517() === 0 # issue #16671 @@ -592,7 +593,7 @@ end # issue #16686 @test parse("try x - catch test() + catch; test() y end") == Expr(:try, Expr(:block, @@ -771,19 +772,29 @@ macro iter() end end let ex = expand(M16096, :(@iter)) - @test isa(ex, Expr) && ex.head === :body + @test isa(ex, Expr) && ex.head === :thunk end let ex = expand(Main, :($M16096.@iter)) - @test isa(ex, Expr) && ex.head === :body + @test isa(ex, Expr) && ex.head === :thunk end -let ex = expand(@__MODULE__, :(@M16096.iter)) - @test isa(ex, Expr) && ex.head === :body +let thismodule = @__MODULE__, + ex = expand(thismodule, :(@M16096.iter)) + @test isa(ex, Expr) && ex.head === :thunk @test !isdefined(M16096, :foo16096) - @test eval(@__MODULE__, ex) === nothing + local_foo16096 = eval(@__MODULE__, ex) + @test local_foo16096(2.0) == 1 @test !@isdefined foo16096 - @test isdefined(M16096, :foo16096) + @test !@isdefined it + @test !isdefined(M16096, :foo16096) + @test !isdefined(M16096, :it) + @test typeof(local_foo16096).name.module === thismodule + @test typeof(local_foo16096).name.mt.module === thismodule + @test getfield(thismodule, typeof(local_foo16096).name.mt.name) === local_foo16096 + @test getfield(thismodule, typeof(local_foo16096).name.name) === typeof(local_foo16096) + @test !isdefined(M16096, typeof(local_foo16096).name.mt.name) + @test !isdefined(M16096, typeof(local_foo16096).name.name) end -@test M16096.foo16096(2.0) == 1 + macro f16096() quote g16096($(esc(:x))) = 2x @@ -794,7 +805,7 @@ let g = @f16096 end macro f16096_2() quote - g16096_2(;$(esc(:x))=2) = 2x + g16096_2(; $(esc(:x))=2) = 2x end end let g = @f16096_2 @@ -1300,3 +1311,20 @@ end # issue #23173 @test_throws ErrorException("invalid module path") eval(:(import $(:.))) + +# issue #23234 +let + f = function (x=0) + x + end + @test f() == 0 + @test f(2) == 2 +end + +# issue #18730 +@test expand(Main, quote + function f() + local Int + x::Int -> 2 + end + end) == Expr(:error, "local variable Int cannot be used in closure declaration") diff --git a/test/path.jl b/test/path.jl index 3a27bcb271ab6..e2a2b34699aaf 100644 --- a/test/path.jl +++ b/test/path.jl @@ -10,6 +10,7 @@ for S in (String, GenericString) @test basename(S("foo$(sep)bar")) == "bar" @test dirname(S("foo$(sep)bar")) == "foo" + @test expanduser(S("")) == "" @test expanduser(S("x")) == "x" @test expanduser(S("~")) == (Sys.iswindows() ? "~" : homedir()) @@ -27,6 +28,16 @@ for S in (String, GenericString) @test joinpath(S("foo"), S(homedir())) == homedir() @test joinpath(S(abspath("foo")), S(homedir())) == homedir() + if Sys.iswindows() + @test joinpath(S("foo"),S("bar:baz")) == "bar:baz" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "D:bar" + @test joinpath(S("C:"),S("foo"),S("D:bar"),S("baz")) == "D:bar$(sep)baz" + elseif Sys.isunix() + @test joinpath(S("foo"),S("bar:baz")) == "foo$(sep)bar:baz" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "C:$(sep)foo$(sep)D:$(sep)bar" + @test joinpath(S("C:"),S("foo"),S("D:"),S("bar"),S("baz")) == "C:$(sep)foo$(sep)D:$(sep)bar$(sep)baz" + end + @test normpath(S(joinpath("."))) == "." @test normpath(S(joinpath(".."))) == ".." @test normpath(S(joinpath("..","."))) == ".." diff --git a/test/perf/array/indexing.jl b/test/perf/array/indexing.jl index 36527b0036a8d..eda9b1a71541a 100644 --- a/test/perf/array/indexing.jl +++ b/test/perf/array/indexing.jl @@ -112,7 +112,7 @@ end struct ArrayLSLS{T,N} <: MyArray{T,N} # IndexCartesian with IndexCartesian similar data::Array{T,N} end -Base.similar{T}(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) = ArrayLSLS(similar(A.data, T, dims)) +Base.similar(A::ArrayLSLS, ::Type{T}, dims::Tuple{Vararg{Int}}) where {T} = ArrayLSLS(similar(A.data, T, dims)) @inline Base.setindex!(A::ArrayLSLS, v, I::Int...) = A.data[I...] = v @inline Base.unsafe_setindex!(A::ArrayLSLS, v, I::Int...) = Base.unsafe_setindex!(A.data, v, I...) Base.first(A::ArrayLSLS) = first(A.data) @@ -140,15 +140,15 @@ Base.size(A::MyArray) = size(A.data) @inline Base.unsafe_getindex(A::ArrayLF, indx::Int) = unsafe_getindex(A.data, indx) @inline Base.unsafe_getindex(A::Union{ArrayLS, ArrayLSLS}, i::Int, j::Int) = unsafe_getindex(A.data, i, j) -@inline Base.getindex{T}(A::ArrayStrides{T,2}, i::Real, j::Real) = getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) +@inline Base.getindex(A::ArrayStrides{T,2}, i::Real, j::Real) where {T} = getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) @inline Base.getindex(A::ArrayStrides1, i::Real, j::Real) = getindex(A.data, i + A.stride1*(j-1)) -@inline Base.unsafe_getindex{T}(A::ArrayStrides{T,2}, i::Real, j::Real) = unsafe_getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) +@inline Base.unsafe_getindex(A::ArrayStrides{T,2}, i::Real, j::Real) where {T} = unsafe_getindex(A.data, 1+A.strides[1]*(i-1)+A.strides[2]*(j-1)) @inline Base.unsafe_getindex(A::ArrayStrides1, i::Real, j::Real) = unsafe_getindex(A.data, i + A.stride1*(j-1)) # Using the qualified Base.IndexLinear() in the IndexStyle definition # requires looking up the symbol in the module on each call. import Base: IndexLinear -Base.IndexStyle{T<:ArrayLF}(::Type{T}) = IndexLinear() +Base.IndexStyle(::Type{T}) where {T<:ArrayLF} = IndexLinear() if !applicable(unsafe_getindex, [1 2], 1:1, 2) @inline Base.unsafe_getindex(A::Array, I...) = @inbounds return A[I...] @@ -157,7 +157,7 @@ if !applicable(unsafe_getindex, [1 2], 1:1, 2) @inline Base.unsafe_getindex(A::BitArray, I1::BitArray, I2::Int) = unsafe_getindex(A, Base.to_index(I1), I2) end -function makearrays{T}(::Type{T}, sz) +function makearrays(::Type{T}, sz) where T L = prod(sz) A = reshape(convert(Vector{T}, [1:L;]), sz) AS = ArrayLS(A) diff --git a/test/perf/shootout/binary_trees.jl b/test/perf/shootout/binary_trees.jl index 042fd81568514..f81469ea00a3f 100644 --- a/test/perf/shootout/binary_trees.jl +++ b/test/perf/shootout/binary_trees.jl @@ -44,9 +44,9 @@ function loop_depths(d, min_depth, max_depth) end function binary_trees(N::Int=10) - const min_depth = 4 - const max_depth = N - const stretch_depth = max_depth + 1 + min_depth = 4 + max_depth = N + stretch_depth = max_depth + 1 # create and check stretch tree let c = check(make(0, stretch_depth)) diff --git a/test/perf/threads/lbm3d/lbm3d.jl b/test/perf/threads/lbm3d/lbm3d.jl index 14df2cea3c46f..11fcee4c37c92 100644 --- a/test/perf/threads/lbm3d/lbm3d.jl +++ b/test/perf/threads/lbm3d/lbm3d.jl @@ -119,11 +119,11 @@ end precompile(calc_equi!, (Array{Float64,4}, Array{Float64,4}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Array{Float64,2}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Array{Float64,3}, Int64, Int64, Int64, Float64)) function lbm3d(n) - const nx = n - const ny = nx - const nz = nx - const omega = 1.0 - const density = 1.0 + nx = n + ny = nx + nz = nx + omega = 1.0 + density = 1.0 # Implementation note: setting nchunk to nthreads() is a hack # to simulate the previous implementation's use of parallel regions. diff --git a/test/perf/threads/stockcorr/pstockcorr.jl b/test/perf/threads/stockcorr/pstockcorr.jl index 0b00c158f82e8..33e055dc5006e 100644 --- a/test/perf/threads/stockcorr/pstockcorr.jl +++ b/test/perf/threads/stockcorr/pstockcorr.jl @@ -61,15 +61,15 @@ end # Threaded version function pstockcorr(n) ## Correlated asset information - const CurrentPrice = [78. 102.] # Initial Prices of the two stocks - const Corr = [1. 0.4; 0.4 1.] # Correlation Matrix - const T = 500 # Number of days to simulate = 2years = 500days - const dt = 1/250 # Time step (1year = 250days) - const Div=[0.01 0.01] # Dividend - const Vol=[0.2 0.3] # Volatility + CurrentPrice = [78. 102.] # Initial Prices of the two stocks + Corr = [1. 0.4; 0.4 1.] # Correlation Matrix + T = 500 # Number of days to simulate = 2years = 500days + dt = 1/250 # Time step (1year = 250days) + Div=[0.01 0.01] # Dividend + Vol=[0.2 0.3] # Volatility ## Market Information - const r = 0.03 # Risk-free rate + r = 0.03 # Risk-free rate ## Define storages SimulPriceA = zeros(T,n) # Simulated Price of Asset A @@ -78,7 +78,7 @@ function pstockcorr(n) SimulPriceB[1,:] = CurrentPrice[2] ## Generating the paths of stock prices by Geometric Brownian Motion - const UpperTriangle = full(chol(Corr)) # UpperTriangle Matrix by Cholesky decomposition + UpperTriangle = full(chol(Corr)) # UpperTriangle Matrix by Cholesky decomposition # Optimization: pre-allocate these for performance # NOTE: the new GC will hopefully fix this, but currently GC time diff --git a/test/pkg.jl b/test/pkg.jl index 937ec7bba2c72..4d2548f253d87 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -65,14 +65,6 @@ temp_pkg_dir() do @test_throws PkgError Pkg.installed("MyFakePackage") @test Pkg.installed("Example") === nothing - # check that versioninfo(io; verbose=true) doesn't error and produces some output - # (done here since it calls Pkg.status which might error or clone metadata) - buf = PipeBuffer() - versioninfo(buf, verbose=true) - ver = read(buf, String) - @test startswith(ver, "Julia Version $VERSION") - @test contains(ver, "Environment:") - # Check that setprotocol! works. begin try @@ -480,7 +472,7 @@ temp_pkg_dir() do nothingtodomsg) Pkg.update("Example") metadata_dir = Pkg.dir("METADATA") - const old_commit = "313bfaafa301e82d40574a778720e893c559a7e2" + old_commit = "313bfaafa301e82d40574a778720e893c559a7e2" # Force a METADATA rollback to an old version, so that we will install some # outdated versions of some packages and then update some of those @@ -525,7 +517,7 @@ temp_pkg_dir() do Pkg.rm(package) # Remove package if installed metadata_dir = Pkg.dir("METADATA") - const old_commit = "83ff7116e51fc9cdbd7e67affbd344b9f5c9dbf2" + old_commit = "83ff7116e51fc9cdbd7e67affbd344b9f5c9dbf2" # Reset METADATA to the second to last update of Example.jl LibGit2.with(LibGit2.GitRepo, metadata_dir) do repo @@ -580,6 +572,23 @@ temp_pkg_dir() do @test contains(msg, "INFO: Main.JULIA_RC_LOADED defined true") end end + + let package = "Output" + stdout_file = Pkg.dir(package, "stdout.txt") + stderr_file = Pkg.dir(package, "stderr.txt") + content = """ + println(STDOUT, "stdout") + println(STDERR, "stderr") + """ + write_build(package, content) + + code = "Pkg.build(\"$package\")" + msg = run(pipeline( + `$(Base.julia_cmd()) --startup-file=no -e $code`, + stdout=stdout_file, stderr=stderr_file)) + @test last(readlines(stdout_file)) == "stdout" + @test last(readlines(stderr_file)) == "stderr" + end end @testset "Pkg functions with .jl extension" begin diff --git a/test/random.jl b/test/random.jl index fd885163d8e77..87382784c2ce9 100644 --- a/test/random.jl +++ b/test/random.jl @@ -38,8 +38,7 @@ A = zeros(UInt128, 2, 2) @test_throws BoundsError rand!(MersenneTwister(0), A, 5) # rand from AbstractArray -let mt = MersenneTwister(0) - srand(mt) +let mt = MersenneTwister() @test rand(mt, 0:3:1000) in 0:3:1000 @test issubset(rand!(mt, Array{Int}(100), 0:3:1000), 0:3:1000) coll = Any[2, UInt128(128), big(619), "string"] @@ -292,6 +291,7 @@ let mt = MersenneTwister(0) c = unsafe_wrap(Array, Ptr{Float64}(pc8), 1000) # Int(pointer(c)) % 16 == 8 for A in (a, b, c) + local A srand(mt, 0) rand(mt) # this is to fill mt.vals, cf. #9040 rand!(mt, A) # must not segfault even if Int(pointer(A)) % 16 != 0 @@ -308,7 +308,7 @@ end for rng in ([], [MersenneTwister(0)], [RandomDevice()]) ftypes = [Float16, Float32, Float64] cftypes = [Complex32, Complex64, Complex128, ftypes...] - types = [Bool, Char, Base.BitInteger_types..., ftypes...] + types = [Bool, Char, BigFloat, Base.BitInteger_types..., ftypes...] randset = Set(rand(Int, 20)) randdict = Dict(zip(rand(Int,10), rand(Int, 10))) collections = [IntSet(rand(1:100, 20)) => Int, @@ -322,6 +322,9 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) Float64 => Float64, "qwèrtï" => Char, GenericString("qwèrtï") => Char] + functypes = Dict(rand => types, randn => cftypes, randexp => ftypes, + rand! => types, randn! => cftypes, randexp! => ftypes) + b2 = big(2) u3 = UInt(3) for f in [rand, randn, randexp] @@ -330,7 +333,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) f(rng..., 2, 3) ::Array{Float64, 2} f(rng..., b2, u3) ::Array{Float64, 2} f(rng..., (2, 3)) ::Array{Float64, 2} - for T in (f === rand ? types : f === randn ? cftypes : ftypes) + for T in functypes[f] a0 = f(rng..., T) ::T a1 = f(rng..., T, 5) ::Vector{T} a2 = f(rng..., T, 2, 3) ::Array{T, 2} @@ -370,9 +373,10 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) @test_throws ArgumentError rand(rng..., C, 5) end for f! in [rand!, randn!, randexp!] - for T in (f! === rand! ? types : f! === randn! ? cftypes : ftypes) + for T in functypes[f!] X = T == Bool ? T[0,1] : T[0,1,2] for A in (Array{T}(5), Array{T}(2, 3), GenericArray{T}(5), GenericArray{T}(2, 3)) + local A f!(rng..., A) ::typeof(A) if f! === rand! f!(rng..., A, X) ::typeof(A) @@ -393,6 +397,7 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) # Test that you cannot call randn or randexp with non-Float types. for r in [randn, randexp, randn!, randexp!] + local r @test_throws MethodError r(Int) @test_throws MethodError r(Int32) @test_throws MethodError r(Bool) @@ -409,17 +414,19 @@ for rng in ([], [MersenneTwister(0)], [RandomDevice()]) end end -function hist(X,n) - v = zeros(Int,n) +function hist(X, n) + v = zeros(Int, n) for x in X - v[floor(Int,x*n)+1] += 1 + v[floor(Int, x*n) + 1] += 1 end v end # test uniform distribution of floats -for rng in [srand(MersenneTwister(0)), RandomDevice()] - for T in [Float16,Float32,Float64] +for rng in [MersenneTwister(), RandomDevice()], + T in [Float16, Float32, Float64, BigFloat], + prec in (T == BigFloat ? [3, 53, 64, 100, 256, 1000] : [256]) + setprecision(BigFloat, prec) do # array version counts = hist(rand(rng, T, 2000), 4) @test minimum(counts) > 300 # should fail with proba < 1e-26 @@ -581,3 +588,32 @@ end # this shouldn't crash (#22403) @test_throws MethodError rand!(Union{UInt,Int}[1, 2, 3]) + +@testset "$RNG() & srand(rng::$RNG) initializes randomly" for RNG in (MersenneTwister, RandomDevice) + m = RNG() + a = rand(m, Int) + m = RNG() + @test rand(m, Int) != a + # passing `nothing` is equivalent to passing nothing + m = RNG(nothing) + b = rand(m, Int) + @test b != a + srand(m) + c = rand(m, Int) + @test c ∉ (a, b) + srand(m) + @test rand(m, Int) ∉ (a, b, c) + srand(m, nothing) + d = rand(m, Int) + @test d ∉ (a, b, c) + srand(m, nothing) + @test rand(m, Int) ∉ (a, b, c, d) +end + +@testset "MersenneTwister($seed_) & srand(m::MersenneTwister, $seed_) produce the same stream" for seed_ in [0:5; 10000:10005] + # "seed_" instead of "seed" because `seed` is a global variable in this file, and there is an "overwriting" warning + m = MersenneTwister(seed_) + a = [rand(m) for _=1:100] + srand(m, seed_) + @test a == [rand(m) for _=1:100] +end diff --git a/test/ranges.jl b/test/ranges.jl index 6ec04c6f89038..a318f04b2c0f0 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1,6 +1,189 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -# ranges +# Because ranges rely on high precision arithmetic, test those utilities first +for (I, T) in ((Int16, Float16), (Int32, Float32), (Int64, Float64)), i = 1:10^3 + i = rand(I) >> 1 # test large values below + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) +end +for (I, T) in ((Int16, Float16), (Int32, Float32), (Int64, Float64)) + x = T(typemax(I)) + Δi = ceil(I, eps(x)) + for i = typemax(I)-2Δi:typemax(I)-Δi + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) + end + for i = typemin(I):typemin(I)+Δi + hi, lo = Base.splitprec(T, i) + @test widen(hi) + widen(lo) == i + @test endswith(bits(hi), repeat('0', Base.Math.significand_bits(T) ÷ 2)) + end +end + +# Compare precision in a manner sensitive to subnormals, which lose +# precision compared to widening. +function cmp_sn(w, hi, lo, slopbits=0) + if !isfinite(hi) + if abs(w) > realmax(typeof(hi)) + return isinf(hi) && sign(w) == sign(hi) + end + if isnan(w) && isnan(hi) + return true + end + return w == hi + end + if abs(w) < subnormalmin(typeof(hi)) + return (hi == zero(hi) || abs(w - widen(hi)) < abs(w)) && lo == zero(hi) + end + # Compare w == hi + lo unless `lo` issubnormal + z = widen(hi) + widen(lo) + if !issubnormal(lo) && lo != 0 + if slopbits == 0 + return z == w + end + wr, zr = roundshift(w, slopbits), roundshift(z, slopbits) + return max(wr-1, zero(wr)) <= zr <= wr+1 + end + # round w to the same number of bits as z + zu = asbits(z) + wu = asbits(w) + lastbit = false + while zu > 0 && !isodd(zu) + lastbit = isodd(wu) + zu = zu >> 1 + wu = wu >> 1 + end + return wu <= zu <= wu + lastbit +end + +asbits(x) = reinterpret(Base.uinttype(typeof(x)), x) + +function roundshift(x, n) + xu = asbits(x) + lastbit = false + for i = 1:n + lastbit = isodd(xu) + xu = xu >> 1 + end + xu + lastbit +end + +subnormalmin(::Type{T}) where T = reinterpret(T, Base.uinttype(T)(1)) + +function highprec_pair(x, y) + slopbits = (Base.Math.significand_bits(typeof(widen(x))) + 1) - + 2*(Base.Math.significand_bits(typeof(x)) + 1) + hi, lo = Base.add12(x, y) + @test cmp_sn(widen(x) + widen(y), hi, lo) + hi, lo = Base.mul12(x, y) + @test cmp_sn(widen(x) * widen(y), hi, lo) + y == 0 && return nothing + hi, lo = Base.div12(x, y) + @test cmp_sn(widen(x) / widen(y), hi, lo, slopbits) + nothing +end + +# # This tests every possible pair of Float16s. It takes too long for +# # ordinary use, which is why it's commented out. +# function pair16() +# for yu in 0x0000:0xffff +# for xu in 0x0000:0xffff +# x, y = reinterpret(Float16, xu), reinterpret(Float16, yu) +# highprec_pair(x, y) +# end +# end +# end + +for T in (Float16, Float32) # skip Float64 (bit representation of BigFloat is not available) + for i = 1:10^5 + x, y = rand(T), rand(T) + highprec_pair(x, y) + highprec_pair(-x, y) + highprec_pair(x, -y) + highprec_pair(-x, -y) + end + # Make sure we test dynamic range too + for i = 1:10^5 + x, y = rand(T), rand(T) + x == 0 || y == 0 && continue + x, y = log(x), log(y) + highprec_pair(x, y) + end +end + +asww(x) = widen(widen(x.hi)) + widen(widen(x.lo)) +astuple(x) = (x.hi, x.lo) + +function cmp_sn2(w, hi, lo, slopbits=0) + if !isfinite(hi) + if abs(w) > realmax(typeof(hi)) + return isinf(hi) && sign(w) == sign(hi) + end + if isnan(w) && isnan(hi) + return true + end + return w == hi + end + if abs(w) < subnormalmin(typeof(hi)) + return (hi == zero(hi) || abs(w - widen(hi)) < abs(w)) && lo == zero(hi) + end + z = widen(hi) + widen(lo) + zu, wu = asbits(z), asbits(w) + while zu > 0 && !isodd(zu) + zu = zu >> 1 + wu = wu >> 1 + end + zu = zu >> slopbits + wu = wu >> slopbits + return wu - 1 <= zu <= wu + 1 +end + +# TwicePrecision test. These routines lose accuracy if you form +# intermediate subnormals; with Float16, this happens so frequently, +# let's only test Float32. +let T = Float32 + Tw = widen(T) + slopbits = (Base.Math.significand_bits(Tw) + 1) - + 2*(Base.Math.significand_bits(T) + 1) + for i = 1:10^5 + x = Base.TwicePrecision{T}(rand()) + y = Base.TwicePrecision{T}(rand()) + xw, yw = asww(x), asww(y) + @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) + @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) + @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) + @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) + y = rand(T) + yw = widen(widen(y)) + @test cmp_sn2(Tw(xw+yw), astuple(x+y)..., slopbits) + @test cmp_sn2(Tw(xw-yw), astuple(x-y)..., slopbits) + @test cmp_sn2(Tw(xw*yw), astuple(x*y)..., slopbits) + @test cmp_sn2(Tw(xw/yw), astuple(x/y)..., slopbits) + end +end + +x1 = Base.TwicePrecision{Float64}(1) +x0 = Base.TwicePrecision{Float64}(0) +xinf = Base.TwicePrecision{Float64}(Inf) +@test Float64(x1+x0) == 1 +@test Float64(x1+0) == 1 +@test Float64(x1+0.0) == 1 +@test Float64(x1*x0) == 0 +@test Float64(x1*0) == 0 +@test Float64(x1*0.0) == 0 +@test Float64(x1/x0) == Inf +@test Float64(x1/0) == Inf +@test Float64(xinf*x1) == Inf +@test isnan(Float64(xinf*x0)) +@test isnan(Float64(xinf*0)) +@test isnan(Float64(xinf*0.0)) +@test isnan(Float64(x0/x0)) +@test isnan(Float64(x0/0)) +@test isnan(Float64(x0/0.0)) + +## ranges @test size(10:1:0) == (0,) @test length(1:.2:2) == 6 @test length(1.:.2:2.) == 6 @@ -131,7 +314,7 @@ end @test sort!(UnitRange(1,2)) == UnitRange(1,2) @test sort(1:10, rev=true) == collect(10:-1:1) @test sort(-3:3, by=abs) == [0,-1,1,-2,2,-3,3] -@test select(1:10, 4) == 4 +@test partialsort(1:10, 4) == 4 @test 0 in UInt(0):100:typemax(UInt) @test last(UInt(0):100:typemax(UInt)) in UInt(0):100:typemax(UInt) @@ -336,16 +519,17 @@ end @test [0.0:prevfloat(0.1):0.3;] == [0.0, prevfloat(0.1), prevfloat(0.2), 0.3] @test [0.0:nextfloat(0.1):0.3;] == [0.0, nextfloat(0.1), nextfloat(0.2)] -for T = (Float32, Float64,),# BigFloat), - a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 - den = convert(T,d) - start = convert(T,a)/den - step = convert(T,s)/den - stop = convert(T,(a+(n-1)*s))/den - vals = T[a:s:a+(n-1)*s;]./den - r = start:step:stop +for T = (Float32, Float64,)# BigFloat), + local T + for a = -5:25, s = [-5:-1;1:25;], d = 1:25, n = -1:15 + denom = convert(T,d) + strt = convert(T,a)/denom + Δ = convert(T,s)/denom + stop = convert(T,(a+(n-1)*s))/denom + vals = T[a:s:a+(n-1)*s;]./denom + r = strt:Δ:stop @test [r;] == vals - @test [linspace(start, stop, length(r));] == vals + @test [linspace(strt, stop, length(r));] == vals # issue #7420 n = length(r) @test [r[1:n];] == [r;] @@ -354,6 +538,7 @@ for T = (Float32, Float64,),# BigFloat), @test [r[2:2:n];] == [r;][2:2:n] @test [r[n:-1:2];] == [r;][n:-1:2] @test [r[n:-2:1];] == [r;][n:-2:1] + end end # issue #20373 (unliftable ranges with exact end points) @@ -362,27 +547,31 @@ end @test [-3*0.05:-0.05:-0.2;] == [linspace(-3*0.05,-0.2,2);] == [-3*0.05,-0.2] @test [-0.2:0.05:-3*0.05;] == [linspace(-0.2,-3*0.05,2);] == [-0.2,-3*0.05] -for T = (Float32, Float64,), i = 1:2^15, n = 1:5 - start, step = randn(T), randn(T) - step == 0 && continue - stop = start + (n-1)*step - # `n` is not necessarily unique s.t. `start + (n-1)*step == stop` - # so test that `length(start:step:stop)` satisfies this identity - # and is the closest value to `(stop-start)/step` to do so - lo = hi = n - while start + (lo-1)*step == stop; lo -= 1; end - while start + (hi-1)*step == stop; hi += 1; end - m = clamp(round(Int, (stop-start)/step) + 1, lo+1, hi-1) - r = start:step:stop - @test m == length(r) - # FIXME: these fail some small portion of the time - @test_skip start == first(r) - @test_skip stop == last(r) - l = linspace(start,stop,n) - @test n == length(l) - # FIXME: these fail some small portion of the time - @test_skip start == first(l) - @test_skip stop == last(l) +function range_fuzztests(::Type{T}, niter, nrange) where {T} + for i = 1:niter, n in nrange + strt, Δ = randn(T), randn(T) + Δ == 0 && continue + stop = strt + (n-1)*Δ + # `n` is not necessarily unique s.t. `strt + (n-1)*Δ == stop` + # so test that `length(strt:Δ:stop)` satisfies this identity + # and is the closest value to `(stop-strt)/Δ` to do so + lo = hi = n + while strt + (lo-1)*Δ == stop; lo -= 1; end + while strt + (hi-1)*Δ == stop; hi += 1; end + m = clamp(round(Int, (stop-strt)/Δ) + 1, lo+1, hi-1) + r = strt:Δ:stop + @test m == length(r) + @test strt == first(r) + @test Δ == step(r) + @test_skip stop == last(r) + l = linspace(strt,stop,n) + @test n == length(l) + @test strt == first(l) + @test stop == last(l) + end +end +for T = (Float32, Float64,) + range_fuzztests(T, 2^15, 1:5) end # Inexact errors on 32 bit architectures. #22613 @@ -464,6 +653,7 @@ let 0.0:0.1:1.0, map(Float32,0.0:0.1:1.0), linspace(0, 1, 20), map(Float32, linspace(0, 1, 20))] for r in Rs + local r ar = collect(r) @test r != ar @test !isequal(r,ar) @@ -546,6 +736,7 @@ r7484 = 0.1:0.1:1 # issue #7387 for r in (0:1, 0.0:1.0) + local r @test [r+im;] == [r;]+im @test [r-im;] == [r;]-im @test [r*im;] == [r;]*im @@ -664,7 +855,7 @@ end # stringmime/show should display the range or linspace nicely # to test print_range in range.jl -replstrmime(x) = sprint((io,x) -> show(IOContext(io, limit=true, displaysize=(24, 80)), MIME("text/plain"), x), x) +replstrmime(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), MIME("text/plain"), x), x) @test replstrmime(1:4) == "1:4" @test stringmime("text/plain", 1:4) == "1:4" @test stringmime("text/plain", linspace(1,5,7)) == "1.0:0.6666666666666666:5.0" @@ -805,6 +996,7 @@ end # Issue #13738 for r in (big(1):big(2), UInt128(1):UInt128(2), 0x1:0x2) + local r rr = r[r] @test typeof(rr) == typeof(r) @test r[r] == r @@ -858,6 +1050,7 @@ r = Base.OneTo(3) @test r+r === 2:2:6 k = 0 for i in r + local i @test i == (k+=1) end @test intersect(r, Base.OneTo(2)) == Base.OneTo(2) @@ -888,16 +1081,23 @@ a, b = rand(10), rand(10) r = linspace(a, b, 5) @test r[1] == a && r[5] == b for i = 2:4 + local i x = ((5-i)//4)*a + ((i-1)//4)*b @test r[i] == x end +# issue #23178 +r = linspace(Float16(0.1094), Float16(0.9697), 300) +@test r[1] == Float16(0.1094) +@test r[end] == Float16(0.9697) + # issue #20382 r = @inferred(colon(big(1.0),big(2.0),big(5.0))) @test eltype(r) == BigFloat # issue #14420 for r in (linspace(0.10000000000000045, 1), 0.10000000000000045:(1-0.10000000000000045)/49:1) + local r @test r[1] === 0.10000000000000045 @test r[end] === 1.0 end @@ -952,8 +1152,15 @@ end # test default values; n = 50, base = 10 @test logspace(a, b) == logspace(a, b, 50) == 10 .^ linspace(a, b, 50) @test logspace(a, b, n) == 10 .^ linspace(a, b, n) - for base in (10, 2, e) + for base in (10, 2, ℯ) @test logspace(a, b, base=base) == logspace(a, b, 50, base=base) == base.^linspace(a, b, 50) @test logspace(a, b, n, base=base) == base.^linspace(a, b, n) end end + +# issue #23300 +x = -5:big(1.0):5 +@test map(Float64, x) === -5.0:1.0:5.0 +@test map(Float32, x) === -5.0f0:1.0f0:5.0f0 +@test map(Float16, x) === Float16(-5.0):Float16(1.0):Float16(5.0) +@test map(BigFloat, x) === x diff --git a/test/read.jl b/test/read.jl index c68076d9010f9..e3b8cf002887e 100644 --- a/test/read.jl +++ b/test/read.jl @@ -123,8 +123,8 @@ end open_streams = [] function cleanup() - for s in open_streams - try close(s) end + for s_ in open_streams + try close(s_) end end empty!(open_streams) for tsk in tasks @@ -138,6 +138,7 @@ verbose = false for (name, f) in l + local f io = ()->(s=f(text); push!(open_streams, s); s) write(filename, text) @@ -176,13 +177,14 @@ for (name, f) in l old_text = text cleanup() - for text in [ + for text_ in [ old_text, String(Char['A' + i % 52 for i in 1:(div(Base.SZ_UNBUFFERED_IO,2))]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO -1)]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO )]), String(Char['A' + i % 52 for i in 1:( Base.SZ_UNBUFFERED_IO +1)]) ] + text = text_ write(filename, text) verbose && println("$name read(io, String)...") @@ -311,7 +313,7 @@ function test_read_nbyte() fn = tempname() # Write one byte. One byte read should work once # but 2-byte read should throw EOFError. - f = open(fn, "w+") do f + open(fn, "w+") do f write(f, 0x55) flush(f) seek(f, 0) diff --git a/test/reduce.jl b/test/reduce.jl index f64e10081b53c..9605b9b0a0707 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -204,9 +204,17 @@ prod2(itr) = invoke(prod, Tuple{Any}, itr) @test maximum(collect(Int16(1):Int16(100))) === Int16(100) @test maximum(Int32[1,2]) === Int32(2) -@test extrema(reshape(1:24,2,3,4),1) == reshape([(1,2),(3,4),(5,6),(7,8),(9,10),(11,12),(13,14),(15,16),(17,18),(19,20),(21,22),(23,24)],1,3,4) -@test extrema(reshape(1:24,2,3,4),2) == reshape([(1,5),(2,6),(7,11),(8,12),(13,17),(14,18),(19,23),(20,24)],2,1,4) -@test extrema(reshape(1:24,2,3,4),3) == reshape([(1,19),(2,20),(3,21),(4,22),(5,23),(6,24)],2,3,1) +A = circshift(reshape(1:24,2,3,4), (0,1,1)) +@test extrema(A,1) == reshape([(23,24),(19,20),(21,22),(5,6),(1,2),(3,4),(11,12),(7,8),(9,10),(17,18),(13,14),(15,16)],1,3,4) +@test extrema(A,2) == reshape([(19,23),(20,24),(1,5),(2,6),(7,11),(8,12),(13,17),(14,18)],2,1,4) +@test extrema(A,3) == reshape([(5,23),(6,24),(1,19),(2,20),(3,21),(4,22)],2,3,1) +@test extrema(A,(1,2)) == reshape([(19,24),(1,6),(7,12),(13,18)],1,1,4) +@test extrema(A,(1,3)) == reshape([(5,24),(1,20),(3,22)],1,3,1) +@test extrema(A,(2,3)) == reshape([(1,23),(2,24)],2,1,1) +@test extrema(A,(1,2,3)) == reshape([(1,24)],1,1,1) +@test size(extrema(A,1)) == size(maximum(A,1)) +@test size(extrema(A,(1,2))) == size(maximum(A,(1,2))) +@test size(extrema(A,(1,2,3))) == size(maximum(A,(1,2,3))) # any & all @@ -310,7 +318,7 @@ struct SomeFunctor end @test contains("quick fox", "fox") == true @test contains("quick fox", "lazy dog") == false -# count & countnz +# count @test count(x->x>0, Int[]) == count(Bool[]) == 0 @test count(x->x>0, -3:5) == count((-3:5) .> 0) == 5 @@ -325,10 +333,10 @@ end @test count(iseven(x) for x in 1:10 if x < 7) == 3 @test count(iseven(x) for x in 1:10 if x < -7) == 0 -@test countnz(Int[]) == 0 -@test countnz(Int[0]) == 0 -@test countnz(Int[1]) == 1 -@test countnz([1, 0, 2, 0, 3, 0, 4]) == 4 +@test count(!iszero, Int[]) == 0 +@test count(!iszero, Int[0]) == 0 +@test count(!iszero, Int[1]) == 1 +@test count(!iszero, [1, 0, 2, 0, 3, 0, 4]) == 4 ## cumsum, cummin, cummax diff --git a/test/reducedim.jl b/test/reducedim.jl index e5d6fb1fcdd54..63cc41775f125 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -117,22 +117,201 @@ R = reducedim((a,b) -> a+b, [1 2; 3 4], 2, 0.0) rt = Base.return_types(reducedim, Tuple{Function, Array{Float64, 3}, Int, Float64}) @test length(rt) == 1 && rt[1] == Array{Float64, 3} +@testset "empty cases" begin + A = Array{Int}(0,1) + @test sum(A) === 0 + @test prod(A) === 1 + @test_throws ArgumentError minimum(A) + @test_throws ArgumentError maximum(A) + @test var(A) === NaN -## findmin/findmax -A = [1.0 3.0 6.0; + @test isequal(sum(A, 1), zeros(Int, 1, 1)) + @test isequal(sum(A, 2), zeros(Int, 0, 1)) + @test isequal(sum(A, (1, 2)), zeros(Int, 1, 1)) + @test isequal(sum(A, 3), zeros(Int, 0, 1)) + @test isequal(prod(A, 1), ones(Int, 1, 1)) + @test isequal(prod(A, 2), ones(Int, 0, 1)) + @test isequal(prod(A, (1, 2)), ones(Int, 1, 1)) + @test isequal(prod(A, 3), ones(Int, 0, 1)) + @test isequal(var(A, 1), fill(NaN, 1, 1)) + @test isequal(var(A, 2), fill(NaN, 0, 1)) + @test isequal(var(A, (1, 2)), fill(NaN, 1, 1)) + @test isequal(var(A, 3), fill(NaN, 0, 1)) + + for f in (minimum, maximum) + @test_throws ArgumentError f(A, 1) + @test isequal(f(A, 2), zeros(Int, 0, 1)) + @test_throws ArgumentError f(A, (1, 2)) + @test isequal(f(A, 3), zeros(Int, 0, 1)) + end + for f in (findmin, findmax) + @test_throws ArgumentError f(A, 1) + @test isequal(f(A, 2), (zeros(Int, 0, 1), zeros(Int, 0, 1))) + @test_throws ArgumentError f(A, (1, 2)) + @test isequal(f(A, 3), (zeros(Int, 0, 1), zeros(Int, 0, 1))) + end + +end +## findmin/findmax/minumum/maximum + +A = [1.0 5.0 6.0; 5.0 2.0 4.0] -for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [1 4 6]), - ((2,), reshape([1.0,2.0], 2, 1), reshape([1,4], 2, 1)), - ((1,2), fill(1.0,1,1),fill(1,1,1))] +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] @test findmin(A, tup) == (rval, rind) @test findmin!(similar(rval), similar(rind), A) == (rval, rind) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) end -for (tup, rval, rind) in [((1,), [5.0 3.0 6.0], [2 3 5]), - ((2,), reshape([6.0,5.0], 2, 1), reshape([5,2], 2, 1)), - ((1,2), fill(6.0,1,1),fill(5,1,1))] +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] @test findmax(A, tup) == (rval, rind) @test findmax!(similar(rval), similar(rind), A) == (rval, rind) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +#issue #23209 + +A = [1.0 3.0 6.0; + NaN 2.0 4.0] +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) + @test isequal(Base.reducedim!(min, copy(rval), A), rval) +end + +for (tup, rval, rind) in [((1,), [NaN 3.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) + @test isequal(Base.reducedim!(max, copy(rval), A), rval) +end + +A = [1.0 NaN 6.0; + NaN 2.0 4.0] +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [Inf -Inf Inf -Inf; + Inf Inf -Inf -Inf] +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(10)] +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(-10)] +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = [BigInt(10) BigInt(-10)] +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) +end + +A = ["a", "b"] +for (tup, rval, rind) in [((1,), ["a"], [1])] + @test isequal(findmin(A, tup), (rval, rind)) + @test isequal(findmin!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(minimum(A, tup), rval) + @test isequal(minimum!(similar(rval), A), rval) + @test isequal(minimum!(copy(rval), A, init=false), rval) +end + +for (tup, rval, rind) in [((1,), ["b"], [2])] + @test isequal(findmax(A, tup), (rval, rind)) + @test isequal(findmax!(similar(rval), similar(rind), A), (rval, rind)) + @test isequal(maximum(A, tup), rval) + @test isequal(maximum!(similar(rval), A), rval) + @test isequal(maximum!(copy(rval), A, init=false), rval) end # issue #6672 diff --git a/test/reflection.jl b/test/reflection.jl index 10e2a17f9aafe..f7564fb0b99ae 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -46,7 +46,7 @@ test_code_reflections(test_bin_reflection, code_native) mktemp() do f, io OLDSTDOUT = STDOUT redirect_stdout(io) - @test try @code_native map(abs, rand(3)); true; catch false; end + @test try @code_native map(abs, rand(3)); true; catch; false; end redirect_stdout(OLDSTDOUT) nothing end @@ -67,7 +67,7 @@ end pos_stable(x) = x > 0 ? x : zero(x) pos_unstable(x) = x > 0 ? x : 0 -tag = Base.have_color ? Base.error_color() : "UNION" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "UNION" @test warntype_hastag(pos_unstable, Tuple{Float64}, tag) @test !warntype_hastag(pos_stable, Tuple{Float64}, tag) @@ -80,18 +80,23 @@ end Base.getindex(A::Stable, i) = A.A[i] Base.getindex(A::Unstable, i) = A.A[i] -tag = Base.have_color ? Base.error_color() : "ARRAY{FLOAT64,N}" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ARRAY{FLOAT64,N}" @test warntype_hastag(getindex, Tuple{Unstable{Float64},Int}, tag) @test !warntype_hastag(getindex, Tuple{Stable{Float64,2},Int}, tag) @test warntype_hastag(getindex, Tuple{Stable{Float64},Int}, tag) # Make sure emphasis is not used for other functions -tag = Base.have_color ? Base.error_color() : "ANY" +tag = Base.have_color ? Base.text_colors[Base.error_color()] : "ANY" iob = IOBuffer() show(iob, expand(Main, :(x -> x^2))) str = String(take!(iob)) @test isempty(search(str, tag)) +# Make sure non used variables are not emphasized +has_unused() = (a = rand(5)) +@test !warntype_hastag(has_unused, Tuple{}, tag) +@test warntype_hastag(has_unused, Tuple{}, "") + module ImportIntrinsics15819 # Make sure changing the lookup path of an intrinsic doesn't break # the heuristic for type instability warning. @@ -100,7 +105,8 @@ import Core.Intrinsics: sqrt_llvm, bitcast sqrt15819(x::Float64) = bitcast(Float64, sqrt_llvm(x)) # Use fully qualified name sqrt15819(x::Float32) = bitcast(Float32, Core.Intrinsics.sqrt_llvm(x)) -end +end # module ImportIntrinsics15819 + foo11122(x) = @fastmath x - 1.0 # issue #11122, #13568 and #15819 @@ -118,7 +124,7 @@ foo11122(x) = @fastmath x - 1.0 @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float64}, tag) @test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag) -end +end # module WarnType # isbits @@ -389,8 +395,17 @@ function g15714(array_var15714) for index_var15714 in eachindex(array_var15714) array_var15714[index_var15714] += 0 end - for index_var15714 in eachindex(array_var15714) - array_var15714[index_var15714] += 0 + let index_var15714 + for index_var15714 in eachindex(array_var15714) + array_var15714[index_var15714] += 0 + end + index_var15714 + end + let index_var15714 + for index_var15714 in eachindex(array_var15714) + array_var15714[index_var15714] += 0 + end + index_var15714 end end @@ -411,6 +426,10 @@ function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types for name in must_used_vars @test name in slotnames end + must_used_checked = Dict{Symbol,Bool}() + for sym in must_used_vars + must_used_checked[sym] = false + end for str in (sprint(code_warntype, f, types), stringmime("text/plain", src)) for var in must_used_vars @@ -422,31 +441,40 @@ function test_typed_ast_printing(Base.@nospecialize(f), Base.@nospecialize(types for i in 1:length(src.slotnames) name = src.slotnames[i] if name in dupnames - @test contains(str, "_$i") - if name in must_used_vars + if name in must_used_vars && ismatch(Regex("_$i\\b"), str) + must_used_checked[name] = true global used_dup_var_tested15714 = true end else - @test !contains(str, "_$i") + @test !ismatch(Regex("_$i\\b"), str) if name in must_used_vars global used_unique_var_tested15714 = true end end end end + for sym in must_used_vars + if sym in dupnames + @test must_used_checked[sym] + end + must_used_checked[sym] = false + end # Make sure printing an AST outside CodeInfo still works. str = sprint(show, src.code) # Check that we are printing the slot numbers when we don't have the context # Use the variable names that we know should be present in the optimized AST for i in 2:length(src.slotnames) name = src.slotnames[i] - if name in must_used_vars - @test contains(str, "_$i") + if name in must_used_vars && ismatch(Regex("_$i\\b"), str) + must_used_checked[name] = true end end + for sym in must_used_vars + @test must_used_checked[sym] + end end test_typed_ast_printing(f15714, Tuple{Vector{Float32}}, - [:array_var15714, :index_var15714]) + [:array_var15714]) test_typed_ast_printing(g15714, Tuple{Vector{Float32}}, [:array_var15714, :index_var15714]) @test used_dup_var_tested15714 @@ -467,7 +495,7 @@ end tracefoo(x, y) = x+y didtrace = false tracer(x::Ptr{Void}) = (@test isa(unsafe_pointer_to_objref(x), Core.MethodInstance); global didtrace = true; nothing) -ccall(:jl_register_method_tracer, Void, (Ptr{Void},), cfunction(tracer, Void, (Ptr{Void},))) +ccall(:jl_register_method_tracer, Void, (Ptr{Void},), cfunction(tracer, Void, Tuple{Ptr{Void}})) meth = which(tracefoo,Tuple{Any,Any}) ccall(:jl_trace_method, Void, (Any,), meth) @test tracefoo(1, 2) == 3 @@ -480,7 +508,7 @@ ccall(:jl_register_method_tracer, Void, (Ptr{Void},), C_NULL) # Method Tracing test methtracer(x::Ptr{Void}) = (@test isa(unsafe_pointer_to_objref(x), Method); global didtrace = true; nothing) -ccall(:jl_register_newmeth_tracer, Void, (Ptr{Void},), cfunction(methtracer, Void, (Ptr{Void},))) +ccall(:jl_register_newmeth_tracer, Void, (Ptr{Void},), cfunction(methtracer, Void, Tuple{Ptr{Void}})) tracefoo2(x, y) = x*y @test didtrace didtrace = false @@ -581,7 +609,7 @@ end mutable struct A18434 end -(::Type{A18434})(x; y=1) = 1 +A18434(x; y=1) = 1 global counter18434 = 0 function get_A18434() @@ -667,8 +695,8 @@ end @test sizeof(Symbol("")) == 0 @test_throws(ErrorException("argument is an abstract type; size is indeterminate"), sizeof(Real)) -@test_throws ErrorException sizeof(Union{Complex64,Complex128}) -@test_throws ErrorException sizeof(Union{Int8,UInt8}) +@test sizeof(Union{Complex64,Complex128}) == 16 +@test sizeof(Union{Int8,UInt8}) == 1 @test_throws ErrorException sizeof(AbstractArray) @test_throws ErrorException sizeof(Tuple) @test_throws ErrorException sizeof(Tuple{Any,Any}) @@ -691,3 +719,32 @@ end @test_throws ErrorException fieldcount(Real) @test_throws ErrorException fieldcount(AbstractArray) @test_throws ErrorException fieldcount(Tuple{Any,Vararg{Any}}) + +# PR #22979 + +function test_similar_codeinfo(a, b) + @test a.code == b.code + @test a.slotnames == b.slotnames + @test a.slotflags == b.slotflags +end + +@generated f22979(x...) = (y = 1; :(x[1] + x[2])) +x22979 = (1, 2.0, 3.0 + im) +T22979 = Tuple{typeof(f22979),typeof.(x22979)...} +world = typemax(UInt) +mtypes, msp, m = Base._methods_by_ftype(T22979, -1, world)[] +instance = Core.Inference.code_for_method(m, mtypes, msp, world, false) +cinfo_generated = Core.Inference.get_staged(instance) +cinfo_ungenerated = Base.uncompressed_ast(m) + +test_similar_codeinfo(@code_lowered(f22979(x22979...)), cinfo_generated) + +cinfos = code_lowered(f22979, typeof.(x22979), true) +@test length(cinfos) == 1 +cinfo = cinfos[] +test_similar_codeinfo(cinfo, cinfo_generated) + +cinfos = code_lowered(f22979, typeof.(x22979), false) +@test length(cinfos) == 1 +cinfo = cinfos[] +test_similar_codeinfo(cinfo, cinfo_ungenerated) diff --git a/test/repl.jl b/test/repl.jl index 3c3fc9a1cbf6f..31bd210b7f58b 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -232,7 +232,7 @@ function AddCustomMode(repl, prompt) hp.mode_mapping[:foobar] = foobar_mode foobar_mode.hist = hp - const foobar_keymap = Dict{Any,Any}( + foobar_keymap = Dict{Any,Any}( '<' => function (s,args...) if isempty(s) if !haskey(s.mode_state,foobar_mode) @@ -359,6 +359,17 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] LineEdit.history_last(s, hp) @test buffercontents(LineEdit.buffer(s)) == "wip" @test position(LineEdit.buffer(s)) == 3 + # test that history_first jumps to beginning of current session's history + hp.start_idx -= 5 # temporarily alter history + LineEdit.history_first(s, hp) + @test hp.cur_idx == 6 + # we are at the beginning of current session's history, so history_first + # must now jump to the beginning of all history + LineEdit.history_first(s, hp) + @test hp.cur_idx == 1 + LineEdit.history_last(s, hp) + @test hp.cur_idx-1 == length(hp.history) + hp.start_idx += 5 LineEdit.move_line_start(s) @test position(LineEdit.buffer(s)) == 0 @@ -550,6 +561,18 @@ fake_repl() do stdin_write, stdout_read, repl wait(c) @test Main.A == 1 + # Test that indentation corresponding to the prompt is removed + sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n end\n\e[201~""") + wait(c) + readuntil(stdout_read, "begin") + @test readuntil(stdout_read, "end") == "\n\r\e[7C α=1\n\r\e[7C β=2\n\r\e[7Cend" + # for incomplete input (`end` below is added after the end of bracket paste) + sendrepl2("""\e[200~julia> begin\n α=1\n β=2\n\e[201~end""") + wait(c) + readuntil(stdout_read, "begin") + readuntil(stdout_read, "begin") + @test readuntil(stdout_read, "end") == "\n\r\e[7C α=1\n\r\e[7C β=2\n\r\e[7Cend" + # Close repl write(stdin_write, '\x04') wait(repltask) @@ -603,7 +626,7 @@ let exename = Base.julia_cmd() TestHelpers.with_fake_pty() do slave, master nENV = copy(ENV) nENV["TERM"] = "dumb" - p = spawn(setenv(`$exename --startup-file=no --quiet`,nENV),slave,slave,slave) + p = spawn(setenv(`$exename --startup-file=no -q`,nENV),slave,slave,slave) output = readuntil(master,"julia> ") if ccall(:jl_running_on_valgrind,Cint,()) == 0 # If --trace-children=yes is passed to valgrind, we will get a @@ -620,7 +643,7 @@ let exename = Base.julia_cmd() end # Test stream mode - outs, ins, p = readandwrite(`$exename --startup-file=no --quiet`) + outs, ins, p = readandwrite(`$exename --startup-file=no -q`) write(ins,"1\nquit()\n") @test read(outs, String) == "1\n" end # let exename diff --git a/test/replcompletions.jl b/test/replcompletions.jl index 6e4630aa52eaf..d79d6fdcd3de5 100644 --- a/test/replcompletions.jl +++ b/test/replcompletions.jl @@ -51,7 +51,8 @@ ex = quote test5(x::Float64) = pass const a=x->x test6()=[a, a] - + test7() = rand() > 0.5 ? 1 : 1.0 + test8() = Any[1][1] kwtest(; x=1, y=2, w...) = pass array = [1, 1] @@ -64,6 +65,12 @@ ex = quote contains=>4, `ls`=>5, 66=>7, 67=>8, ("q",3)=>11, "α"=>12, :α=>13) test_customdict = CustomDict(test_dict) + + macro teststr_str(s) end + macro tϵsτstρ_str(s) end + macro testcmd_cmd(s) end + macro tϵsτcmδ_cmd(s) end + end test_repl_comp_dict = CompletionFoo.test_dict test_repl_comp_customdict = CompletionFoo.test_customdict @@ -76,7 +83,7 @@ function temp_pkg_dir_noinit(fn::Function) # Used in tests below to set up and tear down a sandboxed package directory # Unlike the version in test/pkg.jl, this does not run Pkg.init so does not # clone METADATA (only pkg and libgit2-online tests should need internet access) - const tmpdir = joinpath(tempdir(),randstring()) + tmpdir = joinpath(tempdir(),randstring()) withenv("JULIA_PKGDIR" => tmpdir) do @test !isdir(Pkg.dir()) try @@ -167,6 +174,16 @@ s = "using Base.Test, Base.Random" c,r = test_complete(s) @test !("RandomDevice" in c) +# issue #23226: identifiers must be separated by a comma (not a newline) +s = "using Base\nusi" +c,r = test_complete(s) +@test "using" in c + +# issue 23292 +@test_nowarn test_complete("test7().") +c,r = test_complete("test8().") +@test isempty(c) + # inexistent completion inside a string s = "Pkg.add(\"lol" c,r,res = test_complete(s) @@ -557,10 +574,11 @@ if Sys.isunix() path = homedir() dir = joinpath(path, "tmpfoobar") mkdir(dir) - s = "\"~/tmpfoob" + s = "\"" * path * "/tmpfoob" c,r = test_complete(s) @test "tmpfoobar/" in c - @test r == 4:10 + l = 3 + length(path) + @test r == l:l+6 @test s[r] == "tmpfoob" s = "\"~" @test "tmpfoobar/" in c @@ -667,6 +685,18 @@ let #test that it can auto complete with spaces in file/path rm(dir, recursive=true) end +let # Test tilde path completion + c, r, res = test_complete("\"~/julia") + if !Sys.iswindows() + @test res && c == String[homedir() * "/julia"] + else + @test !res + end + + c, r, res = test_complete("\"foo~bar") + @test !res +end + # Test the completion returns nothing when the folder do not exist c,r = test_complete("cd(\"folder_do_not_exist_77/file") @test length(c) == 0 @@ -777,3 +807,16 @@ test_dict_completion("test_repl_comp_customdict") # Issue #23004: this should not throw: @test REPLCompletions.dict_identifier_key("test_dict_ℂ[\\", :other) isa Tuple + +@testset "completion of string/cmd macros (#22577)" begin + c, r, res = test_complete("ra") + @test "raw\"" in c + c, r, res = test_complete("CompletionFoo.tests") + @test "teststr\"" in c + c, r, res = test_complete("CompletionFoo.tϵsτs") + @test "tϵsτstρ\"" in c + c, r, res = test_complete("CompletionFoo.testc") + @test "testcmd`" in c + c, r, res = test_complete("CompletionFoo.tϵsτc") + @test "tϵsτcmδ`" in c +end diff --git a/test/replutil.jl b/test/replutil.jl index 7049511f1ae52..37d169f1e1afb 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -347,7 +347,7 @@ let err_str, err_str = @except_str randn(1)() MethodError @test contains(err_str, "MethodError: objects of type Array{Float64,1} are not callable") end -@test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 0 methods)" +@test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 0 methods)" @test ismatch(r"^@doc \(macro with \d+ method[s]?\)$", stringmime("text/plain", getfield(Base, Symbol("@doc")))) method_defs_lineno = @__LINE__() + 1 @@ -356,7 +356,7 @@ Base.Symbol() = throw(ErrorException("1")) EightBitType() = throw(ErrorException("3")) (::EightBitType)() = throw(ErrorException("4")) EightBitTypeT() = throw(ErrorException("5")) -(::Type{EightBitTypeT{T}})() where {T} = throw(ErrorException("6")) +EightBitTypeT{T}() where {T} = throw(ErrorException("6")) (::EightBitTypeT)() = throw(ErrorException("7")) (::FunctionLike)() = throw(ErrorException("8")) @@ -385,7 +385,7 @@ let err_str, "@doc(__source__::LineNumberNode, __module__::Module, x...) in Core at boot.jl:") @test startswith(sprint(show, which(FunctionLike(), Tuple{})), "(::$(curmod_prefix)FunctionLike)() in $curmod_str at $sp:$(method_defs_lineno + 7)") - @test stringmime("text/plain", FunctionLike()) == "(::FunctionLike) (generic function with 1 method)" + @test stringmime("text/plain", FunctionLike()) == "(::$(curmod_prefix)FunctionLike) (generic function with 1 method)" @test stringmime("text/plain", Core.arraysize) == "arraysize (built-in function)" err_str = @except_stackframe Symbol() ErrorException @@ -610,7 +610,7 @@ end @testset "Dict printing with limited rows" begin buf = IOBuffer() - io = IOContext(IOContext(buf, :displaysize => (4, 80)), :limit => true) + io = IOContext(buf, :displaysize => (4, 80), :limit => true) d = Base.ImmutableDict(1=>2) show(io, MIME"text/plain"(), d) @test String(take!(buf)) == "Base.ImmutableDict{$Int,$Int} with 1 entry: …" diff --git a/test/rounding.jl b/test/rounding.jl index 32a19d10b1a1b..06256c8e72efd 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Small sanity tests to ensure changing the rounding of float functions work -using Base.Test +using Base: Test, MathConstants ## Float64 checks # a + b returns a number exactly between prevfloat(1.) and 1., so its @@ -110,7 +110,7 @@ end for T in [Float32,Float64] for v in [sqrt(big(2.0)),-big(1.0)/big(3.0),nextfloat(big(1.0)), prevfloat(big(1.0)),nextfloat(big(0.0)),prevfloat(big(0.0)), - pi,e,eulergamma,catalan,golden, + pi,ℯ,eulergamma,catalan,golden, typemax(Int64),typemax(UInt64),typemax(Int128),typemax(UInt128),0xa2f30f6001bb2ec6] pn = T(v,RoundNearest) @test pn == convert(T,BigFloat(v)) diff --git a/test/sets.jl b/test/sets.jl index 93f6022d53e8e..7041e475295b1 100644 --- a/test/sets.jl +++ b/test/sets.jl @@ -120,9 +120,9 @@ let end # start, done, next -for data_in in ((7,8,4,5), - ("hello", 23, 2.7, (), [], (1,8))) - s = Set(data_in) +for data_ in ((7,8,4,5), + ("hello", 23, 2.7, (), [], (1,8))) + s = Set(data_) s_new = Set() for el in s @@ -233,6 +233,8 @@ u = unique([1,1,2]) # issue 20105 @test @inferred(unique(x for x in 1:1)) == [1] @test unique(x for x in Any[1,1.0])::Vector{Real} == [1] +@test unique(x for x in Real[1,1.0])::Vector{Real} == [1] +@test unique(Integer[1,1,2])::Vector{Integer} == [1,2] # unique! @testset "unique!" begin diff --git a/test/show.jl b/test/show.jl index 0a17da25462ba..bf9a491e55c00 100644 --- a/test/show.jl +++ b/test/show.jl @@ -3,7 +3,7 @@ # For curmod_* include("testenv.jl") -replstr(x) = sprint((io,x) -> show(IOContext(io, limit=true, displaysize=(24, 80)), MIME("text/plain"), x), x) +replstr(x) = sprint((io,x) -> show(IOContext(io, :limit => true, :displaysize => (24, 80)), MIME("text/plain"), x), x) @test replstr(Array{Any}(2)) == "2-element Array{Any,1}:\n #undef\n #undef" @test replstr(Array{Any}(2,2)) == "2×2 Array{Any,2}:\n #undef #undef\n #undef #undef" @@ -343,6 +343,7 @@ end" # issue #9474 for s in ("(1::Int64 == 1::Int64)::Bool", "(1:2:3) + 4", "x = 1:2:3") + local s @test sprint(show, parse(s)) == ":("*s*")" end @@ -414,6 +415,8 @@ end @test_repr "a::b where T" @test_repr "X where (T=1)" @test_repr "X where T = 1" +@test_repr "Array{<:Real}" +@test_repr "Array{>:Real}" let oldout = STDOUT, olderr = STDERR local rdout, wrout, rderr, wrerr, out, err, rd, wr @@ -507,7 +510,7 @@ function f13127() show(buf, f) String(take!(buf)) end -@test f13127() == "$(curmod_prefix)f" +@test startswith(f13127(), "getfield($(@__MODULE__), Symbol(\"") #test methodshow.jl functions @test Base.inbase(Base) @@ -598,7 +601,7 @@ A = reshape(1:16,4,4) @test replstr(Bidiagonal(A,:U)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n ⋅ 6 10 ⋅\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(Bidiagonal(A,:L)) == "4×4 Bidiagonal{$(Int),Array{$(Int),1}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n ⋅ 7 11 ⋅\n ⋅ ⋅ 12 16" @test replstr(SymTridiagonal(A+A')) == "4×4 SymTridiagonal{$(Int),Array{$(Int),1}}:\n 2 7 ⋅ ⋅\n 7 12 17 ⋅\n ⋅ 17 22 27\n ⋅ ⋅ 27 32" -@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$Int}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" +@test replstr(Tridiagonal(diag(A,-1),diag(A),diag(A,+1))) == "4×4 Tridiagonal{$(Int),Array{$(Int),1}}:\n 1 5 ⋅ ⋅\n 2 6 10 ⋅\n ⋅ 7 11 15\n ⋅ ⋅ 12 16" @test replstr(UpperTriangular(copy(A))) == "4×4 UpperTriangular{$Int,Array{$Int,2}}:\n 1 5 9 13\n ⋅ 6 10 14\n ⋅ ⋅ 11 15\n ⋅ ⋅ ⋅ 16" @test replstr(LowerTriangular(copy(A))) == "4×4 LowerTriangular{$Int,Array{$Int,2}}:\n 1 ⋅ ⋅ ⋅\n 2 6 ⋅ ⋅\n 3 7 11 ⋅\n 4 8 12 16" @@ -736,7 +739,7 @@ let sv = Core.svec(:a, :b, :c) @test repr == "SimpleVector\n 1: Symbol a\n 2: Symbol b\n 3: #undef\n" end let repr = sprint(dump, sin) - @test repr == "sin (function of type Base.#sin)\n" + @test repr == "sin (function of type typeof(sin))\n" end let repr = sprint(dump, Base.Test) @test repr == "Module Base.Test\n" @@ -758,6 +761,8 @@ end @test repr(:([x for x = y])) == ":([x for x = y])" @test repr(:([x for x = y if z])) == ":([x for x = y if z])" @test repr(:(z for z = 1:5, y = 1:5)) == ":((z for z = 1:5, y = 1:5))" +@test_repr "(x for i in a, b in c)" +@test_repr "(x for a in b, c in d for e in f)" for op in (:(.=), :(.+=), :(.&=)) @test repr(parse("x $op y")) == ":(x $op y)" @@ -874,7 +879,7 @@ end (Pair{Integer,Int64}(1, 2) => 3) => "Pair{Integer,Int64}(1, 2) => 3", ((1+2im) => (3+4im)) => "1+2im => 3+4im", (1 => 2 => Pair{Real,Int64}(3, 4)) => "1 => (2=>Pair{Real,Int64}(3, 4))") - + local s @test sprint(show, p) == s end # - when the context has :compact=>false, print pair's member non-compactly @@ -923,7 +928,7 @@ end @testset "Array printing with limited rows" begin arrstr = let buf = IOBuffer() function (A, rows) - Base.showarray(IOContext(buf, displaysize=(rows, 80), limit=true), + Base.showarray(IOContext(buf, :displaysize => (rows, 80), :limit => true), A, false, header=true) String(take!(buf)) end @@ -947,3 +952,34 @@ end " 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\n", " ⋮ ⋮ ⋱ ⋮ ") end + +module UnexportedOperators +function + end +function == end +end + +@testset "Parseable printing of types" begin + @test repr(typeof(print)) == "typeof(print)" + @test repr(typeof(Base.show_default)) == "typeof(Base.show_default)" + @test repr(typeof(UnexportedOperators.:+)) == "typeof($(curmod_prefix)UnexportedOperators.:+)" + @test repr(typeof(UnexportedOperators.:(==))) == "typeof($(curmod_prefix)UnexportedOperators.:(==))" + anonfn = x->2x + modname = string(@__MODULE__) + anonfn_type_repr = "getfield($modname, Symbol(\"$(typeof(anonfn).name.name)\"))" + @test repr(typeof(anonfn)) == anonfn_type_repr + @test repr(anonfn) == anonfn_type_repr * "()" + @test stringmime("text/plain", anonfn) == "$(typeof(anonfn).name.mt.name) (generic function with 1 method)" + mkclosure = x->y->x+y + clo = mkclosure(10) + @test stringmime("text/plain", clo) == "$(typeof(clo).name.mt.name) (generic function with 1 method)" + @test repr(UnionAll) == "UnionAll" +end + +let x = TypeVar(:_), y = TypeVar(:_) + @test repr(UnionAll(x, UnionAll(y, Pair{x,y}))) == "Pair{_1,_2} where _2 where _1" + @test repr(UnionAll(x, UnionAll(y, Pair{UnionAll(x,Ref{x}),y}))) == "Pair{Ref{_1} where _1,_1} where _1" + x = TypeVar(:a) + y = TypeVar(:a) + z = TypeVar(:a) + @test repr(UnionAll(z, UnionAll(x, UnionAll(y, Tuple{x,y,z})))) == "Tuple{a1,a2,a} where a2 where a1 where a" +end diff --git a/test/sorting.jl b/test/sorting.jl index 28d7020d25458..cc5545e353d04 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -17,16 +17,16 @@ end @test !issorted([2,3,1]) @test issorted([1,2,3]) @test reverse([2,3,1]) == [1,3,2] -@test select([3,6,30,1,9],3) == 6 -@test select([3,6,30,1,9],3:4) == [6,9] -@test selectperm([3,6,30,1,9], 3:4) == [2,5] -@test selectperm!(collect(1:5), [3,6,30,1,9], 3:4) == [2,5] +@test partialsort([3,6,30,1,9],3) == 6 +@test partialsort([3,6,30,1,9],3:4) == [6,9] +@test partialsortperm([3,6,30,1,9], 3:4) == [2,5] +@test partialsortperm!(collect(1:5), [3,6,30,1,9], 3:4) == [2,5] let a=[1:10;] for r in Any[2:4, 1:2, 10:10, 4:2, 2:1, 4:-1:2, 2:-1:1, 10:-1:10, 4:1:3, 1:2:8, 10:-3:1] - @test select(a, r) == [r;] - @test selectperm(a, r) == [r;] - @test select(a, r, rev=true) == (11 .- [r;]) - @test selectperm(a, r, rev=true) == (11 .- [r;]) + @test partialsort(a, r) == [r;] + @test partialsortperm(a, r) == [r;] + @test partialsort(a, r, rev=true) == (11 .- [r;]) + @test partialsortperm(a, r, rev=true) == (11 .- [r;]) end end @test sum(randperm(6)) == 21 @@ -204,10 +204,10 @@ let alg = PartialQuickSort(div(length(a), 10)) @test !issorted(d, rev=true) end -@test select([3,6,30,1,9], 2, rev=true) == 9 -@test select([3,6,30,1,9], 2, by=x->1/x) == 9 -@test selectperm([3,6,30,1,9], 2, rev=true) == 5 -@test selectperm([3,6,30,1,9], 2, by=x->1/x) == 5 +@test partialsort([3,6,30,1,9], 2, rev=true) == 9 +@test partialsort([3,6,30,1,9], 2, by=x->1/x) == 9 +@test partialsortperm([3,6,30,1,9], 2, rev=true) == 5 +@test partialsortperm([3,6,30,1,9], 2, by=x->1/x) == 5 ## more advanced sorting tests ## diff --git a/test/sparse/cholmod.jl b/test/sparse/cholmod.jl index 936f6059e6a37..0539006a6f52d 100644 --- a/test/sparse/cholmod.jl +++ b/test/sparse/cholmod.jl @@ -216,7 +216,7 @@ end ### illegal dtype (for now but should be supported at some point) p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -224,7 +224,7 @@ unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Pt ### illegal dtype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -232,7 +232,7 @@ unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) ### illegal xtype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 3) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -240,7 +240,7 @@ unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) ### illegal itype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -248,7 +248,7 @@ unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(P ### illegal itype p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) puint = convert(Ptr{UInt32}, p) unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) @@ -297,7 +297,7 @@ end ## test free_sparse! p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_Sparse{Float64}}, (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common()) + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) @test CHOLMOD.free_sparse!(p) for elty in (Float64, Complex{Float64}) @@ -668,6 +668,7 @@ let Apre = sprandn(10, 10, 0.2) - I for A in (Symmetric(Apre), Hermitian(Apre), Symmetric(Apre + 10I), Hermitian(Apre + 10I), Hermitian(complex(Apre)), Hermitian(complex(Apre) + 10I)) + local A x = ones(10) b = A*x @test x ≈ A\b @@ -687,14 +688,13 @@ let A = sprandn(10, 10, 0.1) end end -@testset "Check inputs to Sparse. Related to #20024" for A in ( +@testset "Check inputs to Sparse. Related to #20024" for A_ in ( SparseMatrixCSC(2, 2, [1, 2], CHOLMOD.SuiteSparse_long[], Float64[]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[1], Float64[]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[], Float64[1.0]), SparseMatrixCSC(2, 2, [1, 2, 3], CHOLMOD.SuiteSparse_long[1], Float64[1.0])) - - @test_throws ArgumentError CHOLMOD.Sparse(size(A)..., A.colptr - 1, A.rowval - 1, A.nzval) - @test_throws ArgumentError CHOLMOD.Sparse(A) + @test_throws ArgumentError CHOLMOD.Sparse(size(A_)..., A_.colptr - 1, A_.rowval - 1, A_.nzval) + @test_throws ArgumentError CHOLMOD.Sparse(A_) end @testset "sparse right multiplication of Symmetric and Hermitian matrices #21431" begin @@ -714,6 +714,7 @@ AtA = A'*A; C0 = [1., 2., 0, 0, 0] #Test both cholfact and LDLt with and without automatic permutations for F in (cholfact(AtA), cholfact(AtA, perm=1:5), ldltfact(AtA), ldltfact(AtA, perm=1:5)) + local F B0 = F\ones(5) #Test both sparse/dense and vectors/matrices for Ctest in (C0, sparse(C0), [C0 2*C0], sparse([C0 2*C0])) diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 714c8fea0f582..ebdd7c40c3087 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -401,11 +401,11 @@ end fullAt = transpose(Array(A)) @test transpose(A) == fullAt @test transpose!(similar(At), A) == fullAt - # ctranspose[!] + # adjoint[!] C = A + im*A/2 - fullCh = ctranspose(Array(C)) - @test ctranspose(C) == fullCh - @test ctranspose!(similar(sparse(fullCh)), C) == fullCh + fullCh = adjoint(Array(C)) + @test adjoint(C) == fullCh + @test adjoint!(similar(sparse(fullCh)), C) == fullCh # permute[!] p = randperm(m) q = randperm(n) @@ -423,12 +423,12 @@ end @testset "transpose of SubArrays" begin A = view(sprandn(10, 10, 0.3), 1:4, 1:4) @test transpose(Array(A)) == Array(transpose(A)) - @test ctranspose(Array(A)) == Array(ctranspose(A)) + @test adjoint(Array(A)) == Array(adjoint(A)) end @testset "exp" begin A = sprandn(5,5,0.2) - @test e.^A ≈ e.^Array(A) + @test ℯ.^A ≈ ℯ.^Array(A) end @testset "reductions" begin @@ -466,12 +466,18 @@ end @test_throws ArgumentError maximum(sparse(Int[])) @test var(sparse(Int[])) === NaN - for f in (sum, prod, minimum, maximum, var) + for f in (sum, prod, var) @test isequal(f(spzeros(0, 1), 1), f(Array{Int}(0, 1), 1)) @test isequal(f(spzeros(0, 1), 2), f(Array{Int}(0, 1), 2)) @test isequal(f(spzeros(0, 1), (1, 2)), f(Array{Int}(0, 1), (1, 2))) @test isequal(f(spzeros(0, 1), 3), f(Array{Int}(0, 1), 3)) end + for f in (minimum, maximum, findmin, findmax) + @test_throws ArgumentError f(spzeros(0, 1), 1) + @test isequal(f(spzeros(0, 1), 2), f(Array{Int}(0,1), 2)) + @test_throws ArgumentError f(spzeros(0, 1), (1, 2)) + @test isequal(f(spzeros(0, 1), 3), f(Array{Int}(0,1), 3)) + end end end @@ -689,56 +695,56 @@ end @testset "setindex" begin a = spzeros(Int, 10, 10) - @test countnz(a) == 0 + @test count(!iszero, a) == 0 a[1,:] = 1 - @test countnz(a) == 10 + @test count(!iszero, a) == 10 @test a[1,:] == sparse(ones(Int,10)) a[:,2] = 2 - @test countnz(a) == 19 + @test count(!iszero, a) == 19 @test a[:,2] == 2*sparse(ones(Int,10)) b = copy(a) # Zero-assignment behavior of setindex!(A, v, i, j) a[1,3] = 0 @test nnz(a) == 19 - @test countnz(a) == 18 + @test count(!iszero, a) == 18 a[2,1] = 0 @test nnz(a) == 19 - @test countnz(a) == 18 + @test count(!iszero, a) == 18 # Zero-assignment behavior of setindex!(A, v, I, J) a[1,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 9 + @test count(!iszero, a) == 9 a[2,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[:,1] = 0 @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[:,2] = 0 @test nnz(a) == 19 - @test countnz(a) == 0 + @test count(!iszero, a) == 0 a = copy(b) a[:,:] = 0 @test nnz(a) == 19 - @test countnz(a) == 0 + @test count(!iszero, a) == 0 # Zero-assignment behavior of setindex!(A, B::SparseMatrixCSC, I, J) a = copy(b) a[1:2,:] = spzeros(2, 10) @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[1:2,1:3] = sparse([1 0 1; 0 0 1]) @test nnz(a) == 20 - @test countnz(a) == 11 + @test count(!iszero, a) == 11 a = copy(b) a[1:2,:] = let c = sparse(ones(2,10)); fill!(c.nzval, 0); c; end @test nnz(a) == 19 - @test countnz(a) == 8 + @test count(!iszero, a) == 8 a[1:2,1:3] = let c = sparse(ones(2,3)); c[1,2] = c[2,1] = c[2,2] = 0; c; end @test nnz(a) == 20 - @test countnz(a) == 11 + @test count(!iszero, a) == 11 a[1,:] = 1:10 @test a[1,:] == sparse([1:10;]) @@ -782,34 +788,34 @@ end A = spzeros(Int, 10, 20) A[1:5,1:10] = 10 A[1:5,1:10] = 10 - @test countnz(A) == 50 + @test count(!iszero, A) == 50 @test A[1:5,1:10] == 10 * ones(Int, 5, 10) A[6:10,11:20] = 0 - @test countnz(A) == 50 + @test count(!iszero, A) == 50 A[6:10,11:20] = 20 - @test countnz(A) == 100 + @test count(!iszero, A) == 100 @test A[6:10,11:20] == 20 * ones(Int, 5, 10) A[4:8,8:16] = 15 - @test countnz(A) == 121 + @test count(!iszero, A) == 121 @test A[4:8,8:16] == 15 * ones(Int, 5, 9) ASZ = 1000 TSZ = 800 A = sprand(ASZ, 2*ASZ, 0.0001) B = copy(A) - nA = countnz(A) + nA = count(!iszero, A) x = A[1:TSZ, 1:(2*TSZ)] - nx = countnz(x) + nx = count(!iszero, x) A[1:TSZ, 1:(2*TSZ)] = 0 - nB = countnz(A) + nB = count(!iszero, A) @test nB == (nA - nx) A[1:TSZ, 1:(2*TSZ)] = x - @test countnz(A) == nA + @test count(!iszero, A) == nA @test A == B A[1:TSZ, 1:(2*TSZ)] = 10 - @test countnz(A) == nB + 2*TSZ*TSZ + @test count(!iszero, A) == nB + 2*TSZ*TSZ A[1:TSZ, 1:(2*TSZ)] = x - @test countnz(A) == nA + @test count(!iszero, A) == nA @test A == B A = speye(Int, 5) @@ -820,17 +826,17 @@ end @test A[I] == A[X] == collect(1:10) A[I] = zeros(Int, 10) @test nnz(A) == 13 - @test countnz(A) == 3 + @test count(!iszero, A) == 3 @test A[I] == A[X] == zeros(Int, 10) c = collect(11:20); c[1] = c[3] = 0 A[I] = c @test nnz(A) == 13 - @test countnz(A) == 11 + @test count(!iszero, A) == 11 @test A[I] == A[X] == c A = speye(Int, 5) A[I] = c @test nnz(A) == 12 - @test countnz(A) == 11 + @test count(!iszero, A) == 11 @test A[I] == A[X] == c S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) @@ -839,14 +845,14 @@ end FI = Array(I) @test sparse(FS[FI]) == S[I] == S[FI] @test sum(S[FI]) + sum(S[.!FI]) == sum(S) - @test countnz(I) == count(I) + @test count(!iszero, I) == count(I) sumS1 = sum(S) sumFI = sum(S[FI]) nnzS1 = nnz(S) S[FI] = 0 sumS2 = sum(S) - cnzS2 = countnz(S) + cnzS2 = count(!iszero, S) @test sum(S[FI]) == 0 @test nnz(S) == nnzS1 @test (sum(S) + sumFI) == sumS1 @@ -857,7 +863,7 @@ end S[FI] = 0 @test sum(S) == sumS2 @test nnz(S) == nnzS3 - @test countnz(S) == cnzS2 + @test count(!iszero, S) == cnzS2 S[FI] = [1:sum(FI);] @test sum(S) == sumS2 + sum(1:sum(FI)) @@ -992,8 +998,8 @@ end S = spzeros(10,8) A = Array(S) - @test indmax(S) == indmax(A) == 1 - @test indmin(S) == indmin(A) == 1 + @test indmax(S) == indmax(A) == CartesianIndex(1,1) + @test indmin(S) == indmin(A) == CartesianIndex(1,1) A = Array{Int}(0,0) S = sparse(A) @@ -1005,6 +1011,107 @@ end @test iA === iS === nothing end +# findmin/findmax/minumum/maximum + +A = sparse([1.0 5.0 6.0; + 5.0 2.0 4.0]) +for (tup, rval, rind) in [((1,), [1.0 2.0 4.0], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0,2.0], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,2)], 2, 1)), + ((1,2), fill(1.0,1,1),fill(CartesianIndex(1,1),1,1))] + @test findmin(A, tup) == (rval, rind) +end + +for (tup, rval, rind) in [((1,), [5.0 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0,5.0], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(6.0,1,1),fill(CartesianIndex(1,3),1,1))] + @test findmax(A, tup) == (rval, rind) +end + +#issue 23209 + +A = sparse([1.0 5.0 6.0; + NaN 2.0 4.0]) +for (tup, rval, rind) in [((1,), [NaN 2.0 4.0], [CartesianIndex(2,1) CartesianIndex(2,2) CartesianIndex(2,3)]), + ((2,), reshape([1.0, NaN], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [NaN 5.0 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([6.0, NaN], 2, 1), reshape([CartesianIndex(1,3),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([1.0 NaN 6.0; + NaN 2.0 4.0]) +for (tup, rval, rind) in [((1,), [NaN NaN 4.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(2,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [NaN NaN 6.0], [CartesianIndex(2,1) CartesianIndex(1,2) CartesianIndex(1,3)]), + ((2,), reshape([NaN, NaN], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(NaN,1,1),fill(CartesianIndex(2,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([Inf -Inf Inf -Inf; + Inf Inf -Inf -Inf]) +for (tup, rval, rind) in [((1,), [Inf -Inf -Inf -Inf], [CartesianIndex(1,1) CartesianIndex(1,2) CartesianIndex(2,3) CartesianIndex(1,4)]), + ((2,), reshape([-Inf -Inf], 2, 1), reshape([CartesianIndex(1,2),CartesianIndex(2,3)], 2, 1)), + ((1,2), fill(-Inf,1,1),fill(CartesianIndex(1,2),1,1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), [Inf Inf Inf -Inf], [CartesianIndex(1,1) CartesianIndex(2,2) CartesianIndex(1,3) CartesianIndex(1,4)]), + ((2,), reshape([Inf Inf], 2, 1), reshape([CartesianIndex(1,1),CartesianIndex(2,1)], 2, 1)), + ((1,2), fill(Inf,1,1),fill(CartesianIndex(1,1),1,1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(10)]) +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), [BigInt(10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(-10)]) +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), [BigInt(-10)], [1])] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse([BigInt(10) BigInt(-10)]) +for (tup, rval, rind) in [((2,), reshape([BigInt(-10)], 1, 1), reshape([CartesianIndex(1,2)], 1, 1))] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((2,), reshape([BigInt(10)], 1, 1), reshape([CartesianIndex(1,1)], 1, 1))] + @test isequal(findmax(A, tup), (rval, rind)) +end + +A = sparse(["a", "b"]) +@test_throws MethodError findmin(A, 1) + +# Support the case, when user defined `zero` and `isless` for non-numerical type +# +Base.zero(::Type{T}) where T<:AbstractString = "" +for (tup, rval, rind) in [((1,), ["a"], [1])] + @test isequal(findmin(A, tup), (rval, rind)) +end + +for (tup, rval, rind) in [((1,), ["b"], [2])] + @test isequal(findmax(A, tup), (rval, rind)) +end + @testset "findn" begin b = findn( speye(4) ) @test (length(b[1]) == 4) @@ -1095,14 +1202,14 @@ end N=2^3 Irand = randperm(M) Jrand = randperm(N) - I = sort([Irand; Irand; Irand]) + II = sort([Irand; Irand; Irand]) J = [Jrand; Jrand] SA = [sprand(M, N, d) for d in [1., 0.1, 0.01, 0.001, 0.0001, 0.]] for S in SA res = Any[1,2,3] for searchtype in [0, 1, 2] - res[searchtype+1] = test_getindex_algs(S, I, J, searchtype) + res[searchtype+1] = test_getindex_algs(S, II, J, searchtype) end @test res[1] == res[2] == res[3] @@ -1110,12 +1217,12 @@ end M = 2^14 N=2^4 - I = randperm(M) + II = randperm(M) J = randperm(N) Jsorted = sort(J) SA = [sprand(M, N, d) for d in [1., 0.1, 0.01, 0.001, 0.0001, 0.]] - IA = [I[1:round(Int,n)] for n in [M, M*0.1, M*0.01, M*0.001, M*0.0001, 0.]] + IA = [II[1:round(Int,n)] for n in [M, M*0.1, M*0.01, M*0.001, M*0.0001, 0.]] debug = false if debug @printf(" | | | times | memory |\n") @@ -1148,11 +1255,11 @@ end sm = sparse(D) sv = sparsevec(D) - @test countnz(sm) == 10 - @test countnz(sv) == 10 + @test count(!iszero, sm) == 10 + @test count(!iszero, sv) == 10 - @test countnz(sparse(Diagonal(Int[]))) == 0 - @test countnz(sparsevec(Diagonal(Int[]))) == 0 + @test count(!iszero, sparse(Diagonal(Int[]))) == 0 + @test count(!iszero, sparsevec(Diagonal(Int[]))) == 0 end @testset "explicit zeros" begin @@ -1198,6 +1305,9 @@ end B = Bidiagonal(randn(5),randn(4),:L) S = sparse(B) @test norm(Array(B) - Array(S)) == 0.0 + D = Diagonal(randn(5)) + S = sparse(D) + @test norm(Array(D) - Array(S)) == 0.0 end @testset "spdiagm promotion" begin @@ -1316,11 +1426,32 @@ end @test trace(speye(5)) == 5 end -@testset "diagm on a matrix" begin - @test_throws DimensionMismatch diagm(sparse(ones(5,2))) - @test_throws DimensionMismatch diagm(sparse(ones(2,5))) - @test diagm(sparse(ones(1,5))) == speye(5) - @test diagm(sparse(ones(5,1))) == speye(5) +@testset "spdiagm" begin + v = sprand(10, 0.4) + @test spdiagm(v)::SparseMatrixCSC == diagm(Vector(v)) + @test spdiagm(sparse(ones(5)))::SparseMatrixCSC == speye(5) + @test spdiagm(sparse(zeros(5)))::SparseMatrixCSC == spzeros(5,5) +end + +@testset "diag" begin + for T in (Float64, Complex128) + S1 = sprand(T, 5, 5, 0.5) + S2 = sprand(T, 10, 5, 0.5) + S3 = sprand(T, 5, 10, 0.5) + for S in (S1, S2, S3) + A = Matrix(S) + @test diag(S)::SparseVector{T,Int} == diag(A) + for k in -size(S,1):size(S,2) + @test diag(S, k)::SparseVector{T,Int} == diag(A, k) + end + @test_throws ArgumentError diag(S, -size(S,1)-1) + @test_throws ArgumentError diag(S, size(S,2)+1) + end + end + # test that stored zeros are still stored zeros in the diagonal + S = sparse([1,3],[1,3],[0.0,0.0]); V = diag(S) + @test V.nzind == [1,3] + @test V.nzval == [0.0,0.0] end @testset "expandptr" begin @@ -1773,14 +1904,14 @@ end show(io, MIME"text/plain"(), spzeros(Float32, Int64, 2, 2)) @test String(take!(io)) == "2×2 SparseMatrixCSC{Float32,Int64} with 0 stored entries" - ioc = IOContext(io, displaysize = (5, 80), limit = true) + ioc = IOContext(io, :displaysize => (5, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1], Int64[1], [1.0])) @test String(take!(io)) == "1×1 SparseMatrixCSC{Float64,Int64} with 1 stored entry:\n [1, 1] = 1.0" show(ioc, MIME"text/plain"(), sparse(Int64[1, 1], Int64[1, 2], [1.0, 2.0])) @test String(take!(io)) == "1×2 SparseMatrixCSC{Float64,Int64} with 2 stored entries:\n ⋮" # even number of rows - ioc = IOContext(io, displaysize = (8, 80), limit = true) + ioc = IOContext(io, :displaysize => (8, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4], Int64[1,1,2,2], [1.0,2.0,3.0,4.0])) @test String(take!(io)) == string("4×2 SparseMatrixCSC{Float64,Int64} with 4 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0") @@ -1794,7 +1925,7 @@ end " = 1.0\n ⋮\n [5, 3] = 1.0") # odd number of rows - ioc = IOContext(io, displaysize = (9, 80), limit = true) + ioc = IOContext(io, :displaysize => (9, 80), :limit => true) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5], Int64[1,1,2,2,3], [1.0,2.0,3.0,4.0,5.0])) @test String(take!(io)) == string("5×3 SparseMatrixCSC{Float64,Int64} with 5 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0") @@ -1807,7 +1938,7 @@ end @test String(take!(io)) == string("6×3 SparseMatrixCSC{Float64,$Int} with 18 stored entries:\n [1, 1]", " = 1.0\n [2, 1] = 1.0\n ⋮\n [5, 3] = 1.0\n [6, 3] = 1.0") - ioc = IOContext(io, displaysize = (9, 80)) + ioc = IOContext(io, :displaysize => (9, 80)) show(ioc, MIME"text/plain"(), sparse(Int64[1,2,3,4,5,6], Int64[1,1,2,2,3,3], [1.0,2.0,3.0,4.0,5.0,6.0])) @test String(take!(io)) == string("6×3 SparseMatrixCSC{Float64,Int64} with 6 stored entries:\n [1, 1] = 1.0\n", " [2, 1] = 2.0\n [3, 2] = 3.0\n [4, 2] = 4.0\n [5, 3] = 5.0\n [6, 3] = 6.0") diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index 4a61940f53677..fefac54f81a47 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -20,7 +20,7 @@ let x = spv_x1 @test size(x,2) == 1 @test !isempty(x) - @test countnz(x) == 3 + @test count(!iszero, x) == 3 @test nnz(x) == 3 @test SparseArrays.nonzeroinds(x) == [2, 5, 6] @test nonzeros(x) == [1.25, -0.75, 3.5] @@ -296,7 +296,7 @@ let a = SparseVector(8, [2, 5, 6], Int32[12, 35, 72]) @test complex(acp) == acp @test isa(acp, SparseVector{Complex128,Int}) @test exact_equal(acp, SparseVector(8, [2, 5, 6], complex([12., 35., 72.]))) - @test sparsevec(ctranspose(ctranspose(acp))) == acp + @test sparsevec(adjoint(adjoint(acp))) == acp end let x1 = SparseVector(8, [2, 5, 6], [12.2, 1.4, 5.0]) diff --git a/test/sparse/spqr.jl b/test/sparse/spqr.jl index 0e17f0be2a065..071b4a0abe6dc 100644 --- a/test/sparse/spqr.jl +++ b/test/sparse/spqr.jl @@ -73,7 +73,7 @@ end xd = full(A)\b # check that basic solution has more zeros - @test countnz(xs) < countnz(xd) + @test count(!iszero, xs) < count(!iszero, xd) @test A*xs ≈ A*xd end diff --git a/test/spawn.jl b/test/spawn.jl index 43787d66871d7..557899e1bff4b 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -254,7 +254,7 @@ let fname = tempname() oldhandle = OLD_STDERR.handle OLD_STDERR.status = Base.StatusClosing OLD_STDERR.handle = C_NULL - ccall(:uv_close, Void, (Ptr{Void}, Ptr{Void}), oldhandle, cfunction(thrash, Void, (Ptr{Void},))) + ccall(:uv_close, Void, (Ptr{Void}, Ptr{Void}), oldhandle, cfunction(thrash, Void, Tuple{Ptr{Void}})) sleep(1) import Base.zzzInvalidIdentifier """ @@ -453,9 +453,7 @@ if Sys.isunix() isa(ex, Base.UVError) || rethrow(ex) @test ex.code in (Base.UV_EMFILE, Base.UV_ENFILE) finally - for p in ps - close(p) - end + foreach(close, ps) end end end diff --git a/test/staged.jl b/test/staged.jl index 95a9b25ae30a0..05cb9f0b39e3e 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -147,7 +147,7 @@ module TestGeneratedThrow foo() = (bar(rand() > 0.5 ? 1 : 1.0); error("foo")) function __init__() code_typed(foo,(); optimize = false) - cfunction(foo,Void,()) + cfunction(foo,Void,Tuple{}) end end @@ -240,7 +240,7 @@ f22440kernel(::Type{T}) where {T<:AbstractFloat} = zero(T) sig, spvals, method = Base._methods_by_ftype(Tuple{typeof(f22440kernel),y}, -1, typemax(UInt))[1] code_info = Base.uncompressed_ast(method) body = Expr(:block, code_info.code...) - Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0) + Base.Core.Inference.substitute!(body, 0, Any[], sig, Any[spvals...], 0, :propagate) return code_info end diff --git a/test/statistics.jl b/test/statistics.jl index 55dd0fdf71c2b..2962853b42eb9 100644 --- a/test/statistics.jl +++ b/test/statistics.jl @@ -432,3 +432,21 @@ end @test quantile(x, 0.5) === 3.0 @test quantile(x, 1//2) === 3//1 end + +@testset "Promotion in covzm. Issue #8080" begin + A = [1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] + @test Base.covzm(A) - mean(A, 1)'*mean(A, 1)*size(A, 1)/(size(A, 1) - 1) ≈ cov(A) + A = [1//1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] + @test (A'A - size(A, 1)*Base.mean(A, 1)'*Base.mean(A, 1))/4 == cov(A) +end + +@testset "Mean along dimension of empty array" begin + a0 = zeros(0) + a00 = zeros(0, 0) + a01 = zeros(0, 1) + a10 = zeros(1, 0) + @test isequal(mean(a0, 1) , fill(NaN, 1)) + @test isequal(mean(a00, (1, 2)), fill(NaN, 1, 1)) + @test isequal(mean(a01, 1) , fill(NaN, 1, 1)) + @test isequal(mean(a10, 2) , fill(NaN, 1, 1)) +end diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 638c26a21113e..4c2069bcf9782 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -189,16 +189,15 @@ end struct tstStringType <: AbstractString data::Array{UInt8,1} end -tstr = tstStringType("12") +tstr = tstStringType(Vector{UInt8}("12")) @test_throws ErrorException endof(tstr) @test_throws ErrorException next(tstr, Bool(1)) gstr = GenericString("12") -@test typeof(string(gstr))==GenericString +@test string(gstr) isa GenericString -@test convert(Array{UInt8}, gstr) ==[49;50] -@test convert(Array{Char,1}, gstr) ==['1';'2'] -@test convert(Symbol, gstr)==Symbol("12") +@test Array{UInt8}(gstr) == [49, 50] +@test Array{Char,1}(gstr) == ['1', '2'] @test gstr[1] == '1' @test gstr[1:1] == "1" @@ -465,6 +464,7 @@ foobaz(ch) = reinterpret(Char, typemax(UInt32)) # issue #18280: next/nextind must return past String's underlying data for s in ("Hello", "Σ", "こんにちは", "😊😁") + local s @test next(s, endof(s))[2] > sizeof(s) @test nextind(s, endof(s)) > sizeof(s) end diff --git a/test/strings/search.jl b/test/strings/search.jl index 34472914557b9..cb0c342844db0 100644 --- a/test/strings/search.jl +++ b/test/strings/search.jl @@ -379,3 +379,9 @@ end @test rsearchindex("\U1f596\U1f596", "\U1f596\U1f596", endof("\U1f596\U1f596\U1f596")) == 1 @test_throws ErrorException "ab" ∈ "abc" + +# issue #15723 +@test findfirst("⨳(", '(') == 4 +@test findnext("(⨳(", '(', 2) == 5 +@test findlast("(⨳(", '(') == 5 +@test findprev("(⨳(", '(', 2) == 1 diff --git a/test/strings/types.jl b/test/strings/types.jl index c4ff5227a02e2..a37193c4735fb 100644 --- a/test/strings/types.jl +++ b/test/strings/types.jl @@ -114,16 +114,19 @@ let s="lorem ipsum", SubString(s,12,14)=>"" ) for (ss,s) in sdict + local ss for i in -1:12 @test isvalid(ss,i)==isvalid(s,i) end end for (ss,s) in sdict + local ss for i in 1:length(ss) @test ind2chr(ss,i)==ind2chr(s,i) end end for (ss,s) in sdict + local ss for i in 1:length(ss) @test chr2ind(ss,i)==chr2ind(s,i) end diff --git a/test/strings/util.jl b/test/strings/util.jl index 7185964ae7aec..18f2d5002a6e8 100644 --- a/test/strings/util.jl +++ b/test/strings/util.jl @@ -247,3 +247,24 @@ bin_val = hex2bytes("07bf") #non-hex characters @test_throws ArgumentError hex2bytes("0123456789abcdefABCDEFGH") + +@testset "Issue 23161" begin + arr = b"0123456789abcdefABCDEF" + arr1 = Vector{UInt8}(length(arr) >> 1) + @test hex2bytes!(arr1, arr) === arr1 # check in-place + @test "0123456789abcdefabcdef" == bytes2hex(arr1) + @test hex2bytes("0123456789abcdefABCDEF") == hex2bytes(arr) + @test_throws ArgumentError hex2bytes!(arr1, b"") # incorrect arr1 length + @test hex2bytes(b"") == UInt8[] + @test hex2bytes(view(b"012345",1:6)) == UInt8[0x01,0x23,0x45] + @test begin + s = view(b"012345ab",1:6) + d = view(zeros(UInt8, 10),1:3) + hex2bytes!(d,s) == UInt8[0x01,0x23,0x45] + end + # odd size + @test_throws ArgumentError hex2bytes(b"0123456789abcdefABCDEF0") + + #non-hex characters + @test_throws ArgumentError hex2bytes(b"0123456789abcdefABCDEFGH") +end diff --git a/test/subarray.jl b/test/subarray.jl index f80ba3c200c1b..3cbbf426929fb 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -34,11 +34,10 @@ _Agen(A, i1, i2, i3, i4) = [A[j1,j2,j3,j4] for j1 in i1, j2 in i2, j3 in i3, j4 function replace_colon(A::AbstractArray, I) Iout = Array{Any}(length(I)) - for d = 1:length(I)-1 + I == (:,) && return (1:length(A),) + for d = 1:length(I) Iout[d] = isa(I[d], Colon) ? (1:size(A,d)) : I[d] end - d = length(I) - Iout[d] = isa(I[d], Colon) ? (1:prod(size(A)[d:end])) : I[d] (Iout...,) end @@ -226,11 +225,9 @@ end function runviews(SB::AbstractArray, indexN, indexNN, indexNNN) @assert ndims(SB) > 2 for i3 in indexN, i2 in indexN, i1 in indexN - ndims(SB) > 3 && i3 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2, i3) end for i2 in indexN, i1 in indexN - i2 isa Colon && continue # TODO: Re-enable once Colon no longer spans partial trailing dimensions runsubarraytests(SB, i1, i2) end for i1 in indexNNN @@ -282,9 +279,6 @@ _ndims(x) = 1 if testfull let B = copy(reshape(1:13^3, 13, 13, 13)) for o3 in oindex, o2 in oindex, o1 in oindex - if (o3 isa Colon && (_ndims(o1) + _ndims(o2) != 2)) - continue # TODO: remove once Colon no longer spans partial trailing dimensions - end viewB = view(B, o1, o2, o3) runviews(viewB, index5, index25, index125) end @@ -421,7 +415,7 @@ msk = ones(Bool, 2, 2) msk[2,1] = false sA = view(A, :, :, 1) sA[msk] = 1.0 -@test sA[msk] == ones(countnz(msk)) +@test sA[msk] == ones(count(msk)) # bounds checking upon construction; see #4044, #10296 @test_throws BoundsError view(1:10, 8:11) @@ -482,7 +476,7 @@ Y = 4:-1:1 @test X[1:end] == @.(@view X[1:end]) # test compatibility of @. and @view @test X[1:end-3] == @view X[1:end-3] @test X[1:end,2,2] == @view X[1:end,2,2] -# @test X[1,1:end-2] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation +@test X[1,1:end-2] == @view X[1,1:end-2] @test X[1,2,1:end-2] == @view X[1,2,1:end-2] @test X[1,2,Y[2:end]] == @view X[1,2,Y[2:end]] @test X[1:end,2,Y[2:end]] == @view X[1:end,2,Y[2:end]] @@ -533,7 +527,7 @@ end @test X[1:end] == @views X[1:end] @test X[1:end-3] == @views X[1:end-3] @test X[1:end,2,2] == @views X[1:end,2,2] -# @test X[1,1:end-2] == @views X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation +@test X[1,1:end-2] == @views X[1,1:end-2] @test X[1,2,1:end-2] == @views X[1,2,1:end-2] @test X[1,2,Y[2:end]] == @views X[1,2,Y[2:end]] @test X[1:end,2,Y[2:end]] == @views X[1:end,2,Y[2:end]] diff --git a/test/subtype.jl b/test/subtype.jl index 458843661792e..050dc8af0a6fb 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1094,12 +1094,12 @@ f20103(::Type{TT20103{X,Y}},x::X,y::Y) where {X,Y} = 1 f20103(::Type{TT20103{X,X}},x::X) where {X} = 100 @test_broken typeintersect(Type{NTuple{N,E}} where E where N, Type{NTuple{N,E} where N} where E) == Union{} # use @testintersect once fixed let ints = (Int, Int32, UInt, UInt32) - const Ints = Union{ints...} + Ints = Union{ints...} vecs = [] for i = 2:4, t in ints push!(vecs, NTuple{i, t}) end - const Vecs = Union{vecs...} + Vecs = Union{vecs...} T = Type{Tuple{V, I}} where V <: Vecs where I <: Ints @testintersect(T, T, T) test(a::Type{Tuple{V, I}}) where {V <: Vecs, I <: Ints} = I @@ -1130,3 +1130,22 @@ end @testintersect(Tuple{DataType, Any}, Tuple{Type{T}, Int} where T, Tuple{DataType, Int}) + +# issue #23430 +@test [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.] isa Matrix{Float64} +@test !(Tuple{Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64, + Int64,Float64,Int64,Float64,Int64,Float64,Int64,Float64} <: (Tuple{Vararg{T}} where T<:Number)) diff --git a/test/test.jl b/test/test.jl index 5bb97f6510bd6..96897aae0170b 100644 --- a/test/test.jl +++ b/test/test.jl @@ -521,7 +521,7 @@ str = String(take!(io)) # NOTE: This test depends on the code generated by @testset getting compiled, # to get good backtraces. If it fails, check the implementation of `testset_beginend`. @test contains(str, "test.jl") -@test !contains(str, "boot.jl") +@test !contains(str, "client.jl") let io = IOBuffer() exc = Test.TestSetException(1,2,3,4,Vector{Union{Base.Test.Error, Base.Test.Fail}}()) diff --git a/test/topology.jl b/test/topology.jl index 4c1b982152e31..75a4c3ae52322 100644 --- a/test/topology.jl +++ b/test/topology.jl @@ -77,8 +77,8 @@ while true end end -for p1 in workers() - for p2 in workers() +for outer p1 in workers() + for outer p2 in workers() i1 = map_pid_ident[p1] i2 = map_pid_ident[p2] if (iseven(i1) && iseven(i2)) || (isodd(i1) && isodd(i2)) diff --git a/test/unicode/utf8.jl b/test/unicode/utf8.jl index 093c899e0f3ee..bdb9664fc26fb 100644 --- a/test/unicode/utf8.jl +++ b/test/unicode/utf8.jl @@ -4,7 +4,7 @@ let ch = 0x10000 for hi = 0xd800:0xdbff for lo = 0xdc00:0xdfff - @test convert(String, Vector{UInt8}(String(Char[hi, lo]))) == string(Char(ch)) + @test String(Vector{UInt8}(String(Char[hi, lo]))) == string(Char(ch)) ch += 1 end end @@ -41,7 +41,7 @@ end end @testset "string convert" begin - @test convert(String, b"this is a test\xed\x80\x80") == "this is a test\ud000" + @test String(b"this is a test\xed\x80\x80") == "this is a test\ud000" ## Specifically check UTF-8 string whose lead byte is same as a surrogate - @test convert(String, b"\xed\x9f\xbf") == "\ud7ff" + @test String(b"\xed\x9f\xbf") == "\ud7ff" end diff --git a/test/unicode/utf8proc.jl b/test/unicode/utf8proc.jl index d06106a9abefc..2c70ed21291f8 100644 --- a/test/unicode/utf8proc.jl +++ b/test/unicode/utf8proc.jl @@ -310,7 +310,6 @@ end g = graphemes(str) h = hash(str) @test hash(g) == h - @test convert(GenericString, g) == str @test repr(g) == "length-14 GraphemeIterator{String} for \"$str\"" end end diff --git a/test/version.jl b/test/version.jl index 172e5e2cac3b6..f1e94fd0df477 100644 --- a/test/version.jl +++ b/test/version.jl @@ -95,6 +95,10 @@ show(io,v"4.3.2+1.a") # typemin and typemax @test typemin(VersionNumber) == v"0-" @test typemax(VersionNumber) == v"∞" +let ∞ = typemax(UInt32) + @test typemin(VersionNumber) == VersionNumber(0, 0, 0, ("",), ()) + @test typemax(VersionNumber) == VersionNumber(∞, ∞, ∞, (), ("",)) +end # issupbuild import Base.issupbuild @@ -275,3 +279,21 @@ for t = 1:1_000 @test (v ∈ a && v ∈ b) ? (v ∈ i) : (v ∉ i) end end + +# PR #23075 +@testset "versioninfo" begin + # check that versioninfo(io; verbose=true) doesn't error, produces some output + # and doesn't invoke Pkg.status which will error if JULIA_PKGDIR is set + mktempdir() do dir + withenv("JULIA_PKGDIR" => dir) do + buf = PipeBuffer() + versioninfo(buf, verbose=true) + ver = read(buf, String) + @test startswith(ver, "Julia Version $VERSION") + @test contains(ver, "Environment:") + @test contains(ver, "Package Status:") + @test contains(ver, "no packages installed") + @test isempty(readdir(dir)) + end + end +end diff --git a/ui/Makefile b/ui/Makefile index 49c4dbac40167..8f69136d1a501 100644 --- a/ui/Makefile +++ b/ui/Makefile @@ -11,7 +11,7 @@ override CPPFLAGS += $(JCPPFLAGS) SRCS := repl -HEADERS := $(addprefix $(JULIAHOME)/src/,julia.h julia_threads.h julia_internal.h options.h) \ +HEADERS := $(addprefix $(JULIAHOME)/src/,julia.h julia_assert.h julia_threads.h julia_internal.h options.h) \ $(BUILDDIR)/../src/julia_version.h $(wildcard $(JULIAHOME)/src/support/*.h) $(LIBUV_INC)/uv.h FLAGS := -I$(BUILDROOT)/src -I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(build_includedir) diff --git a/ui/repl.c b/ui/repl.c index cba286bd1cf9a..357f612a2987e 100644 --- a/ui/repl.c +++ b/ui/repl.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,8 @@ #include "uv.h" #include "../src/julia.h" +#include "../src/julia_assert.h" + JULIA_DEFINE_FAST_TLS() #ifdef __cplusplus