-
-
Save MikeInnes/c2d11b57a58d7f2466b8013b88df1f1c to your computer and use it in GitHub Desktop.
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Enums | |
import Core.Intrinsics.box | |
export Enum, @enum | |
abstract Enum | |
Base.convert{T<:Integer}(::Type{T}, x::Enum) = convert(T, box(Int32, x)) | |
Base.write(io::IO, x::Enum) = write(io, Int32(x)) | |
Base.read{T<:Enum}(io::IO, ::Type{T}) = T(read(io, Int32)) | |
# generate code to test whether expr is in the given set of values | |
function membershiptest(expr, values) | |
lo, hi = extrema(values) | |
if length(values) == hi - lo + 1 | |
:($lo <= $expr <= $hi) | |
elseif length(values) < 20 | |
foldl((x1,x2)->:($x1 || ($expr == $x2)), :($expr == $(values[1])), values[2:end]) | |
else | |
:($expr in $(Set(values))) | |
end | |
end | |
@noinline enum_argument_error(typename, x) = throw(ArgumentError(string("invalid value for Enum $(typename): $x"))) | |
""" | |
@enum EnumName EnumValue1[=x] EnumValue2[=y] | |
Create an [`Enum`](:obj:`Enum`) type with name `EnumName` and enum member values of | |
`EnumValue1` and `EnumValue2` with optional assigned values of `x` and `y`, respectively. | |
`EnumName` can be used just like other types and enum member values as regular values, such as | |
```jldoctest | |
julia> @enum FRUIT apple=1 orange=2 kiwi=3 | |
julia> f(x::FRUIT) = "I'm a FRUIT with value: \$(Int(x))" | |
f (generic function with 1 method) | |
julia> f(apple) | |
"I'm a FRUIT with value: 1" | |
``` | |
""" | |
macro enum(T,syms...) | |
if isempty(syms) | |
throw(ArgumentError("no arguments given for Enum $T")) | |
end | |
if !isa(T,Symbol) | |
throw(ArgumentError("invalid type expression for enum $T")) | |
end | |
typename = T | |
vals = Array{Tuple{Symbol,Integer}}(0) | |
lo = hi = 0 | |
i = Int32(-1) | |
hasexpr = false | |
for s in syms | |
if isa(s,Symbol) | |
if i == typemax(typeof(i)) | |
throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) | |
end | |
i += one(i) | |
elseif isa(s,Expr) && | |
(s.head == :(=) || s.head == :kw) && | |
length(s.args) == 2 && isa(s.args[1],Symbol) | |
i = eval(current_module(),s.args[2]) # allow exprs, e.g. uint128"1" | |
if !isa(i, Integer) | |
throw(ArgumentError("invalid value for Enum $typename, $s=$i; values must be integers")) | |
end | |
i = convert(Int32, i) | |
s = s.args[1] | |
hasexpr = true | |
else | |
throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) | |
end | |
if !Base.isidentifier(s) | |
throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier.")) | |
end | |
push!(vals, (s,i)) | |
if length(vals) == 1 | |
lo = hi = i | |
else | |
lo = min(lo, i) | |
hi = max(hi, i) | |
end | |
end | |
values = Int32[i[2] for i in vals] | |
if hasexpr && values != unique(values) | |
throw(ArgumentError("values for Enum $typename are not unique")) | |
end | |
blk = quote | |
# enum definition | |
Base.@__doc__(bitstype 32 $(esc(T)) <: Enum) | |
function Base.convert(::Type{$(esc(typename))}, x::Integer) | |
$(membershiptest(:x, values)) || enum_argument_error($(Expr(:quote, typename)), x) | |
box($(esc(typename)), convert(Int32, x)) | |
end | |
Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))($lo) | |
Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($hi) | |
Base.isless(x::$(esc(typename)), y::$(esc(typename))) = isless(Int32(x), Int32(y)) | |
let insts = ntuple(i->$(esc(typename))($values[i]), $(length(vals))) | |
Base.instances(::Type{$(esc(typename))}) = insts | |
end | |
function Base.print(io::IO, x::$(esc(typename))) | |
for (sym, i) in $vals | |
if i == Int32(x) | |
print(io, sym); break | |
end | |
end | |
end | |
function Base.show(io::IO, x::$(esc(typename))) | |
if get(io, :compact, false) | |
print(io, x) | |
else | |
print(io, x, "::") | |
showcompact(io, typeof(x)) | |
print(io, " = ", Int(x)) | |
end | |
end | |
function Base.show(io::IO, t::Type{$(esc(typename))}) | |
Base.show_datatype(io, t) | |
end | |
function Base.show(io::IO, ::MIME"text/plain", t::Type{$(esc(typename))}) | |
print(io, "Enum ") | |
Base.show_datatype(io, t) | |
print(io, ":") | |
for (sym, i) in $vals | |
print(io, "\n", sym, " = ", i) | |
end | |
end | |
end | |
if isa(T,Symbol) | |
for (sym,i) in vals | |
push!(blk.args, :(const $(esc(sym)) = $(esc(T))($i))) | |
end | |
end | |
push!(blk.args, :nothing) | |
blk.head = :toplevel | |
return blk | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module LineEdit | |
using ..Terminals | |
import ..Terminals: raw!, width, height, cmove, getX, | |
getY, clear_line, beep | |
import Base: ensureroom, peek, show, AnyDict | |
abstract TextInterface | |
abstract ModeState | |
export run_interface, Prompt, ModalInterface, transition, reset_state, edit_insert, keymap | |
immutable ModalInterface <: TextInterface | |
modes | |
end | |
type MIState | |
interface::ModalInterface | |
current_mode | |
aborted::Bool | |
mode_state | |
kill_buffer::String | |
previous_key::Array{Char,1} | |
key_repeats::Int | |
end | |
MIState(i, c, a, m) = MIState(i, c, a, m, "", Char[], 0) | |
function show(io::IO, s::MIState) | |
print(io, "MI State (", s.current_mode, " active)") | |
end | |
type Prompt <: TextInterface | |
prompt | |
# A string or function to be printed before the prompt. May not change the length of the prompt. | |
# This may be used for changing the color, issuing other terminal escape codes, etc. | |
prompt_prefix | |
# Same as prefix except after the prompt | |
prompt_suffix | |
keymap_dict | |
keymap_func_data | |
complete | |
on_enter | |
on_done | |
hist | |
sticky::Bool | |
end | |
show(io::IO, x::Prompt) = show(io, string("Prompt(\"", x.prompt, "\",...)")) | |
immutable InputAreaState | |
num_rows::Int64 | |
curs_row::Int64 | |
end | |
type PromptState <: ModeState | |
terminal | |
p::Prompt | |
input_buffer::IOBuffer | |
ias::InputAreaState | |
indent::Int | |
end | |
input_string(s::PromptState) = String(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] | |
return count(c->(c == '\n'), rest) | |
end | |
abstract HistoryProvider | |
abstract CompletionProvider | |
type EmptyCompletionProvider <: CompletionProvider | |
end | |
type EmptyHistoryProvider <: HistoryProvider | |
end | |
reset_state(::EmptyHistoryProvider) = nothing | |
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...) | |
end | |
function common_prefix(completions) | |
ret = "" | |
c1 = completions[1] | |
isempty(c1) && return ret | |
i = 1 | |
cc, nexti = next(c1, i) | |
while true | |
for c in completions | |
(i > endof(c) || c[i] != cc) && return ret | |
end | |
ret = string(ret, cc) | |
i >= endof(c1) && return ret | |
i = nexti | |
cc, nexti = next(c1, i) | |
end | |
end | |
# Show available completions | |
function show_completions(s::PromptState, completions) | |
colmax = maximum(map(length, completions)) | |
num_cols = max(div(width(terminal(s)), colmax+2), 1) | |
entries_per_col, r = divrem(length(completions), num_cols) | |
entries_per_col += r != 0 | |
# skip any lines of input after the cursor | |
cmove_down(terminal(s), input_string_newlines_aftercursor(s)) | |
println(terminal(s)) | |
for row = 1:entries_per_col | |
for col = 0:num_cols | |
idx = row + col*entries_per_col | |
if idx <= length(completions) | |
cmove_col(terminal(s), (colmax+2)*col) | |
print(terminal(s), completions[idx]) | |
end | |
end | |
println(terminal(s)) | |
end | |
# make space for the prompt | |
for i = 1:input_string_newlines(s) | |
println(terminal(s)) | |
end | |
end | |
# Prompt Completions | |
complete_line(s::MIState) = complete_line(s.mode_state[s.current_mode], s.key_repeats) | |
function complete_line(s::PromptState, repeats) | |
completions, partial, should_complete = complete_line(s.p.complete, s) | |
if isempty(completions) | |
beep(terminal(s)) | |
elseif !should_complete | |
# should_complete is false for cases where we only want to show | |
# a list of possible completions but not complete, e.g. foo(\t | |
show_completions(s, completions) | |
elseif length(completions) == 1 | |
# Replace word by completion | |
prev_pos = position(s.input_buffer) | |
seek(s.input_buffer, prev_pos-sizeof(partial)) | |
edit_replace(s, position(s.input_buffer), 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) | |
elseif repeats > 0 | |
show_completions(s, completions) | |
end | |
end | |
end | |
clear_input_area(terminal, s) = (_clear_input_area(terminal, s.ias); s.ias = InputAreaState(0, 0)) | |
clear_input_area(s) = clear_input_area(s.terminal, s) | |
function _clear_input_area(terminal, state::InputAreaState) | |
# Go to the last line | |
if state.curs_row < state.num_rows | |
cmove_down(terminal, state.num_rows - state.curs_row) | |
end | |
# Clear lines one by one going up | |
for j = 2:state.num_rows | |
clear_line(terminal) | |
cmove_up(terminal) | |
end | |
# Clear top line | |
clear_line(terminal) | |
end | |
prompt_string(s::PromptState) = s.p.prompt | |
prompt_string(s::AbstractString) = s | |
refresh_multi_line(s::ModeState) = refresh_multi_line(terminal(s), s) | |
refresh_multi_line(termbuf::TerminalBuffer, s::ModeState) = refresh_multi_line(termbuf, terminal(s), s) | |
refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState) = (@assert term == terminal(s); refresh_multi_line(termbuf,s)) | |
function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf, state::InputAreaState, prompt = ""; indent = 0) | |
_clear_input_area(termbuf, state) | |
cols = width(terminal) | |
curs_row = -1 # relative to prompt (1-based) | |
curs_pos = -1 # 1-based column position of the cursor | |
cur_row = 0 # count of the number of rows | |
buf_pos = position(buf) | |
line_pos = buf_pos | |
# Write out the prompt string | |
write_prompt(termbuf, prompt) | |
prompt = prompt_string(prompt) | |
# Count the '\n' at the end of the line if the terminal emulator does (specific to DOS cmd prompt) | |
miscountnl = @static is_windows() ? (isa(Terminals.pipe_reader(terminal), Base.TTY) && !Base.ispty(Terminals.pipe_reader(terminal))) : false | |
lindent = strwidth(prompt) | |
# Now go through the buffer line by line | |
seek(buf, 0) | |
moreinput = true # add a blank line if there is a trailing newline on the last line | |
while moreinput | |
l = readline(buf) | |
moreinput = endswith(l, "\n") | |
# We need to deal with on-screen characters, so use strwidth to compute occupied columns | |
llength = strwidth(l) | |
slength = sizeof(l) | |
cur_row += 1 | |
cmove_col(termbuf, lindent + 1) | |
write(termbuf, l) | |
# We expect to be line after the last valid output line (due to | |
# the '\n' at the end of the previous line) | |
if curs_row == -1 | |
# in this case, we haven't yet written the cursor position | |
line_pos -= slength # '\n' gets an extra pos | |
if line_pos < 0 || !moreinput | |
num_chars = (line_pos >= 0 ? llength : strwidth(l[1:(line_pos + slength)])) | |
curs_row, curs_pos = divrem(lindent + num_chars - 1, cols) | |
curs_row += cur_row | |
curs_pos += 1 | |
# There's an issue if the cursor is after the very right end of the screen. In that case we need to | |
# move the cursor to the next line, and emit a newline if needed | |
if curs_pos == cols | |
# only emit the newline if the cursor is at the end of the line we're writing | |
if line_pos == 0 | |
write(termbuf, "\n") | |
cur_row += 1 | |
end | |
curs_row += 1 | |
curs_pos = 0 | |
cmove_col(termbuf, 1) | |
end | |
end | |
end | |
cur_row += div(max(lindent + llength + miscountnl - 1, 0), cols) | |
lindent = indent | |
end | |
seek(buf, buf_pos) | |
# Let's move the cursor to the right position | |
# The line first | |
n = cur_row - curs_row | |
if n > 0 | |
cmove_up(termbuf, n) | |
end | |
#columns are 1 based | |
cmove_col(termbuf, curs_pos + 1) | |
# Updated cur_row,curs_row | |
return InputAreaState(cur_row, curs_row) | |
end | |
function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) | |
outbuf = IOBuffer() | |
termbuf = TerminalBuffer(outbuf) | |
ret = refresh_multi_line(termbuf, terminal, args...;kwargs...) | |
# Output the entire refresh at once | |
write(terminal, takebuf_array(outbuf)) | |
flush(terminal) | |
return ret | |
end | |
# Edit functionality | |
is_non_word_char(c) = c in " \t\n\"\\'`@\$><=:;|&{}()[].,+-*/?%^~" | |
function reset_key_repeats(f::Function, s::MIState) | |
key_repeats_sav = s.key_repeats | |
try | |
s.key_repeats = 0 | |
f() | |
finally | |
s.key_repeats = key_repeats_sav | |
end | |
end | |
char_move_left(s::PromptState) = char_move_left(s.input_buffer) | |
function char_move_left(buf::IOBuffer) | |
while position(buf) > 0 | |
seek(buf, position(buf)-1) | |
c = peek(buf) | |
(((c & 0x80) == 0) || ((c & 0xc0) == 0xc0)) && break | |
end | |
pos = position(buf) | |
c = read(buf, Char) | |
seek(buf, pos) | |
c | |
end | |
function edit_move_left(buf::IOBuffer) | |
if position(buf) > 0 | |
#move to the next base UTF8 character to the left | |
while true | |
c = char_move_left(buf) | |
if charwidth(c) != 0 || c == '\n' || position(buf) == 0 | |
break | |
end | |
end | |
return true | |
end | |
return false | |
end | |
edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) && refresh_line(s) | |
function edit_move_word_left(s) | |
if position(s.input_buffer) > 0 | |
char_move_word_left(s.input_buffer) | |
refresh_line(s) | |
end | |
end | |
char_move_right(s) = char_move_right(buffer(s)) | |
function char_move_right(buf::IOBuffer) | |
!eof(buf) && read(buf, Char) | |
end | |
function char_move_word_right(buf::IOBuffer, is_delimiter=is_non_word_char) | |
while !eof(buf) && is_delimiter(char_move_right(buf)) | |
end | |
while !eof(buf) | |
pos = position(buf) | |
if is_delimiter(char_move_right(buf)) | |
seek(buf, pos) | |
break | |
end | |
end | |
end | |
function char_move_word_left(buf::IOBuffer, is_delimiter=is_non_word_char) | |
while position(buf) > 0 && is_delimiter(char_move_left(buf)) | |
end | |
while position(buf) > 0 | |
pos = position(buf) | |
if is_delimiter(char_move_left(buf)) | |
seek(buf, pos) | |
break | |
end | |
end | |
end | |
char_move_word_right(s) = char_move_word_right(buffer(s)) | |
char_move_word_left(s) = char_move_word_left(buffer(s)) | |
function edit_move_right(buf::IOBuffer) | |
if !eof(buf) | |
# move to the next base UTF8 character to the right | |
while true | |
c = char_move_right(buf) | |
eof(buf) && break | |
pos = position(buf) | |
nextc = read(buf,Char) | |
seek(buf,pos) | |
(charwidth(nextc) != 0 || nextc == '\n') && break | |
end | |
return true | |
end | |
return false | |
end | |
edit_move_right(s::PromptState) = edit_move_right(s.input_buffer) && refresh_line(s) | |
function edit_move_word_right(s) | |
if !eof(s.input_buffer) | |
char_move_word_right(s) | |
refresh_line(s) | |
end | |
end | |
## Move line up/down | |
# Querying the terminal is expensive, memory access is cheap | |
# so to find the current column, we find the offset for the start | |
# of the line. | |
function edit_move_up(buf::IOBuffer) | |
npos = 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))])) | |
npos2 = rsearch(buf.data, '\n', npos-1) | |
seek(buf, npos2) | |
for _ = 1:offset | |
pos = position(buf) | |
if read(buf, Char) == '\n' | |
seek(buf, pos) | |
break | |
end | |
end | |
return true | |
end | |
function edit_move_up(s) | |
changed = edit_move_up(buffer(s)) | |
changed && refresh_line(s) | |
changed | |
end | |
function edit_move_down(buf::IOBuffer) | |
npos = rsearch(buf.data[1:buf.size], '\n', position(buf)) | |
# We're interested in character count, not byte count | |
offset = length(String(buf.data[(npos+1):(position(buf))])) | |
npos2 = search(buf.data[1:buf.size], '\n', position(buf)+1) | |
if npos2 == 0 #we're in the last line | |
return false | |
end | |
seek(buf, npos2) | |
for _ = 1:offset | |
pos = position(buf) | |
if eof(buf) || read(buf, Char) == '\n' | |
seek(buf, pos) | |
break | |
end | |
end | |
return true | |
end | |
function edit_move_down(s) | |
changed = edit_move_down(buffer(s)) | |
changed && refresh_line(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!{T<:Integer}(buf::IOBuffer, r::UnitRange{T}, ins::AbstractString = "") | |
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, ins.data) # position(), etc, are 0-indexed | |
buf.size = buf.size + sizeof(ins) - length(r) | |
seek(buf, position(buf) + sizeof(ins)) | |
end | |
function edit_replace(s, from, to, str) | |
splice_buffer!(buffer(s), from:to-1, str) | |
end | |
function edit_insert(s::PromptState, c) | |
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(s.p.prompt) : s.indent | |
if !('\n' in str) && eof(buf) && | |
((line_size() + 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) | |
else | |
refresh_line(s) | |
end | |
end | |
function edit_insert(buf::IOBuffer, c) | |
if eof(buf) | |
return write(buf, c) | |
else | |
s = string(c) | |
splice_buffer!(buf, position(buf):position(buf)-1, s) | |
return sizeof(s) | |
end | |
end | |
function edit_backspace(s::PromptState) | |
if edit_backspace(s.input_buffer) | |
refresh_line(s) | |
else | |
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 | |
else | |
return false | |
end | |
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) | |
true | |
end | |
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 | |
end | |
function edit_werase(s) | |
edit_werase(buffer(s)) && refresh_line(s) | |
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 | |
end | |
function edit_delete_prev_word(s) | |
edit_delete_prev_word(buffer(s)) && refresh_line(s) | |
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 | |
end | |
function edit_delete_next_word(s) | |
edit_delete_next_word(buffer(s)) && refresh_line(s) | |
end | |
function edit_yank(s::MIState) | |
edit_insert(buffer(s), s.kill_buffer) | |
refresh_line(s) | |
end | |
function edit_kill_line(s::MIState) | |
buf = buffer(s) | |
pos = position(buf) | |
killbuf = readline(buf) | |
if length(killbuf) > 1 && killbuf[end] == '\n' | |
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) | |
refresh_line(s) | |
end | |
edit_transpose(s) = edit_transpose(buffer(s)) && refresh_line(s) | |
function edit_transpose(buf::IOBuffer) | |
position(buf) == 0 && return false | |
eof(buf) && char_move_left(buf) | |
char_move_left(buf) | |
pos = position(buf) | |
a, b = read(buf, Char), read(buf, Char) | |
seek(buf, pos) | |
write(buf, b, a) | |
return true | |
end | |
edit_clear(buf::IOBuffer) = truncate(buf, 0) | |
function edit_clear(s::MIState) | |
edit_clear(buffer(s)) | |
refresh_line(s) | |
end | |
function replace_line(s::PromptState, l::IOBuffer) | |
s.input_buffer = copy(l) | |
end | |
function replace_line(s::PromptState, l) | |
s.input_buffer.ptr = 1 | |
s.input_buffer.size = 0 | |
write(s.input_buffer, l) | |
end | |
history_prev(::EmptyHistoryProvider) = ("", false) | |
history_next(::EmptyHistoryProvider) = ("", false) | |
history_search(::EmptyHistoryProvider, args...) = false | |
add_history(::EmptyHistoryProvider, s) = nothing | |
add_history(s::PromptState) = add_history(mode(s).hist, s) | |
history_next_prefix(s, hist, prefix) = false | |
history_prev_prefix(s, hist, prefix) = false | |
function history_prev(s, hist) | |
l, ok = history_prev(mode(s).hist) | |
if ok | |
replace_line(s, l) | |
move_input_start(s) | |
refresh_line(s) | |
else | |
beep(terminal(s)) | |
end | |
end | |
function history_next(s, hist) | |
l, ok = history_next(mode(s).hist) | |
if ok | |
replace_line(s, l) | |
move_input_end(s) | |
refresh_line(s) | |
else | |
beep(terminal(s)) | |
end | |
end | |
refresh_line(s) = refresh_multi_line(s) | |
refresh_line(s, termbuf) = refresh_multi_line(termbuf, s) | |
default_completion_cb(::IOBuffer) = [] | |
default_enter_cb(_) = true | |
write_prompt(terminal, s::PromptState) = write_prompt(terminal, s.p) | |
function write_prompt(terminal, p::Prompt) | |
prefix = isa(p.prompt_prefix,Function) ? p.prompt_prefix() : p.prompt_prefix | |
suffix = isa(p.prompt_suffix,Function) ? p.prompt_suffix() : p.prompt_suffix | |
write(terminal, prefix) | |
write(terminal, p.prompt) | |
write(terminal, Base.text_colors[:normal]) | |
write(terminal, suffix) | |
end | |
write_prompt(terminal, s::String) = write(terminal, s) | |
### Keymap Support | |
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.") | |
buf = IOBuffer() | |
i = start(key) | |
while !done(key, i) | |
c, i = next(key, i) | |
if c == '*' | |
write(buf, '\0') | |
elseif c == '^' | |
c, i = next(key, i) | |
write(buf, uppercase(c)-64) | |
elseif c == '\\' | |
c, i = next(key, i) | |
if c == 'C' | |
c, i = next(key, i) | |
@assert c == '-' | |
c, i = next(key, i) | |
write(buf, uppercase(c)-64) | |
elseif c == 'M' | |
c, i = next(key, i) | |
@assert c == '-' | |
c, i = next(key, i) | |
write(buf, '\e') | |
write(buf, c) | |
end | |
else | |
write(buf, c) | |
end | |
end | |
return takebuf_string(buf) | |
end | |
function normalize_keys(keymap::Dict) | |
ret = Dict{Any,Any}() | |
for (k,v) in keymap | |
normalized = normalize_key(k) | |
if haskey(ret,normalized) | |
error("""Multiple spellings of a key in a single keymap | |
(\"$k\" conflicts with existing mapping)""") | |
end | |
ret[normalized] = v | |
end | |
return ret | |
end | |
function add_nested_key!(keymap::Dict, 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) | |
keymap[c] = value | |
break | |
else | |
keymap[c] = Dict{Char,Any}() | |
end | |
keymap = keymap[c] | |
end | |
end | |
# Redirect a key as if `seq` had been the keysequence instead in a lazy fashion. | |
# This is different from the default eager redirect, which only looks at the current and lower | |
# layers of the stack. | |
immutable KeyAlias | |
seq::String | |
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))) | |
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) | |
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) | |
push!(cs, c) | |
key = haskey(k, c) ? c : '\0' | |
# 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 | |
keymap_fcn(f::Void, c) = (s, p) -> return :ok | |
function keymap_fcn(f::Function, c) | |
return function (s, p) | |
r = f(s, p, c) | |
if isa(r, Symbol) | |
return r | |
else | |
return :ok | |
end | |
end | |
end | |
update_key_repeats(s, keystroke) = nothing | |
function update_key_repeats(s::MIState, keystroke) | |
s.key_repeats = s.previous_key == keystroke ? s.key_repeats + 1 : 0 | |
s.previous_key = keystroke | |
return | |
end | |
## Conflict fixing | |
# Consider a keymap of the form | |
# | |
# { | |
# "**" => f | |
# "ab" => g | |
# } | |
# | |
# Naively this is transformed into a tree as | |
# | |
# { | |
# '*' => { | |
# '*' => f | |
# } | |
# 'a' => { | |
# 'b' => g | |
# } | |
# } | |
# | |
# However, that's not what we want, because now "ac" is | |
# is not defined. We need to fix this up and turn it into | |
# | |
# { | |
# '*' => { | |
# '*' => f | |
# } | |
# 'a' => { | |
# '*' => f | |
# 'b' => g | |
# } | |
# } | |
# | |
# i.e. copy over the appropraite default subdict | |
# | |
# deep merge where target has higher precedence | |
function keymap_merge!(target::Dict, source::Dict) | |
for k in keys(source) | |
if !haskey(target, k) | |
target[k] = source[k] | |
elseif isa(target[k], Dict) | |
keymap_merge!(target[k], source[k]) | |
else | |
# Ignore, target has higher precedence | |
end | |
end | |
end | |
fixup_keymaps!(d, l, s, sk) = nothing | |
function fixup_keymaps!(dict::Dict, level, s, subkeymap) | |
if level > 0 | |
for d in values(dict) | |
fixup_keymaps!(d, level-1, s, subkeymap) | |
end | |
else | |
if haskey(dict, s) | |
if isa(dict[s], Dict) && isa(subkeymap, Dict) | |
keymap_merge!(dict[s], subkeymap) | |
end | |
else | |
dict[s] = deepcopy(subkeymap) | |
end | |
end | |
end | |
function add_specialisations(dict, subdict, level) | |
default_branch = subdict['\0'] | |
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) | |
fixup_keymaps!(dict, level, s, default_branch[s]) | |
end | |
end | |
end | |
postprocess!(others) = nothing | |
function postprocess!(dict::Dict) | |
# needs to be done first for every branch | |
if haskey(dict, '\0') | |
add_specialisations(dict, dict, 1) | |
end | |
for (k,v) in dict | |
k == '\0' && continue | |
postprocess!(v) | |
end | |
end | |
function getEntry(keymap,key) | |
v = keymap | |
for c in key | |
if !haskey(v,c) | |
return nothing | |
end | |
v = v[c] | |
end | |
return v | |
end | |
# `target` is the total keymap being built up, already being a nested tree of Dicts. | |
# source is the keymap specified by the user (with normalized keys) | |
function keymap_merge(target,source) | |
ret = copy(target) | |
direct_keys = filter((k,v) -> isa(v, Union{Function, KeyAlias, Void}), source) | |
# first direct entries | |
for key in keys(direct_keys) | |
add_nested_key!(ret, key, source[key]; override = true) | |
end | |
# then redirected entries | |
for key in setdiff(keys(source), keys(direct_keys)) | |
# We first resolve redirects in the source | |
value = source[key] | |
visited = Array{Any}(0) | |
while isa(value, Union{Char,AbstractString}) | |
value = normalize_key(value) | |
if value in visited | |
error("Eager redirection cycle detected for key " * escape_string(key)) | |
end | |
push!(visited,value) | |
if !haskey(source,value) | |
break | |
end | |
value = source[value] | |
end | |
if isa(value, Union{Char,AbstractString}) | |
value = getEntry(ret, value) | |
if value === nothing | |
error("Could not find redirected value " * escape_string(source[key])) | |
end | |
end | |
add_nested_key!(ret, key, value; override = true) | |
end | |
ret | |
end | |
function keymap_unify(keymaps) | |
ret = Dict{Char,Any}() | |
for keymap in keymaps | |
ret = keymap_merge(ret, keymap) | |
end | |
postprocess!(ret) | |
return ret | |
end | |
function validate_keymap(keymap) | |
for key in keys(keymap) | |
visited_keys = Any[key] | |
v = getEntry(keymap,key) | |
while isa(v,KeyAlias) | |
if v.seq in visited_keys | |
error("Alias cycle detected in keymap") | |
end | |
push!(visited_keys,v.seq) | |
v = getEntry(keymap,v.seq) | |
end | |
end | |
end | |
function keymap{D<:Dict}(keymaps::Array{D}) | |
# keymaps is a vector of prioritized keymaps, with highest priority first | |
ret = keymap_unify(map(normalize_keys, reverse(keymaps))) | |
validate_keymap(ret) | |
ret | |
end | |
const escape_defaults = merge!( | |
AnyDict(Char(i) => nothing for i=vcat(1:26, 28:31)), # Ignore control characters by default | |
AnyDict( # And ignore other escape sequences by default | |
"\e*" => nothing, | |
"\e[*" => nothing, | |
"\eO*" => nothing, | |
# Also ignore extended escape sequences | |
# TODO: Support ranges of characters | |
"\e[1**" => nothing, | |
"\e[2**" => nothing, | |
"\e[3**" => nothing, | |
"\e[4**" => nothing, | |
"\e[5**" => nothing, | |
"\e[6**" => nothing, | |
# less commonly used VT220 editing keys | |
"\e[2~" => nothing, # insert | |
"\e[3~" => nothing, # delete | |
"\e[5~" => nothing, # page up | |
"\e[6~" => nothing, # page down | |
# These are different spellings of arrow keys, home keys, etc. | |
# and should always do the same as the canonical key sequence | |
"\e[1~" => KeyAlias("\e[H"), # home | |
"\e[4~" => KeyAlias("\e[F"), # end | |
"\e[7~" => KeyAlias("\e[H"), # home | |
"\e[8~" => KeyAlias("\e[F"), # end | |
"\eOA" => KeyAlias("\e[A"), | |
"\eOB" => KeyAlias("\e[B"), | |
"\eOC" => KeyAlias("\e[C"), | |
"\eOD" => KeyAlias("\e[D"), | |
"\eOH" => KeyAlias("\e[H"), | |
"\eOF" => KeyAlias("\e[F"), | |
), | |
# set mode commands | |
AnyDict("\e[$(c)h" => nothing for c in 1:20), | |
# reset mode commands | |
AnyDict("\e[$(c)l" => nothing for c in 1:20) | |
) | |
function write_response_buffer(s::PromptState, data) | |
offset = s.input_buffer.ptr | |
ptr = data.response_buffer.ptr | |
seek(data.response_buffer, 0) | |
write(s.input_buffer, readstring(data.response_buffer)) | |
s.input_buffer.ptr = offset + ptr - 2 | |
data.response_buffer.ptr = ptr | |
refresh_line(s) | |
end | |
type SearchState <: ModeState | |
terminal | |
histprompt | |
#rsearch (true) or ssearch (false) | |
backward::Bool | |
query_buffer::IOBuffer | |
response_buffer::IOBuffer | |
ias::InputAreaState | |
#The prompt whose input will be replaced by the matched history | |
parent | |
SearchState(terminal, histprompt, backward, query_buffer, response_buffer) = | |
new(terminal, histprompt, backward, query_buffer, response_buffer, InputAreaState(0,0)) | |
end | |
terminal(s::SearchState) = s.terminal | |
function update_display_buffer(s::SearchState, data) | |
history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, false) || beep(terminal(s)) | |
refresh_line(s) | |
end | |
function history_next_result(s::MIState, data::SearchState) | |
history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, true) || beep(terminal(s)) | |
refresh_line(data) | |
end | |
function history_set_backward(s::SearchState, backward) | |
s.backward = backward | |
end | |
input_string(s::SearchState) = String(s.query_buffer) | |
function reset_state(s::SearchState) | |
if s.query_buffer.size != 0 | |
s.query_buffer.size = 0 | |
s.query_buffer.ptr = 1 | |
end | |
if s.response_buffer.size != 0 | |
s.response_buffer.size = 0 | |
s.response_buffer.ptr = 1 | |
end | |
reset_state(s.histprompt.hp) | |
end | |
type HistoryPrompt{T<:HistoryProvider} <: TextInterface | |
hp::T | |
complete | |
keymap_dict::Dict{Char,Any} | |
HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) | |
end | |
HistoryPrompt{T<:HistoryProvider}(hp::T) = HistoryPrompt{T}(hp) | |
init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) | |
type PrefixSearchState <: ModeState | |
terminal | |
histprompt | |
prefix::String | |
response_buffer::IOBuffer | |
ias::InputAreaState | |
indent::Int | |
# The modal interface state, if present | |
mi | |
#The prompt whose input will be replaced by the matched history | |
parent | |
PrefixSearchState(terminal, histprompt, prefix, response_buffer) = | |
new(terminal, histprompt, prefix, response_buffer, InputAreaState(0,0), 0) | |
end | |
function show(io::IO, s::PrefixSearchState) | |
print(io, "PrefixSearchState ", isdefined(s,:parent) ? | |
string("(", s.parent, " active)") : "(no parent)", " for ", | |
isdefined(s,:mi) ? s.mi : "no MI") | |
end | |
refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, | |
s::Union{PromptState,PrefixSearchState}) = s.ias = | |
refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s, indent = s.indent) | |
input_string(s::PrefixSearchState) = String(s.response_buffer) | |
# a meta-prompt that presents itself as parent_prompt, but which has an independent keymap | |
# for prefix searching | |
type PrefixHistoryPrompt{T<:HistoryProvider} <: TextInterface | |
hp::T | |
parent_prompt::Prompt | |
complete | |
keymap_dict::Dict{Char,Any} | |
PrefixHistoryPrompt(hp, parent_prompt) = new(hp, parent_prompt, EmptyCompletionProvider()) | |
end | |
PrefixHistoryPrompt{T<:HistoryProvider}(hp::T, parent_prompt) = PrefixHistoryPrompt{T}(hp, parent_prompt) | |
init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) | |
write_prompt(terminal, s::PrefixSearchState) = write_prompt(terminal, s.histprompt.parent_prompt) | |
prompt_string(s::PrefixSearchState) = s.histprompt.parent_prompt.prompt | |
terminal(s::PrefixSearchState) = s.terminal | |
function reset_state(s::PrefixSearchState) | |
if s.response_buffer.size != 0 | |
s.response_buffer.size = 0 | |
s.response_buffer.ptr = 1 | |
end | |
reset_state(s.histprompt.hp) | |
end | |
function transition(f::Function, s::PrefixSearchState, mode) | |
if isdefined(s, :mi) | |
transition(s.mi, mode) | |
end | |
s.parent = mode | |
s.histprompt.parent_prompt = mode | |
if isdefined(s, :mi) | |
transition(f, s.mi, s.histprompt) | |
else | |
f() | |
end | |
end | |
replace_line(s::PrefixSearchState, l::IOBuffer) = s.response_buffer = l | |
function replace_line(s::PrefixSearchState, l) | |
s.response_buffer.ptr = 1 | |
s.response_buffer.size = 0 | |
write(s.response_buffer, l) | |
end | |
function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) | |
buf = IOBuffer() | |
unsafe_write(buf, pointer(s.query_buffer.data), s.query_buffer.ptr-1) | |
write(buf, "': ") | |
offset = buf.ptr | |
ptr = s.response_buffer.ptr | |
seek(s.response_buffer, 0) | |
write(buf, readstring(s.response_buffer)) | |
buf.ptr = offset + ptr - 1 | |
s.response_buffer.ptr = ptr | |
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) | |
mode(s::MIState) = s.current_mode | |
mode(s::PromptState) = s.p | |
mode(s::SearchState) = @assert false | |
mode(s::PrefixSearchState) = s.histprompt.parent_prompt | |
# Search Mode completions | |
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]) | |
end | |
end | |
function accept_result(s, p) | |
parent = state(s, p).parent | |
transition(s, parent) do | |
replace_line(state(s, parent), state(s, p).response_buffer) | |
end | |
end | |
function copybuf!(dst::IOBuffer, src::IOBuffer) | |
n = src.size | |
ensureroom(dst, n) | |
copy!(dst.data, 1, src.data, 1, n) | |
dst.size = src.size | |
dst.ptr = src.ptr | |
end | |
function enter_search(s::MIState, p::HistoryPrompt, backward::Bool) | |
# a bit of hack to help fix #6325 | |
buf = copy(buffer(s)) | |
parent = mode(s) | |
p.hp.last_mode = mode(s) | |
p.hp.last_buffer = buf | |
transition(s, p) do | |
ss = state(s, p) | |
ss.parent = parent | |
ss.backward = backward | |
truncate(ss.query_buffer, 0) | |
copybuf!(ss.response_buffer, buf) | |
end | |
end | |
function enter_prefix_search(s::MIState, p::PrefixHistoryPrompt, backward::Bool) | |
buf = copy(buffer(s)) | |
parent = mode(s) | |
transition(s, p) do | |
pss = state(s, p) | |
pss.parent = parent | |
pss.histprompt.parent_prompt = parent | |
pss.prefix = String(buf.data[1:position(buf)]) | |
copybuf!(pss.response_buffer, buf) | |
pss.indent = state(s, parent).indent | |
pss.mi = s | |
end | |
pss = state(s, p) | |
if backward | |
history_prev_prefix(pss, pss.histprompt.hp, pss.prefix) | |
else | |
history_next_prefix(pss, pss.histprompt.hp, pss.prefix) | |
end | |
end | |
function setup_search_keymap(hp) | |
p = HistoryPrompt(hp) | |
pkeymap = AnyDict( | |
"^R" => (s,data,c)->(history_set_backward(data, true); history_next_result(s, data)), | |
"^S" => (s,data,c)->(history_set_backward(data, false); history_next_result(s, data)), | |
'\r' => (s,o...)->accept_result(s, p), | |
'\n' => '\r', | |
# Limited form of tab completions | |
'\t' => (s,data,c)->(complete_line(s); update_display_buffer(s, data)), | |
"^L" => (s,data,c)->(Terminals.clear(terminal(s)); update_display_buffer(s, data)), | |
# Backspace/^H | |
'\b' => (s,data,c)->(edit_backspace(data.query_buffer) ? | |
update_display_buffer(s, data) : beep(terminal(s))), | |
127 => KeyAlias('\b'), | |
# Meta Backspace | |
"\e\b" => (s,data,c)->(edit_delete_prev_word(data.query_buffer) ? | |
update_display_buffer(s, data) : beep(terminal(s))), | |
"\e\x7f" => "\e\b", | |
# Word erase to whitespace | |
"^W" => (s,data,c)->(edit_werase(data.query_buffer) ? | |
update_display_buffer(s, data) : beep(terminal(s))), | |
# ^C and ^D | |
"^C" => (s,data,c)->(edit_clear(data.query_buffer); | |
edit_clear(data.response_buffer); | |
update_display_buffer(s, data); | |
reset_state(data.histprompt.hp); | |
transition(s, data.parent)), | |
"^D" => "^C", | |
# Other ways to cancel search mode (it's difficult to bind \e itself) | |
"^G" => "^C", | |
"\e\e" => "^C", | |
"^K" => (s,o...)->transition(s, state(s, p).parent), | |
"^Y" => (s,data,c)->(edit_yank(s); update_display_buffer(s, data)), | |
"^U" => (s,data,c)->(edit_clear(data.query_buffer); | |
edit_clear(data.response_buffer); | |
update_display_buffer(s, data)), | |
# Right Arrow | |
"\e[C" => (s,o...)->(accept_result(s, p); edit_move_right(s)), | |
# Left Arrow | |
"\e[D" => (s,o...)->(accept_result(s, p); edit_move_left(s)), | |
# Up Arrow | |
"\e[A" => (s,o...)->(accept_result(s, p); edit_move_up(s)), | |
# Down Arrow | |
"\e[B" => (s,o...)->(accept_result(s, p); edit_move_down(s)), | |
"^B" => (s,o...)->(accept_result(s, p); edit_move_left(s)), | |
"^F" => (s,o...)->(accept_result(s, p); edit_move_right(s)), | |
# Meta B | |
"\eb" => (s,o...)->(accept_result(s, p); edit_move_word_left(s)), | |
# Meta F | |
"\ef" => (s,o...)->(accept_result(s, p); edit_move_word_right(s)), | |
# Ctrl-Left Arrow | |
"\e[1;5D" => "\eb", | |
# Ctrl-Left Arrow on rxvt | |
"\eOd" => "\eb", | |
# Ctrl-Right Arrow | |
"\e[1;5C" => "\ef", | |
# Ctrl-Right Arrow on rxvt | |
"\eOc" => "\ef", | |
"^A" => (s,o...)->(accept_result(s, p); move_line_start(s); refresh_line(s)), | |
"^E" => (s,o...)->(accept_result(s, p); move_line_end(s); refresh_line(s)), | |
"^Z" => (s,o...)->(return :suspend), | |
# Try to catch all Home/End keys | |
"\e[H" => (s,o...)->(accept_result(s, p); move_input_start(s); refresh_line(s)), | |
"\e[F" => (s,o...)->(accept_result(s, p); move_input_end(s); refresh_line(s)), | |
# Use ^N and ^P to change search directions and iterate through results | |
"^N" => (s,data,c)->(history_set_backward(data, false); history_next_result(s, data)), | |
"^P" => (s,data,c)->(history_set_backward(data, true); history_next_result(s, data)), | |
# Bracketed paste mode | |
"\e[200~" => (s,data,c)-> begin | |
ps = state(s, mode(s)) | |
input = readuntil(ps.terminal, "\e[201~")[1:(end-6)] | |
edit_insert(data.query_buffer, input); update_display_buffer(s, data) | |
end, | |
"*" => (s,data,c)->(edit_insert(data.query_buffer, c); update_display_buffer(s, data)) | |
) | |
p.keymap_dict = keymap([pkeymap, escape_defaults]) | |
skeymap = AnyDict( | |
"^R" => (s,o...)->(enter_search(s, p, true)), | |
"^S" => (s,o...)->(enter_search(s, p, false)), | |
) | |
(p, skeymap) | |
end | |
keymap(state, p::Union{HistoryPrompt,PrefixHistoryPrompt}) = p.keymap_dict | |
keymap_data(state, ::Union{HistoryPrompt, PrefixHistoryPrompt}) = state | |
Base.isempty(s::PromptState) = s.input_buffer.size == 0 | |
on_enter(s::PromptState) = s.p.on_enter(s) | |
move_input_start(s) = (seek(buffer(s), 0)) | |
move_input_end(buf::IOBuffer) = seekend(buf) | |
move_input_end(s) = move_input_end(buffer(s)) | |
function move_line_start(s::MIState) | |
buf = buffer(s) | |
curpos = position(buf) | |
curpos == 0 && return | |
if s.key_repeats > 0 | |
move_input_start(s) | |
else | |
seek(buf, rsearch(buf.data, '\n', curpos)) | |
end | |
end | |
function move_line_end(s::MIState) | |
s.key_repeats > 0 ? | |
move_input_end(s) : | |
move_line_end(buffer(s)) | |
end | |
function move_line_end(buf::IOBuffer) | |
eof(buf) && return | |
pos = search(buf.data, '\n', position(buf)+1) | |
if pos == 0 | |
move_input_end(buf) | |
return | |
end | |
seek(buf, pos-1) | |
end | |
function commit_line(s) | |
move_input_end(s) | |
refresh_line(s) | |
println(terminal(s)) | |
add_history(s) | |
state(s, mode(s)).ias = InputAreaState(0, 0) | |
end | |
""" | |
`Base.LineEdit.tabwidth` controls the presumed tab width of code pasted into the REPL. | |
You can modify it by doing `eval(Base.LineEdit, :(tabwidth = 4))`, for example. | |
Must satisfy `0 < tabwidth <= 16`. | |
""" | |
global tabwidth = 8 | |
function bracketed_paste(s) | |
ps = state(s, mode(s)) | |
input = readuntil(ps.terminal, "\e[201~")[1:(end-6)] | |
input = replace(input, '\r', '\n') | |
if position(buffer(s)) == 0 | |
indent = Base.indentation(input; tabwidth=tabwidth)[1] | |
input = Base.unindent(input, indent; tabwidth=tabwidth) | |
end | |
return replace(input, '\t', " "^tabwidth) | |
end | |
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 <tab>`, 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, | |
# 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') | |
end | |
end, | |
'\n' => KeyAlias('\r'), | |
# Backspace/^H | |
'\b' => (s,o...)->edit_backspace(s), | |
127 => KeyAlias('\b'), | |
# Meta Backspace | |
"\e\b" => (s,o...)->edit_delete_prev_word(s), | |
"\e\x7f" => "\e\b", | |
# ^D | |
"^D" => (s,o...)->begin | |
if buffer(s).size > 0 | |
edit_delete(s) | |
else | |
println(terminal(s)) | |
return :abort | |
end | |
end, | |
"^B" => (s,o...)->edit_move_left(s), | |
"^F" => (s,o...)->edit_move_right(s), | |
# Meta B | |
"\eb" => (s,o...)->edit_move_word_left(s), | |
# Meta F | |
"\ef" => (s,o...)->edit_move_word_right(s), | |
# Ctrl-Left Arrow | |
"\e[1;5D" => "\eb", | |
# Ctrl-Left Arrow on rxvt | |
"\eOd" => "\eb", | |
# Ctrl-Right Arrow | |
"\e[1;5C" => "\ef", | |
# Ctrl-Right Arrow on rxvt | |
"\eOc" => "\ef", | |
# Meta Enter | |
"\e\r" => (s,o...)->(edit_insert(s, '\n')), | |
"\e\n" => "\e\r", | |
# 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), | |
"^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 | |
"\e[H" => (s,o...)->(move_input_start(s); refresh_line(s)), | |
"\e[F" => (s,o...)->(move_input_end(s); refresh_line(s)), | |
"^L" => (s,o...)->(Terminals.clear(terminal(s)); refresh_line(s)), | |
"^W" => (s,o...)->edit_werase(s), | |
# Meta D | |
"\ed" => (s,o...)->edit_delete_next_word(s), | |
"^C" => (s,o...)->begin | |
try # raise the debugger if present | |
ccall(:jl_raise_debugger, Int, ()) | |
end | |
move_input_end(s) | |
refresh_line(s) | |
print(terminal(s), "^C\n\n") | |
transition(s, :reset) | |
refresh_line(s) | |
end, | |
"^Z" => (s,o...)->(return :suspend), | |
# Right Arrow | |
"\e[C" => (s,o...)->edit_move_right(s), | |
# Left Arrow | |
"\e[D" => (s,o...)->edit_move_left(s), | |
# Up Arrow | |
"\e[A" => (s,o...)->edit_move_up(s), | |
# Down Arrow | |
"\e[B" => (s,o...)->edit_move_down(s), | |
# Delete | |
"\e[3~" => (s,o...)->edit_delete(s), | |
# Bracketed Paste Mode | |
"\e[200~" => (s,o...)->begin | |
input = bracketed_paste(s) | |
edit_insert(s, input) | |
end, | |
"^T" => (s,o...)->edit_transpose(s) | |
) | |
const history_keymap = AnyDict( | |
"^P" => (s,o...)->(history_prev(s, mode(s).hist)), | |
"^N" => (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 | |
"\e[B" => (s,o...)->(edit_move_down(s) || history_next(s, mode(s).hist)), | |
# Page Up | |
"\e[5~" => (s,o...)->(history_prev(s, mode(s).hist)), | |
# Page Down | |
"\e[6~" => (s,o...)->(history_next(s, mode(s).hist)) | |
) | |
const prefix_history_keymap = merge!( | |
AnyDict( | |
# Up Arrow | |
"\e[A" => (s,data,c)->history_prev_prefix(data, data.histprompt.hp, data.prefix), | |
# Down Arrow | |
"\e[B" => (s,data,c)->history_next_prefix(data, data.histprompt.hp, data.prefix), | |
# by default, pass thru to the parent mode | |
"*" => (s,data,c)->begin | |
accept_result(s, data.histprompt); | |
ps = state(s, mode(s)) | |
map = keymap(ps, mode(s)) | |
match_input(map, s, IOBuffer(c))(s, keymap_data(ps, mode(s))) | |
end, | |
# match escape sequences for pass thru | |
"\e*" => "*", | |
"\e[*" => "*", | |
"\eO*" => "*", | |
"\e[1;5*" => "*", # Ctrl-Arrow | |
"\e[200~" => "*" | |
), | |
# VT220 editing commands | |
AnyDict("\e[$(n)~" => "*" for n in 1:8), | |
# set mode commands | |
AnyDict("\e[$(c)h" => "*" for c in 1:20), | |
# reset mode commands | |
AnyDict("\e[$(c)l" => "*" for c in 1:20) | |
) | |
function setup_prefix_keymap(hp, parent_prompt) | |
p = PrefixHistoryPrompt(hp, parent_prompt) | |
p.keymap_dict = keymap([prefix_history_keymap]) | |
pkeymap = AnyDict( | |
# Up Arrow | |
"\e[A" => (s,o...)->(edit_move_up(s) || enter_prefix_search(s, p, true)), | |
# Down Arrow | |
"\e[B" => (s,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), | |
) | |
(p, pkeymap) | |
end | |
function deactivate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) | |
clear_input_area(termbuf, s) | |
s | |
end | |
function activate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) | |
s.ias = InputAreaState(0, 0) | |
refresh_line(s, termbuf) | |
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) | |
end | |
activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) = | |
activate(s.current_mode, s, termbuf, term) | |
commit_changes(t::UnixTerminal, termbuf) = write(t, takebuf_array(termbuf.out_stream)) | |
function transition(f::Function, s::MIState, mode) | |
if mode === :abort | |
s.aborted = true | |
return | |
end | |
if mode === :reset | |
reset_state(s) | |
return | |
end | |
if !haskey(s.mode_state,mode) | |
s.mode_state[mode] = init_state(terminal(s), mode) | |
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 | |
f() | |
activate(mode, s.mode_state[mode], termbuf, t) | |
commit_changes(t, termbuf) | |
end | |
transition(s::MIState, mode) = transition((args...)->nothing, s, mode) | |
function reset_state(s::PromptState) | |
if s.input_buffer.size != 0 | |
s.input_buffer.size = 0 | |
s.input_buffer.ptr = 1 | |
end | |
s.ias = InputAreaState(0, 0) | |
end | |
function reset_state(s::MIState) | |
for (mode,state) in s.mode_state | |
reset_state(state) | |
end | |
end | |
const default_keymap_dict = keymap([default_keymap, escape_defaults]) | |
function Prompt(prompt; | |
prompt_prefix = "", | |
prompt_suffix = "", | |
keymap_dict = default_keymap_dict, | |
keymap_func_data = nothing, | |
complete = EmptyCompletionProvider(), | |
on_enter = default_enter_cb, | |
on_done = ()->nothing, | |
hist = EmptyHistoryProvider(), | |
sticky = false) | |
Prompt(prompt, prompt_prefix, prompt_suffix, keymap_dict, keymap_func_data, | |
complete, on_enter, on_done, hist, sticky) | |
end | |
run_interface(::Prompt) = nothing | |
init_state(terminal, prompt::Prompt) = PromptState(terminal, prompt, IOBuffer(), InputAreaState(1, 1), #=indent(spaces)=#strwidth(prompt.prompt)) | |
function init_state(terminal, m::ModalInterface) | |
s = MIState(m, m.modes[1], false, Dict{Any,Any}()) | |
for mode in m.modes | |
s.mode_state[mode] = init_state(terminal, mode) | |
end | |
s | |
end | |
function run_interface(terminal, m::ModalInterface) | |
s::MIState = init_state(terminal, m) | |
while !s.aborted | |
p = s.current_mode | |
buf, ok, suspend = prompt!(terminal, m, s) | |
while suspend | |
@static if is_unix(); ccall(:jl_repl_raise_sigtstp, Cint, ()); end | |
buf, ok, suspend = prompt!(terminal, m, s) | |
end | |
mode(state(s, s.current_mode)).on_done(s, buf, ok) | |
end | |
end | |
buffer(s::PromptState) = s.input_buffer | |
buffer(s::SearchState) = s.query_buffer | |
buffer(s::PrefixSearchState) = s.response_buffer | |
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) | |
function prompt!(term, prompt, s = init_state(term, prompt)) | |
Base.reseteof(term) | |
raw!(term, true) | |
enable_bracketed_paste(term) | |
try | |
activate(prompt, s, term, term) | |
while true | |
map = keymap(s, prompt) | |
fcn = match_input(map, s) | |
# errors in keymaps shouldn't cause the REPL to fail, so wrap in a | |
# try/catch block | |
local state | |
try | |
state = fcn(s, keymap_data(s, prompt)) | |
catch e | |
warn("Caught an exception in the keymap:") | |
warn(e) | |
state = :done | |
end | |
if state === :abort | |
return buffer(s), false, false | |
elseif state === :done | |
return buffer(s), true, false | |
elseif state === :suspend | |
if is_unix() | |
return buffer(s), true, true | |
end | |
else | |
@assert state === :ok | |
end | |
end | |
finally | |
raw!(term, false) && disable_bracketed_paste(term) | |
end | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module REPL | |
using Base.Meta | |
using ..Terminals | |
using ..LineEdit | |
using ..REPLCompletions | |
export | |
BasicREPL, | |
LineEditREPL, | |
StreamREPL | |
import Base: | |
Display, | |
display, | |
show, | |
AnyDict, | |
== | |
import ..LineEdit: | |
CompletionProvider, | |
HistoryProvider, | |
add_history, | |
complete_line, | |
history_next, | |
history_next_prefix, | |
history_prev, | |
history_prev_prefix, | |
history_search, | |
accept_result, | |
terminal | |
abstract AbstractREPL | |
answer_color(::AbstractREPL) = "" | |
const JULIA_PROMPT = "julia> " | |
type REPLBackend | |
"channel for AST" | |
repl_channel::Channel | |
"channel for results: (value, nothing) or (error, backtrace)" | |
response_channel::Channel | |
"flag indicating the state of this backend" | |
in_eval::Bool | |
"current backend task" | |
backend_task::Task | |
REPLBackend(repl_channel, response_channel, in_eval) = | |
new(repl_channel, response_channel, in_eval) | |
end | |
function eval_user_input(ast::ANY, backend::REPLBackend) | |
iserr, lasterr = false, ((), nothing) | |
Base.sigatomic_begin() | |
while true | |
try | |
Base.sigatomic_end() | |
if iserr | |
put!(backend.response_channel, lasterr) | |
iserr, lasterr = false, () | |
else | |
backend.in_eval = true | |
value = eval(Main, ast) | |
backend.in_eval = false | |
# note: value wrapped in a closure to ensure it doesn't get passed through expand | |
eval(Main, Expr(:(=), :ans, Expr(:call, ()->value))) | |
put!(backend.response_channel, (value, nothing)) | |
end | |
break | |
catch err | |
if iserr | |
println("SYSTEM ERROR: Failed to report error to REPL frontend") | |
println(err) | |
end | |
iserr, lasterr = true, (err, catch_backtrace()) | |
end | |
end | |
Base.sigatomic_end() | |
end | |
function start_repl_backend(repl_channel::Channel, response_channel::Channel) | |
backend = REPLBackend(repl_channel, response_channel, false) | |
backend.backend_task = @schedule begin | |
# include looks at this to determine the relative include path | |
# nothing means cwd | |
while true | |
tls = task_local_storage() | |
tls[:SOURCE_PATH] = nothing | |
ast, show_value = take!(backend.repl_channel) | |
if show_value == -1 | |
# exit flag | |
break | |
end | |
eval_user_input(ast, backend) | |
end | |
end | |
backend | |
end | |
function ip_matches_func(ip, func::Symbol) | |
for fr in StackTraces.lookup(ip) | |
if fr === StackTraces.UNKNOWN || fr.from_c | |
return false | |
end | |
fr.func === func && return true | |
end | |
return false | |
end | |
function display_error(io::IO, er, bt) | |
Base.with_output_color(:red, io) do io | |
print(io, "ERROR: ") | |
# remove REPL-related frames from interactive printing | |
eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt) | |
if eval_ind != 0 | |
bt = bt[1:eval_ind-1] | |
end | |
Base.showerror(io, er, bt) | |
end | |
end | |
immutable REPLDisplay{R<:AbstractREPL} <: Display | |
repl::R | |
end | |
==(a::REPLDisplay, b::REPLDisplay) = a.repl === b.repl | |
function display(d::REPLDisplay, mime::MIME"text/plain", x) | |
io = outstream(d.repl) | |
Base.have_color && write(io, answer_color(d.repl)) | |
show(IOContext(io, :limit => true), mime, x) | |
println(io) | |
end | |
display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) | |
function print_response(repl::AbstractREPL, val::ANY, bt, show_value::Bool, have_color::Bool) | |
repl.waserror = bt !== nothing | |
print_response(outstream(repl), val, bt, show_value, have_color, specialdisplay(repl)) | |
end | |
function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::Bool, specialdisplay=nothing) | |
Base.sigatomic_begin() | |
while true | |
try | |
Base.sigatomic_end() | |
if bt !== nothing | |
display_error(errio, val, bt) | |
println(errio) | |
iserr, lasterr = false, () | |
else | |
if val !== nothing && show_value | |
try | |
if specialdisplay === nothing | |
display(val) | |
else | |
display(specialdisplay,val) | |
end | |
catch err | |
println(errio, "Error showing value of type ", typeof(val), ":") | |
rethrow(err) | |
end | |
end | |
end | |
break | |
catch err | |
if bt !== nothing | |
println(errio, "SYSTEM: show(lasterr) caused an error") | |
break | |
end | |
val = err | |
bt = catch_backtrace() | |
end | |
end | |
Base.sigatomic_end() | |
end | |
# A reference to a backend | |
immutable REPLBackendRef | |
repl_channel::Channel | |
response_channel::Channel | |
end | |
function run_repl(repl::AbstractREPL, consumer = x->nothing) | |
repl_channel = Channel(1) | |
response_channel = Channel(1) | |
backend = start_repl_backend(repl_channel, response_channel) | |
consumer(backend) | |
run_frontend(repl, REPLBackendRef(repl_channel,response_channel)) | |
return backend | |
end | |
## BasicREPL ## | |
type BasicREPL <: AbstractREPL | |
terminal::TextTerminal | |
waserror::Bool | |
BasicREPL(t) = new(t,false) | |
end | |
outstream(r::BasicREPL) = r.terminal | |
function run_frontend(repl::BasicREPL, backend::REPLBackendRef) | |
d = REPLDisplay(repl) | |
dopushdisplay = !in(d,Base.Multimedia.displays) | |
dopushdisplay && pushdisplay(d) | |
repl_channel, response_channel = backend.repl_channel, backend.response_channel | |
hit_eof = false | |
while true | |
Base.reseteof(repl.terminal) | |
write(repl.terminal, JULIA_PROMPT) | |
line = "" | |
ast = nothing | |
interrupted = false | |
while true | |
try | |
line *= readline(repl.terminal) | |
catch e | |
if isa(e,InterruptException) | |
try # raise the debugger if present | |
ccall(:jl_raise_debugger, Int, ()) | |
end | |
line = "" | |
interrupted = true | |
break | |
elseif isa(e,EOFError) | |
hit_eof = true | |
break | |
else | |
rethrow() | |
end | |
end | |
ast = Base.parse_input_line(line) | |
(isa(ast,Expr) && ast.head == :incomplete) || break | |
end | |
if !isempty(line) | |
put!(repl_channel, (ast, 1)) | |
val, bt = take!(response_channel) | |
if !ends_with_semicolon(line) | |
print_response(repl, val, bt, true, false) | |
end | |
end | |
write(repl.terminal, '\n') | |
((!interrupted && isempty(line)) || hit_eof) && break | |
end | |
# terminate backend | |
put!(repl_channel, (nothing, -1)) | |
dopushdisplay && popdisplay(d) | |
end | |
## LineEditREPL ## | |
type LineEditREPL <: AbstractREPL | |
t::TextTerminal | |
hascolor::Bool | |
prompt_color::String | |
input_color::String | |
answer_color::String | |
shell_color::String | |
help_color::String | |
history_file::Bool | |
in_shell::Bool | |
in_help::Bool | |
envcolors::Bool | |
waserror::Bool | |
specialdisplay | |
interface | |
backendref::REPLBackendRef | |
LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) = | |
new(t,true,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, | |
in_help,envcolors,false,nothing) | |
end | |
outstream(r::LineEditREPL) = r.t | |
specialdisplay(r::LineEditREPL) = r.specialdisplay | |
specialdisplay(r::AbstractREPL) = nothing | |
terminal(r::LineEditREPL) = r.t | |
LineEditREPL(t::TextTerminal, envcolors = false) = LineEditREPL(t, | |
true, | |
julia_green, | |
Base.input_color(), | |
Base.answer_color(), | |
Base.text_colors[:red], | |
Base.text_colors[:yellow], | |
false, false, false, envcolors) | |
type REPLCompletionProvider <: CompletionProvider | |
r::LineEditREPL | |
end | |
type ShellCompletionProvider <: CompletionProvider | |
r::LineEditREPL | |
end | |
immutable LatexCompletions <: CompletionProvider; end | |
beforecursor(buf::IOBuffer) = String(buf.data[1:buf.ptr-1]) | |
function complete_line(c::REPLCompletionProvider, s) | |
partial = beforecursor(s.input_buffer) | |
full = LineEdit.input_string(s) | |
ret, range, should_complete = completions(full, endof(partial)) | |
return ret, partial[range], should_complete | |
end | |
function complete_line(c::ShellCompletionProvider, s) | |
# First parse everything up to the current position | |
partial = beforecursor(s.input_buffer) | |
full = LineEdit.input_string(s) | |
ret, range, should_complete = shell_completions(full, endof(partial)) | |
return ret, partial[range], should_complete | |
end | |
function complete_line(c::LatexCompletions, s) | |
partial = beforecursor(LineEdit.buffer(s)) | |
full = LineEdit.input_string(s) | |
ret, range, should_complete = bslash_completions(full, endof(partial))[2] | |
return ret, partial[range], should_complete | |
end | |
type REPLHistoryProvider <: HistoryProvider | |
history::Array{String,1} | |
history_file | |
start_idx::Int | |
cur_idx::Int | |
last_idx::Int | |
last_buffer::IOBuffer | |
last_mode | |
mode_mapping | |
modes::Array{Symbol,1} | |
end | |
REPLHistoryProvider(mode_mapping) = | |
REPLHistoryProvider(String[], nothing, 0, 0, -1, IOBuffer(), | |
nothing, mode_mapping, UInt8[]) | |
invalid_history_message(path::String) = """ | |
Invalid history file ($path) format: | |
If you have a history file left over from an older version of Julia, | |
try renaming or deleting it. | |
Invalid character: """ | |
munged_history_message(path::String) = """ | |
Invalid history file ($path) format: | |
An editor may have converted tabs to spaces at line """ | |
function hist_getline(file) | |
while !eof(file) | |
line = readline(file) | |
isempty(line) && return line | |
line[1] in "\r\n" || return line | |
end | |
return "" | |
end | |
function hist_from_file(hp, file, path) | |
hp.history_file = file | |
seek(file, 0) | |
countlines = 0 | |
while true | |
mode = :julia | |
line = hist_getline(file) | |
isempty(line) && break | |
countlines += 1 | |
line[1] != '#' && | |
error(invalid_history_message(path), repr(line[1]), " at line ", countlines) | |
while !isempty(line) | |
m = match(r"^#\s*(\w+)\s*:\s*(.*?)\s*$", line) | |
m === nothing && break | |
if m.captures[1] == "mode" | |
mode = Symbol(m.captures[2]) | |
end | |
line = hist_getline(file) | |
countlines += 1 | |
end | |
isempty(line) && break | |
# Make sure starts with tab | |
line[1] == ' ' && | |
error(munged_history_message(path), countlines) | |
line[1] != '\t' && | |
error(invalid_history_message(path), repr(line[1]), " at line ", countlines) | |
lines = String[] | |
while !isempty(line) | |
push!(lines, chomp(line[2:end])) | |
eof(file) && break | |
ch = Char(Base.peek(file)) | |
ch == ' ' && error(munged_history_message(path), countlines) | |
ch != '\t' && break | |
line = hist_getline(file) | |
countlines += 1 | |
end | |
push!(hp.modes, mode) | |
push!(hp.history, join(lines, '\n')) | |
end | |
seekend(file) | |
hp.start_idx = length(hp.history) | |
hp | |
end | |
function mode_idx(hist::REPLHistoryProvider, mode) | |
c = :julia | |
for (k,v) in hist.mode_mapping | |
isequal(v, mode) && (c = k) | |
end | |
return c | |
end | |
function add_history(hist::REPLHistoryProvider, s) | |
str = rstrip(String(s.input_buffer)) | |
isempty(strip(str)) && return | |
mode = mode_idx(hist, LineEdit.mode(s)) | |
!isempty(hist.history) && | |
isequal(mode, hist.modes[end]) && str == hist.history[end] && return | |
push!(hist.modes, mode) | |
push!(hist.history, str) | |
hist.history_file === nothing && return | |
entry = """ | |
# time: $(Libc.strftime("%Y-%m-%d %H:%M:%S %Z", time())) | |
# mode: $mode | |
$(replace(str, r"^"ms, "\t")) | |
""" | |
# TODO: write-lock history file | |
seekend(hist.history_file) | |
print(hist.history_file, entry) | |
flush(hist.history_file) | |
end | |
function history_move(s::Union{LineEdit.MIState,LineEdit.PrefixSearchState}, hist::REPLHistoryProvider, idx::Int, save_idx::Int = hist.cur_idx) | |
max_idx = length(hist.history) + 1 | |
@assert 1 <= hist.cur_idx <= max_idx | |
(1 <= idx <= max_idx) || return :none | |
idx != hist.cur_idx || return :none | |
# save the current line | |
if save_idx == max_idx | |
hist.last_mode = LineEdit.mode(s) | |
hist.last_buffer = copy(LineEdit.buffer(s)) | |
else | |
hist.history[save_idx] = LineEdit.input_string(s) | |
hist.modes[save_idx] = mode_idx(hist, LineEdit.mode(s)) | |
end | |
# load the saved line | |
if idx == max_idx | |
last_buffer = hist.last_buffer | |
LineEdit.transition(s, hist.last_mode) do | |
LineEdit.replace_line(s, last_buffer) | |
end | |
hist.last_mode = nothing | |
hist.last_buffer = IOBuffer() | |
else | |
if haskey(hist.mode_mapping, hist.modes[idx]) | |
LineEdit.transition(s, hist.mode_mapping[hist.modes[idx]]) do | |
LineEdit.replace_line(s, hist.history[idx]) | |
end | |
else | |
return :skip | |
end | |
end | |
hist.cur_idx = idx | |
return :ok | |
end | |
# Modified version of accept_result that also transitions modes | |
function LineEdit.accept_result(s, p::LineEdit.HistoryPrompt{REPLHistoryProvider}) | |
parent = LineEdit.state(s, p).parent | |
hist = p.hp | |
if 1 <= hist.cur_idx <= length(hist.modes) | |
m = hist.mode_mapping[hist.modes[hist.cur_idx]] | |
LineEdit.transition(s, m) do | |
LineEdit.replace_line(LineEdit.state(s, m), LineEdit.state(s, p).response_buffer) | |
end | |
else | |
LineEdit.transition(s, parent) | |
end | |
end | |
function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider, | |
save_idx::Int = hist.cur_idx) | |
hist.last_idx = -1 | |
m = history_move(s, hist, hist.cur_idx-1, save_idx) | |
if m === :ok | |
LineEdit.move_input_start(s) | |
LineEdit.reset_key_repeats(s) do | |
LineEdit.move_line_end(s) | |
end | |
LineEdit.refresh_line(s) | |
elseif m === :skip | |
hist.cur_idx -= 1 | |
history_prev(s, hist, save_idx) | |
else | |
Terminals.beep(LineEdit.terminal(s)) | |
end | |
end | |
function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, | |
save_idx::Int = hist.cur_idx) | |
cur_idx = hist.cur_idx | |
max_idx = length(hist.history) + 1 | |
if cur_idx == max_idx && 0 < hist.last_idx | |
# issue #6312 | |
cur_idx = hist.last_idx | |
hist.last_idx = -1 | |
end | |
m = history_move(s, hist, cur_idx+1, save_idx) | |
if m === :ok | |
LineEdit.move_input_end(s) | |
LineEdit.refresh_line(s) | |
elseif m === :skip | |
hist.cur_idx += 1 | |
history_next(s, hist, save_idx) | |
else | |
Terminals.beep(LineEdit.terminal(s)) | |
end | |
end | |
function history_move_prefix(s::LineEdit.PrefixSearchState, | |
hist::REPLHistoryProvider, | |
prefix::AbstractString, | |
backwards::Bool, | |
cur_idx = hist.cur_idx) | |
cur_response = String(LineEdit.buffer(s)) | |
# when searching forward, start at last_idx | |
if !backwards && hist.last_idx > 0 | |
cur_idx = hist.last_idx | |
end | |
hist.last_idx = -1 | |
max_idx = length(hist.history)+1 | |
idxs = backwards ? ((cur_idx-1):-1:1) : ((cur_idx+1):max_idx) | |
for idx in idxs | |
if (idx == max_idx) || (startswith(hist.history[idx], prefix) && (hist.history[idx] != cur_response || hist.modes[idx] != LineEdit.mode(s))) | |
m = history_move(s, hist, idx) | |
if m === :ok | |
if idx == max_idx | |
# on resuming the in-progress edit, leave the cursor where the user last had it | |
elseif isempty(prefix) | |
# on empty prefix search, move cursor to the end | |
LineEdit.move_input_end(s) | |
else | |
# otherwise, keep cursor at the prefix position as a visual cue | |
seek(LineEdit.buffer(s), sizeof(prefix)) | |
end | |
LineEdit.refresh_line(s) | |
return :ok | |
elseif m === :skip | |
return history_move_prefix(s,hist,prefix,backwards,idx) | |
end | |
end | |
end | |
Terminals.beep(LineEdit.terminal(s)) | |
end | |
history_next_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = | |
history_move_prefix(s, hist, prefix, false) | |
history_prev_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = | |
history_move_prefix(s, hist, prefix, true) | |
function history_search(hist::REPLHistoryProvider, query_buffer::IOBuffer, response_buffer::IOBuffer, | |
backwards::Bool=false, skip_current::Bool=false) | |
qpos = position(query_buffer) | |
qpos > 0 || return true | |
searchdata = beforecursor(query_buffer) | |
response_str = String(response_buffer) | |
# Alright, first try to see if the current match still works | |
a = position(response_buffer) + 1 | |
b = min(endof(response_str), prevind(response_str, a + sizeof(searchdata))) | |
!skip_current && searchdata == response_str[a:b] && return true | |
searchfunc, searchstart, skipfunc = backwards ? (rsearch, b, prevind) : | |
(search, a, nextind) | |
skip_current && (searchstart = skipfunc(response_str, searchstart)) | |
# Start searching | |
# First the current response buffer | |
if 1 <= searchstart <= endof(response_str) | |
match = searchfunc(response_str, searchdata, searchstart) | |
if match != 0:-1 | |
seek(response_buffer, prevind(response_str, first(match))) | |
return true | |
end | |
end | |
# Now search all the other buffers | |
idxs = backwards ? ((hist.cur_idx-1):-1:1) : ((hist.cur_idx+1):length(hist.history)) | |
for idx in idxs | |
h = hist.history[idx] | |
match = searchfunc(h, searchdata) | |
if match != 0:-1 && h != response_str && haskey(hist.mode_mapping, hist.modes[idx]) | |
truncate(response_buffer, 0) | |
write(response_buffer, h) | |
seek(response_buffer, prevind(h, first(match))) | |
hist.cur_idx = idx | |
return true | |
end | |
end | |
return false | |
end | |
function history_reset_state(hist::REPLHistoryProvider) | |
if hist.cur_idx != length(hist.history) + 1 | |
hist.last_idx = hist.cur_idx | |
hist.cur_idx = length(hist.history) + 1 | |
end | |
end | |
LineEdit.reset_state(hist::REPLHistoryProvider) = history_reset_state(hist) | |
const julia_green = "\033[1m\033[32m" | |
function return_callback(s) | |
ast = Base.syntax_deprecation_warnings(false) do | |
Base.parse_input_line(String(LineEdit.buffer(s))) | |
end | |
if !isa(ast, Expr) || (ast.head != :continue && ast.head != :incomplete) | |
return true | |
else | |
return false | |
end | |
end | |
function find_hist_file() | |
filename = ".julia_history" | |
if isfile(filename) | |
return filename | |
elseif haskey(ENV, "JULIA_HISTORY") | |
return ENV["JULIA_HISTORY"] | |
else | |
return joinpath(homedir(), filename) | |
end | |
end | |
backend(r::AbstractREPL) = r.backendref | |
send_to_backend(ast, backend::REPLBackendRef) = send_to_backend(ast, backend.repl_channel, backend.response_channel) | |
function send_to_backend(ast, req, rep) | |
put!(req, (ast, 1)) | |
val, bt = take!(rep) | |
end | |
function respond(f, repl, main; pass_empty = false) | |
(s,buf,ok)->begin | |
if !ok | |
return transition(s, :abort) | |
end | |
line = takebuf_string(buf) | |
if !isempty(line) || pass_empty | |
reset(repl) | |
val, bt = send_to_backend(f(line), backend(repl)) | |
if !ends_with_semicolon(line) || bt !== nothing | |
print_response(repl, val, bt, true, Base.have_color) | |
end | |
end | |
prepare_next(repl) | |
reset_state(s) | |
s.current_mode.sticky || transition(s, main) | |
end | |
end | |
function reset(repl::LineEditREPL) | |
raw!(repl.t, false) | |
print(repl.t,Base.text_colors[:normal]) | |
end | |
function prepare_next(repl::LineEditREPL) | |
println(terminal(repl)) | |
end | |
function mode_keymap(julia_prompt) | |
AnyDict( | |
'\b' => function (s,o...) | |
if isempty(s) || position(LineEdit.buffer(s)) == 0 | |
buf = copy(LineEdit.buffer(s)) | |
transition(s, julia_prompt) do | |
LineEdit.state(s, julia_prompt).input_buffer = buf | |
end | |
else | |
LineEdit.edit_backspace(s) | |
end | |
end, | |
"^C" => function (s,o...) | |
LineEdit.move_input_end(s) | |
LineEdit.refresh_line(s) | |
print(LineEdit.terminal(s), "^C\n\n") | |
transition(s, julia_prompt) | |
transition(s, :reset) | |
LineEdit.refresh_line(s) | |
end) | |
end | |
repl_filename(repl, hp::REPLHistoryProvider) = "REPL[$(length(hp.history)-hp.start_idx)]" | |
repl_filename(repl, hp) = "REPL" | |
const JL_PROMT_PASTE = Ref(true) | |
enable_promtpaste(v::Bool) = JL_PROMT_PASTE[] = v | |
function setup_interface(repl::LineEditREPL; hascolor = repl.hascolor, extra_repl_keymap = Dict{Any,Any}[]) | |
### | |
# | |
# This function returns the main interface that describes the REPL | |
# functionality, it is called internally by functions that setup a | |
# Terminal-based REPL frontend, but if you want to customize your REPL | |
# or embed the REPL in another interface, you may call this function | |
# directly and append it to your interface. | |
# | |
# Usage: | |
# | |
# repl_channel,response_channel = Channel(),Channel() | |
# start_repl_backend(repl_channel, response_channel) | |
# setup_interface(REPLDisplay(t),repl_channel,response_channel) | |
# | |
### | |
### | |
# We setup the interface in two stages. | |
# First, we set up all components (prompt,rsearch,shell,help) | |
# Second, we create keymaps with appropriate transitions between them | |
# and assign them to the components | |
# | |
### | |
############################### Stage I ################################ | |
# This will provide completions for REPL and help mode | |
replc = REPLCompletionProvider(repl) | |
# Set up the main Julia prompt | |
julia_prompt = Prompt(JULIA_PROMPT; | |
# Copy colors from the prompt object | |
prompt_prefix = hascolor ? repl.prompt_color : "", | |
prompt_suffix = hascolor ? | |
(repl.envcolors ? Base.input_color : repl.input_color) : "", | |
keymap_func_data = repl, | |
complete = replc, | |
on_enter = return_callback) | |
# Setup help mode | |
help_mode = Prompt("help?> ", | |
prompt_prefix = hascolor ? repl.help_color : "", | |
prompt_suffix = hascolor ? | |
(repl.envcolors ? Base.input_color : repl.input_color) : "", | |
keymap_func_data = repl, | |
complete = replc, | |
# When we're done transform the entered line into a call to help("$line") | |
on_done = respond(Docs.helpmode, repl, julia_prompt)) | |
# Set up shell mode | |
shell_mode = Prompt("shell> "; | |
prompt_prefix = hascolor ? repl.shell_color : "", | |
prompt_suffix = hascolor ? | |
(repl.envcolors ? Base.input_color : repl.input_color) : "", | |
keymap_func_data = repl, | |
complete = ShellCompletionProvider(repl), | |
# Transform "foo bar baz" into `foo bar baz` (shell quoting) | |
# and pass into Base.repl_cmd for processing (handles `ls` and `cd` | |
# special) | |
on_done = respond(repl, julia_prompt) do line | |
Expr(:call, :(Base.repl_cmd), macroexpand(Expr(:macrocall, Symbol("@cmd"),line)), outstream(repl)) | |
end) | |
################################# Stage II ############################# | |
# Setup history | |
# We will have a unified history for all REPL modes | |
hp = REPLHistoryProvider(Dict{Symbol,Any}(:julia => julia_prompt, | |
:shell => shell_mode, | |
:help => help_mode)) | |
if repl.history_file | |
try | |
hist_path = find_hist_file() | |
f = open(hist_path, true, true, true, false, false) | |
finalizer(replc, replc->close(f)) | |
hist_from_file(hp, f, hist_path) | |
catch e | |
print_response(repl, e, catch_backtrace(), true, Base.have_color) | |
println(outstream(repl)) | |
info("Disabling history file for this session.") | |
repl.history_file = false | |
end | |
end | |
history_reset_state(hp) | |
julia_prompt.hist = hp | |
shell_mode.hist = hp | |
help_mode.hist = hp | |
julia_prompt.on_done = respond(x->Base.parse_input_line(x,filename=repl_filename(repl,hp)), repl, julia_prompt) | |
search_prompt, skeymap = LineEdit.setup_search_keymap(hp) | |
search_prompt.complete = LatexCompletions() | |
# Canonicalize user keymap input | |
if isa(extra_repl_keymap, Dict) | |
extra_repl_keymap = [extra_repl_keymap] | |
end | |
const repl_keymap = AnyDict( | |
';' => function (s,o...) | |
if isempty(s) || position(LineEdit.buffer(s)) == 0 | |
buf = copy(LineEdit.buffer(s)) | |
transition(s, shell_mode) do | |
LineEdit.state(s, shell_mode).input_buffer = buf | |
end | |
else | |
edit_insert(s, ';') | |
end | |
end, | |
'?' => function (s,o...) | |
if isempty(s) || position(LineEdit.buffer(s)) == 0 | |
buf = copy(LineEdit.buffer(s)) | |
transition(s, help_mode) do | |
LineEdit.state(s, help_mode).input_buffer = buf | |
end | |
else | |
edit_insert(s, '?') | |
end | |
end, | |
# Bracketed Paste Mode | |
"\e[200~" => (s,o...)->begin | |
input = LineEdit.bracketed_paste(s) # read directly from s until reaching the end-bracketed-paste marker | |
sbuffer = LineEdit.buffer(s) | |
curspos = position(sbuffer) | |
seek(sbuffer, 0) | |
shouldeval = (nb_available(sbuffer) == curspos && search(sbuffer, UInt8('\n')) == 0) | |
seek(sbuffer, curspos) | |
if curspos == 0 | |
# if pasting at the beginning, strip leading whitespace | |
input = lstrip(input) | |
end | |
if !shouldeval | |
# when pasting in the middle of input, just paste in place | |
# don't try to execute all the WIP, since that's rather confusing | |
# and is often ill-defined how it should behave | |
edit_insert(s, input) | |
return | |
end | |
edit_insert(sbuffer, input) | |
input = takebuf_string(sbuffer) | |
oldpos = start(input) | |
firstline = true | |
isprompt_paste = false | |
while !done(input, oldpos) # loop until all lines have been executed | |
if JL_PROMT_PASTE[] | |
# Check if the next statement starts with "julia> ", in that case | |
# skip it. But first skip whitespace | |
while input[oldpos] in ('\n', ' ', '\t') | |
oldpos = nextind(input, oldpos) | |
oldpos >= sizeof(input) && return | |
end | |
# Check if input line starts with "julia> ", remove it if we are in prompt paste mode | |
jl_prompt_len = 7 | |
if (firstline || isprompt_paste) && (oldpos + jl_prompt_len <= sizeof(input) && input[oldpos:oldpos+jl_prompt_len-1] == JULIA_PROMPT) | |
isprompt_paste = true | |
oldpos += jl_prompt_len | |
# If we are prompt pasting and current statement does not begin with julia> , skip to next line | |
elseif isprompt_paste | |
while input[oldpos] != '\n' | |
oldpos = nextind(input, oldpos) | |
oldpos >= sizeof(input) && return | |
end | |
continue | |
end | |
end | |
ast, pos = Base.syntax_deprecation_warnings(false) do | |
Base.parse(input, oldpos, raise=false) | |
end | |
if (isa(ast, Expr) && (ast.head == :error || ast.head == :continue || ast.head == :incomplete)) || | |
(done(input, pos) && !endswith(input, '\n')) | |
# remaining text is incomplete (an error, or parser ran to the end but didn't stop with a newline): | |
# Insert all the remaining text as one line (might be empty) | |
tail = input[oldpos:end] | |
if !firstline | |
# strip leading whitespace, but only if it was the result of executing something | |
# (avoids modifying the user's current leading wip line) | |
tail = lstrip(tail) | |
end | |
LineEdit.replace_line(s, tail) | |
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) | |
# put the line on the screen and history | |
LineEdit.replace_line(s, line) | |
LineEdit.commit_line(s) | |
# execute the statement | |
terminal = LineEdit.terminal(s) # This is slightly ugly but ok for now | |
raw!(terminal, false) && disable_bracketed_paste(terminal) | |
LineEdit.mode(s).on_done(s, LineEdit.buffer(s), true) | |
raw!(terminal, true) && enable_bracketed_paste(terminal) | |
end | |
oldpos = pos | |
firstline = false | |
end | |
end, | |
) | |
prefix_prompt, prefix_keymap = LineEdit.setup_prefix_keymap(hp, julia_prompt) | |
a = Dict{Any,Any}[skeymap, repl_keymap, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] | |
prepend!(a, extra_repl_keymap) | |
julia_prompt.keymap_dict = LineEdit.keymap(a) | |
mk = mode_keymap(julia_prompt) | |
b = Dict{Any,Any}[skeymap, mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] | |
prepend!(b, extra_repl_keymap) | |
shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) | |
ModalInterface([julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt]) | |
end | |
function run_frontend(repl::LineEditREPL, backend) | |
d = REPLDisplay(repl) | |
dopushdisplay = repl.specialdisplay === nothing && !in(d,Base.Multimedia.displays) | |
dopushdisplay && pushdisplay(d) | |
if !isdefined(repl,:interface) | |
interface = repl.interface = setup_interface(repl) | |
else | |
interface = repl.interface | |
end | |
repl.backendref = backend | |
run_interface(repl.t, interface) | |
dopushdisplay && popdisplay(d) | |
end | |
if isdefined(Base, :banner_color) | |
banner(io, t) = banner(io, hascolor(t)) | |
banner(io, x::Bool) = print(io, x ? Base.banner_color : Base.banner_plain) | |
else | |
banner(io,t) = Base.banner(io) | |
end | |
## StreamREPL ## | |
type StreamREPL <: AbstractREPL | |
stream::IO | |
prompt_color::String | |
input_color::String | |
answer_color::String | |
waserror::Bool | |
StreamREPL(stream,pc,ic,ac) = new(stream,pc,ic,ac,false) | |
end | |
StreamREPL(stream::IO) = StreamREPL(stream, julia_green, Base.input_color(), Base.answer_color()) | |
run_repl(stream::IO) = run_repl(StreamREPL(stream)) | |
outstream(s::StreamREPL) = s.stream | |
answer_color(r::LineEditREPL) = r.envcolors ? Base.answer_color() : r.answer_color | |
answer_color(r::StreamREPL) = r.answer_color | |
input_color(r::LineEditREPL) = r.envcolors ? Base.input_color() : r.input_color | |
input_color(r::StreamREPL) = r.input_color | |
function ends_with_semicolon(line) | |
match = rsearch(line, ';') | |
if match != 0 | |
for c in line[(match+1):end] | |
isspace(c) || return c == '#' | |
end | |
return true | |
end | |
return false | |
end | |
function run_frontend(repl::StreamREPL, backend::REPLBackendRef) | |
have_color = Base.have_color | |
banner(repl.stream, have_color) | |
d = REPLDisplay(repl) | |
dopushdisplay = !in(d,Base.Multimedia.displays) | |
dopushdisplay && pushdisplay(d) | |
repl_channel, response_channel = backend.repl_channel, backend.response_channel | |
while !eof(repl.stream) | |
if have_color | |
print(repl.stream,repl.prompt_color) | |
end | |
print(repl.stream, "julia> ") | |
if have_color | |
print(repl.stream, input_color(repl)) | |
end | |
line = readline(repl.stream) | |
if !isempty(line) | |
ast = Base.parse_input_line(line) | |
if have_color | |
print(repl.stream, Base.color_normal) | |
end | |
put!(repl_channel, (ast, 1)) | |
val, bt = take!(response_channel) | |
if !ends_with_semicolon(line) | |
print_response(repl, val, bt, true, have_color) | |
end | |
end | |
end | |
# Terminate Backend | |
put!(repl_channel, (nothing, -1)) | |
dopushdisplay && popdisplay(d) | |
end | |
function start_repl_server(port) | |
listen(port) do server, status | |
client = accept(server) | |
run_repl(client) | |
end | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module REPLCompletions | |
export completions, shell_completions, bslash_completions | |
using Base.Meta | |
function completes_global(x, name) | |
return startswith(x, name) && !('#' in x) | |
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] | |
filter!(x->completes_global(x, name), syms) | |
end | |
# REPL Symbol Completions | |
function complete_symbol(sym, ffunc) | |
# Maybe be smarter in the future | |
context_module = Main | |
mod = context_module | |
name = sym | |
lookup_module = true | |
t = Union{} | |
if rsearch(sym, non_identifier_chars) < rsearch(sym, '.') | |
# Find module | |
lookup_name, name = rsplit(sym, ".", limit=2) | |
ex = Base.syntax_deprecation_warnings(false) do | |
parse(lookup_name, raise=false) | |
end | |
b, found = get_value(ex, context_module) | |
if found | |
if isa(b, Module) | |
mod = b | |
lookup_module = true | |
elseif Base.isstructtype(typeof(b)) | |
lookup_module = false | |
t = typeof(b) | |
end | |
else # If the value is not found using get_value, the expression contain an advanced expression | |
lookup_module = false | |
t, found = get_type(ex, context_module) | |
end | |
found || return String[] | |
# Ensure REPLCompletion do not crash when asked to complete a tuple, #15329 | |
!lookup_module && t <: Tuple && return String[] | |
end | |
suggestions = String[] | |
if lookup_module | |
# We will exclude the results that the user does not want, as well | |
# as excluding Main.Main.Main, etc., because that's most likely not what | |
# the user wants | |
p = s->(!Base.isdeprecated(mod, s) && s != module_name(mod) && ffunc(mod, s)) | |
# Looking for a binding in a module | |
if mod == context_module | |
# Also look in modules we got through `using` | |
mods = ccall(:jl_module_usings, Any, (Any,), Main) | |
for m in mods | |
append!(suggestions, filtered_mod_names(p, m, name)) | |
end | |
append!(suggestions, filtered_mod_names(p, mod, name, true, true)) | |
else | |
append!(suggestions, filtered_mod_names(p, mod, name, true, false)) | |
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) | |
end | |
end | |
end | |
suggestions | |
end | |
function complete_keyword(s::String) | |
const sorted_keywords = [ | |
"abstract", "baremodule", "begin", "bitstype", "break", "catch", "ccall", | |
"const", "continue", "do", "else", "elseif", "end", "export", "false", | |
"finally", "for", "function", "global", "if", "immutable", "import", | |
"importall", "let", "local", "macro", "module", "quote", "return", | |
"true", "try", "type", "typealias", "using", "while"] | |
r = searchsorted(sorted_keywords, s) | |
i = first(r) | |
n = length(sorted_keywords) | |
while i <= n && startswith(sorted_keywords[i],s) | |
r = first(r):i | |
i += 1 | |
end | |
sorted_keywords[r] | |
end | |
function complete_path(path::AbstractString, pos; use_envpath=false) | |
if Base.is_unix() && ismatch(r"^~(?:/|$)", path) | |
# if the path is just "~", don't consider the expanded username as a prefix | |
if path == "~" | |
dir, prefix = homedir(), "" | |
else | |
dir, prefix = splitdir(homedir() * path[2:end]) | |
end | |
else | |
dir, prefix = splitdir(path) | |
end | |
local files | |
try | |
if isempty(dir) | |
files = readdir() | |
elseif isdir(dir) | |
files = readdir(dir) | |
else | |
return String[], 0:-1, false | |
end | |
catch | |
return String[], 0:-1, false | |
end | |
matches = Set{String}() | |
for file in files | |
if startswith(file, prefix) | |
id = try isdir(joinpath(dir, file)) catch; false end | |
# joinpath is not used because windows needs to complete with double-backslash | |
push!(matches, id ? file * (@static is_windows() ? "\\\\" : "/") : file) | |
end | |
end | |
if use_envpath && length(dir) == 0 | |
# Look for files in PATH as well | |
local pathdirs = split(ENV["PATH"], @static is_windows() ? ";" : ":") | |
for pathdir in pathdirs | |
local actualpath | |
try | |
actualpath = realpath(pathdir) | |
catch | |
# Bash doesn't expect every folder in PATH to exist, so neither shall we | |
continue | |
end | |
if actualpath != pathdir && in(actualpath,pathdirs) | |
# Remove paths which (after resolving links) are in the env path twice. | |
# Many distros eg. point /bin to /usr/bin but have both in the env path. | |
continue | |
end | |
local filesinpath | |
try | |
filesinpath = readdir(pathdir) | |
catch e | |
# Bash allows dirs in PATH that can't be read, so we should as well. | |
if isa(e, SystemError) | |
continue | |
else | |
# We only handle SystemErrors here | |
rethrow(e) | |
end | |
end | |
for file in filesinpath | |
# In a perfect world, we would filter on whether the file is executable | |
# here, or even on whether the current user can execute the file in question. | |
if startswith(file, prefix) && isfile(joinpath(pathdir, file)) | |
push!(matches, file) | |
end | |
end | |
end | |
end | |
matchList = String[replace(s, r"\s", "\\ ") for s in matches] | |
startpos = pos - endof(prefix) + 1 - length(matchall(r" ", prefix)) | |
# The pos - endof(prefix) + 1 is correct due to `endof(prefix)-endof(prefix)==0`, | |
# hence we need to add one to get the first index. This is also correct when considering | |
# pos, because pos is the `endof` a larger string which `endswith(path)==true`. | |
return matchList, startpos:pos, !isempty(matchList) | |
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) | |
method_complete = false | |
for c in reverse(s) | |
if c in [',', '('] | |
method_complete = true | |
break | |
elseif !(c in whitespace_chars) | |
method_complete = false | |
break | |
end | |
end | |
method_complete | |
end | |
# Returns a range that includes the method name in front of the first non | |
# closed start brace from the end of the string. | |
function find_start_brace(s::AbstractString; c_start='(', c_end=')') | |
braces = 0 | |
r = RevString(s) | |
i = start(r) | |
in_single_quotes = false | |
in_double_quotes = false | |
in_back_ticks = false | |
while !done(r, i) | |
c, i = next(r, i) | |
if !in_single_quotes && !in_double_quotes && !in_back_ticks | |
if c == c_start | |
braces += 1 | |
elseif c == c_end | |
braces -= 1 | |
elseif c == '\'' | |
in_single_quotes = true | |
elseif c == '"' | |
in_double_quotes = true | |
elseif c == '`' | |
in_back_ticks = true | |
end | |
else | |
if !in_back_ticks && !in_double_quotes && c == '\'' && !done(r, i) && next(r, i)[1]!='\\' | |
in_single_quotes = !in_single_quotes | |
elseif !in_back_ticks && !in_single_quotes && c == '"' && !done(r, i) && next(r, i)[1]!='\\' | |
in_double_quotes = !in_double_quotes | |
elseif !in_single_quotes && !in_double_quotes && c == '`' && !done(r, i) && next(r, i)[1]!='\\' | |
in_back_ticks = !in_back_ticks | |
end | |
end | |
braces == 1 && break | |
end | |
braces != 1 && return 0:-1, -1 | |
method_name_end = reverseind(r, i) | |
startind = nextind(s, rsearch(s, non_identifier_chars, method_name_end)) | |
return (startind:endof(s), method_name_end) | |
end | |
# Returns the value in a expression if sym is defined in current namespace fn. | |
# This method is used to iterate to the value of a expression like: | |
# :(Base.REPLCompletions.whitespace_chars) a `dump` of this expression | |
# will show it consist of Expr, QuoteNode's and Symbol's which all needs to | |
# be handled differently to iterate down to get the value of whitespace_chars. | |
function get_value(sym::Expr, fn) | |
sym.head != :. && return (nothing, false) | |
for ex in sym.args | |
fn, found = get_value(ex, fn) | |
!found && return (nothing, false) | |
end | |
return (fn, true) | |
end | |
get_value(sym::Symbol, fn) = isdefined(fn, sym) ? (getfield(fn, sym), true) : (nothing, false) | |
get_value(sym::QuoteNode, fn) = isdefined(fn, sym.value) ? (getfield(fn, sym.value), true) : (nothing, false) | |
get_value(sym, fn) = (sym, true) | |
# Return the value of a getfield call expression | |
function get_value_getfield(ex::Expr, fn) | |
# Example :((top(getfield))(Base,:max)) | |
val, found = get_value_getfield(ex.args[2],fn) #Look up Base in Main and returns the module | |
found || return (nothing, false) | |
return get_value_getfield(ex.args[3], val) #Look up max in Base and returns the function if found. | |
end | |
get_value_getfield(sym, fn) = get_value(sym, fn) | |
# Determines the return type with Base.return_types of a function call using the type information of the arguments. | |
function get_type_call(expr::Expr) | |
f_name = expr.args[1] | |
# The if statement should find the f function. How f is found depends on how f is referenced | |
if isa(f_name, GlobalRef) && isconst(f_name.mod,f_name.name) && isdefined(f_name.mod,f_name.name) | |
ft = typeof(eval(f_name)) | |
found = true | |
else | |
ft, found = get_type(f_name, Main) | |
end | |
found || return (Any, false) # If the function f is not found return Any. | |
args = Any[] | |
for ex in expr.args[2:end] # Find the type of the function arguments | |
typ, found = get_type(ex, Main) | |
found ? push!(args, typ) : push!(args, Any) | |
end | |
# use _methods_by_ftype as the function is supplied as a type | |
mt = Base._methods_by_ftype(Tuple{ft, args...}, -1) | |
length(mt) == 1 || return (Any, false) | |
m = first(mt) | |
# Typeinference | |
params = Core.Inference.InferenceParams() | |
return_type = Core.Inference.typeinf_type(m[3], m[1], m[2], true, params) | |
return_type === nothing && return (Any, false) | |
return (return_type, true) | |
end | |
# Returns the return type. example: get_type(:(Base.strip("",' ')),Main) returns (String,true) | |
function get_type(sym::Expr, fn) | |
sym=expand(sym) | |
val, found = get_value(sym, fn) | |
found && return Base.typesof(val).parameters[1], found | |
if sym.head === :call | |
# getfield call is special cased as the evaluation of getfield provides good type information, | |
# is inexpensive and it is also performed in the complete_symbol function. | |
a1 = sym.args[1] | |
if isa(a1,GlobalRef) && isconst(a1.mod,a1.name) && isdefined(a1.mod,a1.name) && | |
eval(a1) === Core.getfield | |
val, found = get_value_getfield(sym, Main) | |
return found ? Base.typesof(val).parameters[1] : Any, found | |
end | |
return get_type_call(sym) | |
end | |
return (Any, false) | |
end | |
function get_type(sym, fn) | |
val, found = get_value(sym, fn) | |
return found ? Base.typesof(val).parameters[1] : Any, found | |
end | |
# Method completion on function call expression that look like :(max(1)) | |
function complete_methods(ex_org::Expr) | |
args_ex = DataType[] | |
func, found = get_value(ex_org.args[1], Main) | |
!found && return String[] | |
for ex in ex_org.args[2:end] | |
val, found = get_type(ex, Main) | |
push!(args_ex, val) | |
end | |
out = String[] | |
t_in = Tuple{Core.Typeof(func), args_ex...} # Input types | |
na = length(args_ex)+1 | |
ml = methods(func) | |
kwtype = isdefined(ml.mt, :kwsorter) ? Nullable{DataType}(typeof(ml.mt.kwsorter)) : Nullable{DataType}() | |
io = IOBuffer() | |
for method in ml | |
# Check if the method's type signature intersects the input types | |
if typeintersect(Tuple{method.sig.parameters[1 : min(na, end)]...}, t_in) != Union{} | |
show(io, method, kwtype=kwtype) | |
push!(out, takebuf_string(io)) | |
end | |
end | |
return out | |
end | |
include("latex_symbols.jl") | |
include("emoji_symbols.jl") | |
const non_identifier_chars = [" \t\n\r\"\\'`\$><=:;|&{}()[],+-*/?%^~"...] | |
const whitespace_chars = [" \t\n\r"...] | |
# "\"'`"... is added to whitespace_chars as non of the bslash_completions | |
# characters contain any of these characters. It prohibits the | |
# bslash_completions function to try and complete on escaped characters in strings | |
const bslash_separators = [whitespace_chars..., "\"'`"...] | |
# Aux function to detect whether we're right after a | |
# using or import keyword | |
function afterusing(string::String, startpos::Int) | |
(isempty(string) || startpos == 0) && return false | |
str = string[1:prevind(string,startpos)] | |
isempty(str) && return false | |
rstr = reverse(str) | |
r = search(rstr, r"\s(gnisu|tropmi)\b") | |
isempty(r) && return false | |
fr = reverseind(str, last(r)) | |
return ismatch(r"^\b(using|import)\s*(\w+\s*,\s*)*\w*$", str[fr:end]) | |
end | |
function bslash_completions(string, pos) | |
slashpos = rsearch(string, '\\', pos) | |
if (rsearch(string, bslash_separators, pos) < slashpos && | |
!(1 < slashpos && (string[prevind(string, slashpos)]=='\\'))) | |
# latex / emoji symbol substitution | |
s = string[slashpos:pos] | |
latex = get(latex_symbols, s, "") | |
if !isempty(latex) # complete an exact match | |
return (true, ([latex], slashpos:pos, true)) | |
end | |
emoji = get(emoji_symbols, s, "") | |
if !isempty(emoji) | |
return (true, ([emoji], slashpos:pos, true)) | |
end | |
# return possible matches; these cannot be mixed with regular | |
# Julian completions as only latex / emoji symbols contain the leading \ | |
if startswith(s, "\\:") # emoji | |
emoji_names = Iterators.filter(k -> startswith(k, s), keys(emoji_symbols)) | |
return (true, (sort!(collect(emoji_names)), slashpos:pos, true)) | |
else # latex | |
latex_names = Iterators.filter(k -> startswith(k, s), keys(latex_symbols)) | |
return (true, (sort!(collect(latex_names)), slashpos:pos, true)) | |
end | |
end | |
return (false, (String[], 0:-1, false)) | |
end | |
function dict_identifier_key(str,tag) | |
if tag === :string | |
str_close = str*"\"" | |
elseif tag === :cmd | |
str_close = str*"`" | |
else | |
str_close = str | |
end | |
frange, end_of_indentifier = find_start_brace(str_close, c_start='[', c_end=']') | |
isempty(frange) && return (nothing, nothing, nothing) | |
obj = Main | |
for name in split(str[frange[1]:end_of_indentifier], '.') | |
Base.isidentifier(name) || return (nothing, nothing, nothing) | |
sym = Symbol(name) | |
isdefined(obj, sym) || return (nothing, nothing, nothing) | |
obj = getfield(obj, sym) | |
# Avoid `isdefined(::Array, ::Symbol)` | |
isa(obj, Array) && return (nothing, nothing, nothing) | |
end | |
begin_of_key = findnext(x->!in(x,whitespace_chars), str, end_of_indentifier+2) | |
begin_of_key==0 && return (true, nothing, nothing) | |
partial_key = str[begin_of_key:end] | |
(isa(obj, Associative) && length(obj) < 1e6) || return (true, nothing, nothing) | |
return (obj, partial_key, begin_of_key) | |
end | |
function completions(string, pos) | |
# First parse everything up to the current position | |
partial = string[1:pos] | |
inc_tag = Base.syntax_deprecation_warnings(false) do | |
Base.incomplete_tag(parse(partial, raise=false)) | |
end | |
# if completing a key in a Dict | |
identifier, partial_key, loc = dict_identifier_key(partial,inc_tag) | |
if identifier !== nothing | |
if partial_key !== nothing | |
matches = [] | |
for key in keys(identifier) | |
rkey = repr(key) | |
startswith(rkey,partial_key) && push!(matches,rkey) | |
end | |
length(matches)==1 && (length(string) <= pos || string[pos+1] != ']') && (matches[1]*="]") | |
length(matches)>0 && return sort(matches), loc:pos, true | |
else | |
return String[], 0:-1, false | |
end | |
end | |
# otherwise... | |
if inc_tag in [:cmd, :string] | |
m = match(r"[\t\n\r\"'`@\$><=;|&\{]| (?!\\)", reverse(partial)) | |
startpos = nextind(partial, reverseind(partial, m.offset)) | |
r = startpos:pos | |
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 | |
ok, ret = bslash_completions(string, pos) | |
ok && return ret | |
# Make sure that only bslash_completions is working on strings | |
inc_tag==:string && return String[], 0:-1, false | |
if inc_tag == :other && should_method_complete(partial) | |
frange, method_name_end = find_start_brace(partial) | |
ex = Base.syntax_deprecation_warnings(false) do | |
parse(partial[frange] * ")", raise=false) | |
end | |
if isa(ex, Expr) && ex.head==:call | |
return complete_methods(ex), start(frange):method_name_end, false | |
end | |
elseif inc_tag == :comment | |
return String[], 0:-1, false | |
end | |
dotpos = rsearch(string, '.', pos) | |
startpos = nextind(string, rsearch(string, non_identifier_chars, pos)) | |
ffunc = (mod,x)->true | |
suggestions = String[] | |
comp_keywords = true | |
if afterusing(string, startpos) | |
# We're right after using or import. Let's look only for packages | |
# and modules we can reach from here | |
# If there's no dot, we're in toplevel, so we should | |
# also search for packages | |
s = string[startpos:pos] | |
if dotpos <= startpos | |
for dir in [Pkg.dir(); LOAD_PATH; pwd()] | |
isdir(dir) || continue | |
for pname in readdir(dir) | |
if pname[1] != '.' && pname != "METADATA" && | |
pname != "REQUIRE" && startswith(pname, s) | |
# Valid file paths are | |
# <Mod>.jl | |
# <Mod>/src/<Mod>.jl | |
# <Mod>.jl/src/<Mod>.jl | |
if isfile(joinpath(dir, pname)) | |
endswith(pname, ".jl") && push!(suggestions, pname[1:end-3]) | |
else | |
mod_name = if endswith(pname, ".jl") | |
pname[1:end - 3] | |
else | |
pname | |
end | |
if isfile(joinpath(dir, pname, "src", | |
"$mod_name.jl")) | |
push!(suggestions, mod_name) | |
end | |
end | |
end | |
end | |
end | |
end | |
ffunc = (mod,x)->(isdefined(mod, x) && isa(getfield(mod, x), Module)) | |
comp_keywords = false | |
end | |
startpos == 0 && (pos = -1) | |
dotpos < startpos && (dotpos = startpos - 1) | |
s = string[startpos:pos] | |
comp_keywords && append!(suggestions, complete_keyword(s)) | |
# The case where dot and start pos is equal could look like: "(""*"").d","". or CompletionFoo.test_y_array[1].y | |
# This case can be handled by finding the begining of the expresion. This is done bellow. | |
if dotpos == startpos | |
i = prevind(string, startpos) | |
while 0 < i | |
c = string[i] | |
if c in [')', ']'] | |
if c==')' | |
c_start='('; c_end=')' | |
elseif c==']' | |
c_start='['; c_end=']' | |
end | |
frange, end_of_indentifier = find_start_brace(string[1:prevind(string, i)], c_start=c_start, c_end=c_end) | |
startpos = start(frange) | |
i = prevind(string, startpos) | |
elseif c in ["\'\"\`"...] | |
s = "$c$c"*string[startpos:pos] | |
break | |
else | |
break | |
end | |
s = string[startpos:pos] | |
end | |
end | |
append!(suggestions, complete_symbol(s, ffunc)) | |
return sort(unique(suggestions)), (dotpos+1):pos, true | |
end | |
function shell_completions(string, pos) | |
# First parse everything up to the current position | |
scs = string[1:pos] | |
local args, last_parse | |
try | |
args, last_parse = Base.shell_parse(scs, true) | |
catch | |
return String[], 0:-1, false | |
end | |
# Now look at the last thing we parsed | |
isempty(args.args[end].args) && return String[], 0:-1, false | |
arg = args.args[end].args[end] | |
if all(s -> isa(s, AbstractString), args.args[end].args) | |
# Treat this as a path | |
# As Base.shell_parse throws away trailing spaces (unless they are escaped), | |
# we need to special case here. | |
# If the last char was a space, but shell_parse ignored it search on "". | |
ignore_last_word = arg != " " && scs[end] == ' ' | |
prefix = ignore_last_word ? "" : join(args.args[end].args) | |
# Also try looking into the env path if the user wants to complete the first argument | |
use_envpath = !ignore_last_word && length(args.args) < 2 | |
return complete_path(prefix, pos, use_envpath=use_envpath) | |
elseif isexpr(arg, :escape) && (isexpr(arg.args[1], :incomplete) || isexpr(arg.args[1], :error)) | |
r = first(last_parse):prevind(last_parse, last(last_parse)) | |
partial = scs[r] | |
ret, range = completions(partial, endof(partial)) | |
range += first(r) - 1 | |
return ret, range, true | |
end | |
return String[], 0:-1, false | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Terminals | |
export | |
TextTerminal, | |
UnixTerminal, | |
TerminalBuffer, | |
TTYTerminal, | |
cmove, | |
cmove_col, | |
cmove_down, | |
cmove_left, | |
cmove_line_down, | |
cmove_line_up, | |
cmove_right, | |
cmove_up, | |
disable_bracketed_paste, | |
enable_bracketed_paste, | |
end_keypad_transmit_mode, | |
getX, | |
getY, | |
hascolor, | |
pos, | |
raw! | |
import Base: | |
check_open, # stream.jl | |
displaysize, | |
flush, | |
pipe_reader, | |
pipe_writer, | |
read, | |
readuntil | |
## TextTerminal ## | |
abstract TextTerminal <: Base.AbstractPipe | |
# INTERFACE | |
pipe_reader(::TextTerminal) = error("Unimplemented") | |
pipe_writer(::TextTerminal) = error("Unimplemented") | |
displaysize(::TextTerminal) = error("Unimplemented") | |
cmove(t::TextTerminal, x, y) = error("Unimplemented") | |
getX(t::TextTerminal) = error("Unimplemented") | |
getY(t::TextTerminal) = error("Unimplemented") | |
pos(t::TextTerminal) = (getX(t), getY(t)) | |
# Relative moves (Absolute position fallbacks) | |
cmove_up(t::TextTerminal, n) = cmove(getX(t), max(1, getY(t)-n)) | |
cmove_up(t) = cmove_up(t, 1) | |
cmove_down(t::TextTerminal, n) = cmove(getX(t), max(height(t), getY(t)+n)) | |
cmove_down(t) = cmove_down(t, 1) | |
cmove_left(t::TextTerminal, n) = cmove(max(1, getX(t)-n), getY(t)) | |
cmove_left(t) = cmove_left(t, 1) | |
cmove_right(t::TextTerminal, n) = cmove(max(width(t), getX(t)+n), getY(t)) | |
cmove_right(t) = cmove_right(t, 1) | |
cmove_line_up(t::TextTerminal, n) = cmove(1, max(1, getY(t)-n)) | |
cmove_line_up(t) = cmove_line_up(t, 1) | |
cmove_line_down(t::TextTerminal, n) = cmove(1, max(height(t), getY(t)+n)) | |
cmove_line_down(t) = cmove_line_down(t, 1) | |
cmove_col(t::TextTerminal, c) = cmove(c, getY(t)) | |
# Defaults | |
hascolor(::TextTerminal) = false | |
# Utility Functions | |
width(t::TextTerminal) = displaysize(t)[2] | |
height(t::TextTerminal) = displaysize(t)[1] | |
# For terminals with buffers | |
flush(t::TextTerminal) = nothing | |
clear(t::TextTerminal) = error("Unimplemented") | |
clear_line(t::TextTerminal, row) = error("Unimplemented") | |
clear_line(t::TextTerminal) = error("Unimplemented") | |
raw!(t::TextTerminal, raw::Bool) = error("Unimplemented") | |
beep(t::TextTerminal) = nothing | |
enable_bracketed_paste(t::TextTerminal) = nothing | |
disable_bracketed_paste(t::TextTerminal) = nothing | |
## UnixTerminal ## | |
abstract UnixTerminal <: TextTerminal | |
pipe_reader(t::UnixTerminal) = t.in_stream | |
pipe_writer(t::UnixTerminal) = t.out_stream | |
type TerminalBuffer <: UnixTerminal | |
out_stream::Base.IO | |
end | |
type TTYTerminal <: UnixTerminal | |
term_type::String | |
in_stream::Base.TTY | |
out_stream::Base.TTY | |
err_stream::Base.TTY | |
end | |
const CSI = "\x1b[" | |
cmove_up(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)A") | |
cmove_down(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)B") | |
cmove_right(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)C") | |
cmove_left(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)D") | |
cmove_line_up(t::UnixTerminal, n) = (cmove_up(t, n); cmove_col(t, 0)) | |
cmove_line_down(t::UnixTerminal, n) = (cmove_down(t, n); cmove_col(t, 0)) | |
cmove_col(t::UnixTerminal, n) = write(t.out_stream, "$(CSI)$(n)G") | |
if is_windows() | |
function raw!(t::TTYTerminal,raw::Bool) | |
check_open(t.in_stream) | |
if Base.ispty(t.in_stream) | |
run(if raw | |
`stty raw -echo onlcr -ocrnl opost` | |
else | |
`stty sane` | |
end,t.in_stream,t.out_stream,t.err_stream) | |
true | |
else | |
ccall(:jl_tty_set_mode, | |
Int32, (Ptr{Void},Int32), | |
t.in_stream.handle, raw) != -1 | |
end | |
end | |
else | |
function raw!(t::TTYTerminal, raw::Bool) | |
check_open(t.in_stream) | |
ccall(:jl_tty_set_mode, Int32, (Ptr{Void},Int32), t.in_stream.handle, raw) != -1 | |
end | |
end | |
enable_bracketed_paste(t::UnixTerminal) = write(t.out_stream, "$(CSI)?2004h") | |
disable_bracketed_paste(t::UnixTerminal) = write(t.out_stream, "$(CSI)?2004l") | |
end_keypad_transmit_mode(t::UnixTerminal) = # tput rmkx | |
write(t.out_stream, "$(CSI)?1l\x1b>") | |
function Base.displaysize(t::UnixTerminal) | |
return displaysize(t.out_stream) | |
end | |
clear(t::UnixTerminal) = write(t.out_stream, "\x1b[H\x1b[2J") | |
clear_line(t::UnixTerminal) = write(t.out_stream, "\x1b[0G\x1b[0K") | |
#beep(t::UnixTerminal) = write(t.err_stream,"\x7") | |
if is_windows() | |
hascolor(t::TTYTerminal) = true | |
else | |
function hascolor(t::TTYTerminal) | |
startswith(t.term_type, "xterm") && return true | |
try | |
return success(`tput setaf 0`) | |
catch | |
return false | |
end | |
end | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## Basic functions ## | |
""" | |
size(A::AbstractArray, [dim...]) | |
Returns a tuple containing the dimensions of `A`. Optionally you can specify the | |
dimension(s) you want the length of, and get the length of that dimension, or a tuple of the | |
lengths of dimensions you asked for. | |
```jldoctest | |
julia> A = ones(2,3,4); | |
julia> size(A, 2) | |
3 | |
julia> size(A,3,2) | |
(4,3) | |
``` | |
""" | |
size{T,N}(t::AbstractArray{T,N}, d) = d <= N ? size(t)[d] : 1 | |
size{N}(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(x, d1), size(x, d2), ntuple(k->size(x, dx[k]), Val{N})...) | |
""" | |
indices(A, d) | |
Returns the valid range of indices for array `A` along dimension `d`. | |
```jldoctest | |
julia> A = ones(5,6,7); | |
julia> indices(A,2) | |
Base.OneTo(6) | |
``` | |
""" | |
function indices{T,N}(A::AbstractArray{T,N}, d) | |
@_inline_meta | |
d <= N ? indices(A)[d] : OneTo(1) | |
end | |
""" | |
indices(A) | |
Returns the tuple of valid indices for array `A`. | |
```jldoctest | |
julia> A = ones(5,6,7); | |
julia> indices(A) | |
(Base.OneTo(5),Base.OneTo(6),Base.OneTo(7)) | |
``` | |
""" | |
function indices(A) | |
@_inline_meta | |
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. | |
indices1{T}(A::AbstractArray{T,0}) = OneTo(1) | |
indices1{T}(A::AbstractArray{T}) = (@_inline_meta; indices(A)[1]) | |
indices1(iter) = OneTo(length(iter)) | |
unsafe_indices(A) = indices(A) | |
unsafe_indices(r::Range) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size | |
""" | |
linearindices(A) | |
Returns a `UnitRange` specifying the valid range of indices for `A[i]` | |
where `i` is an `Int`. For arrays with conventional indexing (indices | |
start at 1), or any multidimensional array, this is `1:length(A)`; | |
however, for one-dimensional arrays with unconventional indices, this | |
is `indices(A, 1)`. | |
Calling this function is the "safe" way to write algorithms that | |
exploit linear indexing. | |
```jldoctest | |
julia> A = ones(5,6,7); | |
julia> b = linearindices(A); | |
julia> extrema(b) | |
(1,210) | |
``` | |
""" | |
linearindices(A) = (@_inline_meta; OneTo(_length(A))) | |
linearindices(A::AbstractVector) = (@_inline_meta; indices1(A)) | |
eltype{T}(::Type{AbstractArray{T}}) = T | |
eltype{T,N}(::Type{AbstractArray{T,N}}) = T | |
elsize{T}(::AbstractArray{T}) = sizeof(T) | |
""" | |
ndims(A::AbstractArray) -> Integer | |
Returns the number of dimensions of `A`. | |
```jldoctest | |
julia> A = ones(3,4,5); | |
julia> ndims(A) | |
3 | |
``` | |
""" | |
ndims{T,N}(::AbstractArray{T,N}) = N | |
ndims{T,N}(::Type{AbstractArray{T,N}}) = N | |
ndims{T<:AbstractArray}(::Type{T}) = ndims(supertype(T)) | |
""" | |
length(A::AbstractArray) -> Integer | |
Returns the number of elements in `A`. | |
```jldoctest | |
julia> A = ones(3,4,5); | |
julia> length(A) | |
60 | |
``` | |
""" | |
length(t::AbstractArray) = prod(size(t)) | |
_length(A::AbstractArray) = prod(map(unsafe_length, indices(A))) # circumvent missing size | |
_length(A) = length(A) | |
endof(a::AbstractArray) = length(a) | |
first(a::AbstractArray) = a[first(eachindex(a))] | |
function first(itr) | |
state = start(itr) | |
done(itr, state) && throw(ArgumentError("collection must be non-empty")) | |
next(itr, state)[1] | |
end | |
last(a) = a[end] | |
""" | |
stride(A, k::Integer) | |
Returns the distance in memory (in number of elements) between adjacent elements in dimension `k`. | |
```jldoctest | |
julia> A = ones(3,4,5); | |
julia> stride(A,2) | |
3 | |
julia> stride(A,3) | |
12 | |
``` | |
""" | |
function stride(a::AbstractArray, i::Integer) | |
if i > ndims(a) | |
return length(a) | |
end | |
s = 1 | |
for n=1:(i-1) | |
s *= size(a, n) | |
end | |
return s | |
end | |
strides{T}(A::AbstractArray{T,0}) = () | |
""" | |
strides(A) | |
Returns a tuple of the memory strides in each dimension. | |
```jldoctest | |
julia> A = ones(3,4,5); | |
julia> strides(A) | |
(1,3,12) | |
``` | |
""" | |
strides(A::AbstractArray) = _strides((1,), A) | |
_strides{T,N}(out::NTuple{N}, A::AbstractArray{T,N}) = out | |
function _strides{M,T,N}(out::NTuple{M}, A::AbstractArray{T,N}) | |
@_inline_meta | |
_strides((out..., out[M]*size(A, M)), A) | |
end | |
function isassigned(a::AbstractArray, i::Int...) | |
try | |
a[i...] | |
true | |
catch e | |
if isa(e, BoundsError) || isa(e, UndefRefError) | |
return false | |
else | |
rethrow(e) | |
end | |
end | |
end | |
# used to compute "end" for last index | |
function trailingsize(A::AbstractArray, n) | |
s = 1 | |
for i=n:ndims(A) | |
s *= size(A,i) | |
end | |
return s | |
end | |
function trailingsize(inds::Indices, n) | |
s = 1 | |
for i=n:length(inds) | |
s *= unsafe_length(inds[i]) | |
end | |
return s | |
end | |
# This version is type-stable even if inds is heterogeneous | |
function trailingsize(inds::Indices) | |
@_inline_meta | |
prod(map(unsafe_length, inds)) | |
end | |
## Traits for array types ## | |
abstract LinearIndexing | |
immutable LinearFast <: LinearIndexing end | |
immutable LinearSlow <: LinearIndexing end | |
""" | |
Base.linearindexing(A) | |
`linearindexing` defines how an AbstractArray most efficiently accesses its elements. If | |
`Base.linearindexing(A)` returns `Base.LinearFast()`, this means that linear indexing with | |
only one index is an efficient operation. If it instead returns `Base.LinearSlow()` (by | |
default), this means that the array intrinsically accesses its elements with indices | |
specified for every dimension. Since converting a linear index to multiple indexing | |
subscripts is typically very expensive, this provides a traits-based mechanism to enable | |
efficient generic code for all array types. | |
An abstract array subtype `MyArray` that wishes to opt into fast linear indexing behaviors | |
should define `linearindexing` in the type-domain: | |
Base.linearindexing{T<:MyArray}(::Type{T}) = Base.LinearFast() | |
""" | |
linearindexing(A::AbstractArray) = linearindexing(typeof(A)) | |
linearindexing{T<:AbstractArray}(::Type{T}) = LinearSlow() | |
linearindexing{T<:Array}(::Type{T}) = LinearFast() | |
linearindexing{T<:Range}(::Type{T}) = LinearFast() | |
linearindexing(A::AbstractArray, B::AbstractArray) = linearindexing(linearindexing(A), linearindexing(B)) | |
linearindexing(A::AbstractArray, B::AbstractArray...) = linearindexing(linearindexing(A), linearindexing(B...)) | |
linearindexing(::LinearFast, ::LinearFast) = LinearFast() | |
linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() | |
## Bounds checking ## | |
# The overall hierarchy is | |
# `checkbounds(A, I...)` -> | |
# `checkbounds(Bool, A, I...)` -> | |
# `checkbounds_indices(Bool, IA, I)`, which recursively calls | |
# `checkindex` for each dimension | |
# | |
# See the "boundscheck" devdocs for more information. | |
# | |
# Note this hierarchy has been designed to reduce the likelihood of | |
# method ambiguities. We try to make `checkbounds` the place to | |
# specialize on array type, and try to avoid specializations on index | |
# types; conversely, `checkindex` is intended to be specialized only | |
# on index type (especially, its last argument). | |
""" | |
checkbounds(Bool, A, I...) | |
Return `true` if the specified indices `I` are in bounds for the given | |
array `A`. Subtypes of `AbstractArray` should specialize this method | |
if they need to provide custom bounds checking behaviors; however, in | |
many cases one can rely on `A`'s indices and [`checkindex`](:func:`checkindex`). | |
See also [`checkindex`](:func:`checkindex`). | |
""" | |
function checkbounds(::Type{Bool}, A::AbstractArray, I...) | |
@_inline_meta | |
checkbounds_indices(Bool, indices(A), I) | |
end | |
# As a special extension, allow using logical arrays that match the source array exactly | |
function checkbounds{_,N}(::Type{Bool}, A::AbstractArray{_,N}, I::AbstractArray{Bool,N}) | |
@_inline_meta | |
indices(A) == indices(I) | |
end | |
""" | |
checkbounds(A, I...) | |
Throw an error if the specified indices `I` are not in bounds for the given array `A`. | |
""" | |
function checkbounds(A::AbstractArray, I...) | |
@_inline_meta | |
checkbounds(Bool, A, I...) || throw_boundserror(A, I) | |
nothing | |
end | |
checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case | |
""" | |
checkbounds_indices(Bool, IA, I) | |
Return `true` if the "requested" indices in the tuple `I` fall within | |
the bounds of the "permitted" indices specified by the tuple | |
`IA`. This function recursively consumes elements of these tuples, | |
usually in a 1-for-1 fashion, | |
checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & | |
checkbounds_indices(Bool, IA, I) | |
Note that [`checkindex`](:func:`checkindex`) is being used to perform the actual | |
bounds-check for a single dimension of the array. | |
There are two important exceptions to the 1-1 rule: linear indexing and | |
CartesianIndex{N}, both of which may "consume" more than one element | |
of `IA`. | |
""" | |
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 | |
checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1])) | |
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 | |
checkindex(Bool, OneTo(trailingsize(IA)), I[1]) # linear indexing | |
end | |
checkbounds_indices(::Type{Bool}, ::Tuple, ::Tuple{}) = true | |
throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) | |
# check along a single dimension | |
""" | |
checkindex(Bool, inds::AbstractUnitRange, index) | |
Return `true` if the given `index` is within the bounds of | |
`inds`. Custom types that would like to behave as indices for all | |
arrays can extend this method in order to provide a specialized bounds | |
checking implementation. | |
```jldoctest | |
julia> checkindex(Bool,1:20,8) | |
true | |
julia> checkindex(Bool,1:20,21) | |
false | |
``` | |
""" | |
checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) | |
checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) | |
checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true | |
function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::Range) | |
@_propagate_inbounds_meta | |
isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) | |
end | |
checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == indices1(I) | |
# Logical indexing is an exception to the "output dimensionality is the sum of | |
# the dimensionality of the indices" rule; `A[A.<0]` always returns a vector, | |
# regardless of the dimensionalities of `A and `A.<0`. This method goes one step | |
# further and ignores singleton dimensions for logical mask indices. While a | |
# little strange, it enables idioms like `A[:, sum(A, 1) .< 0]`. Ref #18271. | |
function checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool}) | |
# Ensure that there's no more than one non-singleton dimension and that it | |
# matches the source array's index. Note that there's an ambiguity at length | |
# 1, since we cannot tell which dimension should be the non-singleton one. | |
@_inline_meta | |
length(indx) == prod(map(length, indices(I))) && any(x->x==indx, indices(I)) | |
end | |
function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) | |
@_inline_meta | |
b = true | |
for i in I | |
b &= checkindex(Bool, inds, i) | |
end | |
b | |
end | |
# See also specializations in multidimensional | |
## Constructors ## | |
# default arguments to similar() | |
""" | |
similar(array, [element_type=eltype(array)], [dims=size(array)]) | |
Create an uninitialized mutable array with the given element type and size, based upon the | |
given source array. The second and third arguments are both optional, defaulting to the | |
given array's `eltype` and `size`. The dimensions may be specified either as a single tuple | |
argument or as a series of integer arguments. | |
Custom AbstractArray subtypes may choose which specific array type is best-suited to return | |
for the given element type and dimensionality. If they do not specialize this method, the | |
default is an `Array{element_type}(dims...)`. | |
For example, `similar(1:10, 1, 4)` returns an uninitialized `Array{Int,2}` since ranges are | |
neither mutable nor support 2 dimensions: | |
julia> similar(1:10, 1, 4) | |
1×4 Array{Int64,2}: | |
4419743872 4374413872 4419743888 0 | |
Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two | |
elements since `BitArray`s are both mutable and can support 1-dimensional arrays: | |
julia> similar(trues(10,10), 2) | |
2-element BitArray{1}: | |
false | |
false | |
Since `BitArray`s can only store elements of type `Bool`, however, if you request a | |
different element type it will create a regular `Array` instead: | |
julia> similar(falses(10), Float64, 2, 4) | |
2×4 Array{Float64,2}: | |
2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 | |
2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 | |
""" | |
similar{T}(a::AbstractArray{T}) = similar(a, T) | |
similar{T}(a::AbstractArray, ::Type{T}) = similar(a, T, to_shape(indices(a))) | |
similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims)) | |
similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims)) | |
similar{T}(a::AbstractArray, ::Type{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims)) | |
similar{T}(a::AbstractArray, ::Type{T}, dims::NeedsShaping) = similar(a, T, to_shape(dims)) | |
# similar creates an Array by default | |
similar{T,N}(a::AbstractArray, ::Type{T}, dims::Dims{N}) = Array{T,N}(dims) | |
to_shape(::Tuple{}) = () | |
to_shape(dims::Dims) = dims | |
to_shape(dims::DimsOrInds) = map(to_shape, dims) | |
# each dimension | |
to_shape(i::Int) = i | |
to_shape(i::Integer) = Int(i) | |
to_shape(r::OneTo) = Int(last(r)) | |
to_shape(r::AbstractUnitRange) = r | |
""" | |
similar(storagetype, indices) | |
Create an uninitialized mutable array analogous to that specified by | |
`storagetype`, but with `indices` specified by the last | |
argument. `storagetype` might be a type or a function. | |
**Examples**: | |
similar(Array{Int}, indices(A)) | |
creates an array that "acts like" an `Array{Int}` (and might indeed be | |
backed by one), but which is indexed identically to `A`. If `A` has | |
conventional indexing, this will be identical to | |
`Array{Int}(size(A))`, but if `A` has unconventional indexing then the | |
indices of the result will match `A`. | |
similar(BitArray, (indices(A, 2),)) | |
would create a 1-dimensional logical array whose indices match those | |
of the columns of `A`. | |
similar(dims->zeros(Int, dims), indices(A)) | |
would create an array of `Int`, initialized to zero, matching the | |
indices of `A`. | |
""" | |
similar(f, shape::Tuple) = f(to_shape(shape)) | |
similar(f, dims::DimOrInd...) = similar(f, dims) | |
## from general iterable to any array | |
function copy!(dest::AbstractArray, src) | |
destiter = eachindex(dest) | |
state = start(destiter) | |
for x in src | |
i, state = next(destiter, state) | |
dest[i] = x | |
end | |
return dest | |
end | |
function copy!(dest::AbstractArray, dstart::Integer, src) | |
i = Int(dstart) | |
for x in src | |
dest[i] = x | |
i += 1 | |
end | |
return dest | |
end | |
# copy from an some iterable object into an AbstractArray | |
function copy!(dest::AbstractArray, dstart::Integer, src, sstart::Integer) | |
if (sstart < 1) | |
throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) | |
end | |
st = start(src) | |
for j = 1:(sstart-1) | |
if done(src, st) | |
throw(ArgumentError(string("source has fewer elements than required, ", | |
"expected at least ",sstart,", got ",j-1))) | |
end | |
_, st = next(src, st) | |
end | |
dn = done(src, st) | |
if dn | |
throw(ArgumentError(string("source has fewer elements than required, ", | |
"expected at least ",sstart,", got ",sstart-1))) | |
end | |
i = Int(dstart) | |
while !dn | |
val, st = next(src, st) | |
dest[i] = val | |
i += 1 | |
dn = done(src, st) | |
end | |
return dest | |
end | |
# this method must be separate from the above since src might not have a length | |
function copy!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer) | |
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) | |
n == 0 && return dest | |
dmax = dstart + n - 1 | |
inds = linearindices(dest) | |
if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1) | |
sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) | |
throw(BoundsError(dest, dstart:dmax)) | |
end | |
st = start(src) | |
for j = 1:(sstart-1) | |
if done(src, st) | |
throw(ArgumentError(string("source has fewer elements than required, ", | |
"expected at least ",sstart,", got ",j-1))) | |
end | |
_, st = next(src, st) | |
end | |
i = Int(dstart) | |
while i <= dmax && !done(src, st) | |
val, st = next(src, st) | |
@inbounds dest[i] = val | |
i += 1 | |
end | |
i <= dmax && throw(BoundsError(dest, i)) | |
return dest | |
end | |
## copy between abstract arrays - generally more efficient | |
## since a single index variable can be used. | |
copy!(dest::AbstractArray, src::AbstractArray) = | |
copy!(linearindexing(dest), dest, linearindexing(src), src) | |
function copy!(::LinearIndexing, dest::AbstractArray, ::LinearIndexing, src::AbstractArray) | |
destinds, srcinds = linearindices(dest), linearindices(src) | |
isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) | |
@inbounds for i in srcinds | |
dest[i] = src[i] | |
end | |
return dest | |
end | |
function copy!(::LinearIndexing, dest::AbstractArray, ::LinearSlow, src::AbstractArray) | |
destinds, srcinds = linearindices(dest), linearindices(src) | |
isempty(srcinds) || (first(srcinds) ∈ destinds && last(srcinds) ∈ destinds) || throw(BoundsError(dest, srcinds)) | |
i = 0 | |
@inbounds for a in src | |
dest[i+=1] = a | |
end | |
return dest | |
end | |
function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray) | |
copy!(dest, dstart, src, first(linearindices(src)), _length(src)) | |
end | |
function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) | |
srcinds = linearindices(src) | |
sstart ∈ srcinds || throw(BoundsError(src, sstart)) | |
copy!(dest, dstart, src, sstart, last(srcinds)-sstart+1) | |
end | |
function copy!(dest::AbstractArray, dstart::Integer, | |
src::AbstractArray, sstart::Integer, | |
n::Integer) | |
n == 0 && return dest | |
n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) | |
destinds, srcinds = linearindices(dest), linearindices(src) | |
(dstart ∈ destinds && dstart+n-1 ∈ destinds) || throw(BoundsError(dest, dstart:dstart+n-1)) | |
(sstart ∈ srcinds && sstart+n-1 ∈ srcinds) || throw(BoundsError(src, sstart:sstart+n-1)) | |
@inbounds for i = 0:(n-1) | |
dest[dstart+i] = src[sstart+i] | |
end | |
return dest | |
end | |
function copy(a::AbstractArray) | |
@_propagate_inbounds_meta | |
copymutable(a) | |
end | |
function copy!{R,S}(B::AbstractVecOrMat{R}, ir_dest::Range{Int}, jr_dest::Range{Int}, | |
A::AbstractVecOrMat{S}, ir_src::Range{Int}, jr_src::Range{Int}) | |
if length(ir_dest) != length(ir_src) | |
throw(ArgumentError(string("source and destination must have same size (got ", | |
length(ir_src)," and ",length(ir_dest),")"))) | |
end | |
if length(jr_dest) != length(jr_src) | |
throw(ArgumentError(string("source and destination must have same size (got ", | |
length(jr_src)," and ",length(jr_dest),")"))) | |
end | |
@boundscheck checkbounds(B, ir_dest, jr_dest) | |
@boundscheck checkbounds(A, ir_src, jr_src) | |
jdest = first(jr_dest) | |
for jsrc in jr_src | |
idest = first(ir_dest) | |
for isrc in ir_src | |
B[idest,jdest] = A[isrc,jsrc] | |
idest += step(ir_dest) | |
end | |
jdest += step(jr_dest) | |
end | |
return B | |
end | |
function copy_transpose!{R,S}(B::AbstractVecOrMat{R}, ir_dest::Range{Int}, jr_dest::Range{Int}, | |
A::AbstractVecOrMat{S}, ir_src::Range{Int}, jr_src::Range{Int}) | |
if length(ir_dest) != length(jr_src) | |
throw(ArgumentError(string("source and destination must have same size (got ", | |
length(jr_src)," and ",length(ir_dest),")"))) | |
end | |
if length(jr_dest) != length(ir_src) | |
throw(ArgumentError(string("source and destination must have same size (got ", | |
length(ir_src)," and ",length(jr_dest),")"))) | |
end | |
@boundscheck checkbounds(B, ir_dest, jr_dest) | |
@boundscheck checkbounds(A, ir_src, jr_src) | |
idest = first(ir_dest) | |
for jsrc in jr_src | |
jdest = first(jr_dest) | |
for isrc in ir_src | |
B[idest,jdest] = A[isrc,jsrc] | |
jdest += step(jr_dest) | |
end | |
idest += step(ir_dest) | |
end | |
return B | |
end | |
""" | |
copymutable(a) | |
Make a mutable copy of an array or iterable `a`. For `a::Array`, | |
this is equivalent to `copy(a)`, but for other array types it may | |
differ depending on the type of `similar(a)`. For generic iterables | |
this is equivalent to `collect(a)`. | |
""" | |
function copymutable(a::AbstractArray) | |
@_propagate_inbounds_meta | |
copy!(similar(a), a) | |
end | |
copymutable(itr) = collect(itr) | |
zero{T}(x::AbstractArray{T}) = fill!(similar(x), zero(T)) | |
## iteration support for arrays by iterating over `eachindex` in the array ## | |
# Allows fast iteration by default for both LinearFast and LinearSlow arrays | |
# While the definitions for LinearFast are all simple enough to inline on their | |
# own, LinearSlow's CartesianRange is more complicated and requires explicit | |
# inlining. | |
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 iterates over all indices. LinearSlow definitions are later. | |
eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A)) | |
""" | |
eachindex(A...) | |
Creates an iterable object for visiting each index of an AbstractArray `A` in an efficient | |
manner. For array types that have opted into fast linear indexing (like `Array`), this is | |
simply the range `1:length(A)`. For other array types, this returns a specialized Cartesian | |
range to efficiently index into the array with indices specified for every dimension. For | |
other iterables, including strings and dictionaries, this returns an iterator object | |
supporting arbitrary index types (e.g. unevenly spaced or non-integer indices). | |
Example for a sparse 2-d array: | |
```jldoctest | |
julia> A = sparse([1, 1, 2], [1, 3, 1], [1, 2, -5]) | |
2×3 sparse matrix with 3 Int64 nonzero entries: | |
[1, 1] = 1 | |
[2, 1] = -5 | |
[1, 3] = 2 | |
julia> for iter in eachindex(A) | |
@show iter.I[1], iter.I[2] | |
@show A[iter] | |
end | |
(iter.I[1],iter.I[2]) = (1,1) | |
A[iter] = 1 | |
(iter.I[1],iter.I[2]) = (2,1) | |
A[iter] = -5 | |
(iter.I[1],iter.I[2]) = (1,2) | |
A[iter] = 0 | |
(iter.I[1],iter.I[2]) = (2,2) | |
A[iter] = 0 | |
(iter.I[1],iter.I[2]) = (1,3) | |
A[iter] = 2 | |
(iter.I[1],iter.I[2]) = (2,3) | |
A[iter] = 0 | |
``` | |
If you supply more than one `AbstractArray` argument, `eachindex` will create an | |
iterable object that is fast for all arguments (a [`UnitRange`](:obj:`UnitRange`) | |
if all inputs have fast linear indexing, a [`CartesianRange`](:obj:`CartesianRange`) | |
otherwise). | |
If the arrays have different sizes and/or dimensionalities, `eachindex` returns an | |
iterable that spans the largest range along each dimension. | |
""" | |
eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(linearindexing(A), A)) | |
function eachindex(A::AbstractArray, B::AbstractArray) | |
@_inline_meta | |
eachindex(linearindexing(A,B), A, B) | |
end | |
function eachindex(A::AbstractArray, B::AbstractArray...) | |
@_inline_meta | |
eachindex(linearindexing(A,B...), A, B...) | |
end | |
eachindex(::LinearFast, A::AbstractArray) = linearindices(A) | |
function eachindex(::LinearFast, A::AbstractArray, B::AbstractArray...) | |
@_inline_meta | |
1:_maxlength(A, B...) | |
end | |
_maxlength(A) = length(A) | |
function _maxlength(A, B, C...) | |
@_inline_meta | |
max(length(A), _maxlength(B, C...)) | |
end | |
isempty(a::AbstractArray) = (_length(a) == 0) | |
## Conversions ## | |
convert{T,N }(::Type{AbstractArray{T,N}}, A::AbstractArray{T,N}) = A | |
convert{T,S,N}(::Type{AbstractArray{T,N}}, A::AbstractArray{S,N}) = copy!(similar(A,T), A) | |
convert{T,S,N}(::Type{AbstractArray{T }}, A::AbstractArray{S,N}) = convert(AbstractArray{T,N}, A) | |
convert{T,N}(::Type{Array}, A::AbstractArray{T,N}) = convert(Array{T,N}, A) | |
""" | |
of_indices(x, y) | |
Represents the array `y` as an array having the same indices type as `x`. | |
""" | |
of_indices(x, y) = similar(dims->y, oftype(indices(x), indices(y))) | |
full(x::AbstractArray) = x | |
## range conversions ## | |
map{T<:Real}(::Type{T}, r::StepRange) = T(r.start):T(r.step):T(last(r)) | |
map{T<:Real}(::Type{T}, r::UnitRange) = T(r.start):T(last(r)) | |
map{T<:AbstractFloat}(::Type{T}, r::FloatRange) = FloatRange(T(r.start), T(r.step), r.len, T(r.divisor)) | |
function map{T<:AbstractFloat}(::Type{T}, r::LinSpace) | |
new_len = T(r.len) | |
new_len == r.len || error("$r: too long for $T") | |
LinSpace(T(r.start), T(r.stop), new_len, T(r.divisor)) | |
end | |
## unsafe/pointer conversions ## | |
# note: the following type definitions don't mean any AbstractArray is convertible to | |
# a data Ref. they just map the array element type to the pointer type for | |
# convenience in cases that work. | |
pointer{T}(x::AbstractArray{T}) = unsafe_convert(Ptr{T}, x) | |
pointer{T}(x::AbstractArray{T}, i::Integer) = (@_inline_meta; unsafe_convert(Ptr{T},x) + (i-first(linearindices(x)))*elsize(x)) | |
## Approach: | |
# We only define one fallback method on getindex for all argument types. | |
# That dispatches to an (inlined) internal _getindex function, where the goal is | |
# to transform the indices such that we can call the only getindex method that | |
# we require the type A{T,N} <: AbstractArray{T,N} to define; either: | |
# getindex(::A, ::Int) # if linearindexing(A) == LinearFast() OR | |
# getindex{T,N}(::A{T,N}, ::Vararg{Int, N}) # if LinearSlow() | |
# If the subtype hasn't defined the required method, it falls back to the | |
# _getindex function again where an error is thrown to prevent stack overflows. | |
function getindex(A::AbstractArray, I...) | |
@_propagate_inbounds_meta | |
_getindex(linearindexing(A), A, I...) | |
end | |
function unsafe_getindex(A::AbstractArray, I...) | |
@_inline_meta | |
@inbounds r = getindex(A, I...) | |
r | |
end | |
## Internal definitions | |
_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") | |
## LinearFast Scalar indexing: canonical method is one Int | |
_getindex(::LinearFast, A::AbstractVector, ::Int) = error("indexing not defined for ", typeof(A)) | |
_getindex(::LinearFast, A::AbstractArray, ::Int) = error("indexing not defined for ", typeof(A)) | |
_getindex{T}(::LinearFast, A::AbstractArray{T,0}) = A[1] | |
_getindex(::LinearFast, A::AbstractArray, i::Real) = (@_propagate_inbounds_meta; getindex(A, to_index(i))) | |
function _getindex{T,N}(::LinearFast, A::AbstractArray{T,N}, I::Vararg{Real,N}) | |
# We must check bounds for sub2ind; so we can then use @inbounds | |
@_inline_meta | |
J = to_indexes(I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = getindex(A, sub2ind(A, J...)) | |
r | |
end | |
function _getindex(::LinearFast, A::AbstractVector, I1::Real, I::Real...) | |
@_inline_meta | |
J = to_indexes(I1, I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = getindex(A, J[1]) | |
r | |
end | |
function _getindex(::LinearFast, A::AbstractArray, I::Real...) # TODO: DEPRECATE FOR #14770 | |
@_inline_meta | |
J = to_indexes(I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = getindex(A, sub2ind(A, J...)) | |
r | |
end | |
## LinearSlow Scalar indexing: Canonical method is full dimensionality of Ints | |
_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, ::Vararg{Int, N}) = error("indexing not defined for ", typeof(A)) | |
_getindex{T,N}(::LinearSlow, A::AbstractArray{T,N}, I::Vararg{Real, N}) = (@_propagate_inbounds_meta; getindex(A, to_indexes(I...)...)) | |
function _getindex(::LinearSlow, A::AbstractArray, i::Real) | |
# ind2sub requires all dimensions to be > 0; may as well just check bounds | |
@_inline_meta | |
@boundscheck checkbounds(A, i) | |
@inbounds r = getindex(A, ind2sub(A, to_index(i))...) | |
r | |
end | |
@generated function _getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...) # TODO: DEPRECATE FOR #14770 | |
N = length(I) | |
if N > AN | |
# Drop trailing ones | |
Isplat = Expr[:(I[$d]) for d = 1:AN] | |
Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] | |
quote | |
@_propagate_inbounds_meta | |
@boundscheck (&)($(Osplat...)) || throw_boundserror(A, I) | |
getindex(A, $(Isplat...)) | |
end | |
else | |
# Expand the last index into the appropriate number of indices | |
Isplat = Expr[:(I[$d]) for d = 1:N-1] | |
sz = Expr(:tuple) | |
sz.args = Expr[:(size(A, $d)) for d=max(N,1):AN] | |
szcheck = Expr[:(size(A, $d) > 0) for d=max(N,1):AN] | |
last_idx = N > 0 ? :(to_index(I[$N])) : 1 | |
quote | |
# ind2sub requires all dimensions to be > 0: | |
@_propagate_inbounds_meta | |
@boundscheck (&)($(szcheck...)) || throw_boundserror(A, I) | |
getindex(A, $(Isplat...), ind2sub($sz, $last_idx)...) | |
end | |
end | |
end | |
## Setindex! is defined similarly. We first dispatch to an internal _setindex! | |
# function that allows dispatch on array storage | |
function setindex!(A::AbstractArray, v, I...) | |
@_propagate_inbounds_meta | |
_setindex!(linearindexing(A), A, v, I...) | |
end | |
function unsafe_setindex!(A::AbstractArray, v, I...) | |
@_inline_meta | |
@inbounds r = setindex!(A, v, I...) | |
r | |
end | |
## Internal defitions | |
_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") | |
## LinearFast Scalar indexing | |
_setindex!(::LinearFast, A::AbstractVector, v, ::Int) = error("indexed assignment not defined for ", typeof(A)) | |
_setindex!(::LinearFast, A::AbstractArray, v, ::Int) = error("indexed assignment not defined for ", typeof(A)) | |
_setindex!{T}(::LinearFast, A::AbstractArray{T,0}, v) = (@_propagate_inbounds_meta; setindex!(A, v, 1)) | |
_setindex!(::LinearFast, A::AbstractArray, v, i::Real) = (@_propagate_inbounds_meta; setindex!(A, v, to_index(i))) | |
function _setindex!{T,N}(::LinearFast, A::AbstractArray{T,N}, v, I::Vararg{Real,N}) | |
# We must check bounds for sub2ind; so we can then use @inbounds | |
@_inline_meta | |
J = to_indexes(I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = setindex!(A, v, sub2ind(A, J...)) | |
r | |
end | |
function _setindex!(::LinearFast, A::AbstractVector, v, I1::Real, I::Real...) | |
@_inline_meta | |
J = to_indexes(I1, I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = setindex!(A, v, J[1]) | |
r | |
end | |
function _setindex!(::LinearFast, A::AbstractArray, v, I::Real...) # TODO: DEPRECATE FOR #14770 | |
@_inline_meta | |
J = to_indexes(I...) | |
@boundscheck checkbounds(A, J...) | |
@inbounds r = setindex!(A, v, sub2ind(A, J...)) | |
r | |
end | |
# LinearSlow Scalar indexing | |
_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, ::Vararg{Int, N}) = error("indexed assignment not defined for ", typeof(A)) | |
_setindex!{T,N}(::LinearSlow, A::AbstractArray{T,N}, v, I::Vararg{Real, N}) = (@_propagate_inbounds_meta; setindex!(A, v, to_indexes(I...)...)) | |
function _setindex!(::LinearSlow, A::AbstractArray, v, i::Real) | |
# ind2sub requires all dimensions to be > 0; may as well just check bounds | |
@_inline_meta | |
@boundscheck checkbounds(A, i) | |
@inbounds r = setindex!(A, v, ind2sub(A, to_index(i))...) | |
r | |
end | |
@generated function _setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...) # TODO: DEPRECATE FOR #14770 | |
N = length(I) | |
if N > AN | |
# Drop trailing ones | |
Isplat = Expr[:(I[$d]) for d = 1:AN] | |
Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] | |
quote | |
# We only check the trailing ones, so just propagate @inbounds state | |
@_propagate_inbounds_meta | |
@boundscheck (&)($(Osplat...)) || throw_boundserror(A, I) | |
setindex!(A, v, $(Isplat...)) | |
end | |
else | |
# Expand the last index into the appropriate number of indices | |
Isplat = Expr[:(I[$d]) for d = 1:N-1] | |
sz = Expr(:tuple) | |
sz.args = Expr[:(size(A, $d)) for d=max(N,1):AN] | |
szcheck = Expr[:(size(A, $d) > 0) for d=max(N,1):AN] | |
last_idx = N > 0 ? :(to_index(I[$N])) : 1 | |
quote | |
# ind2sub requires all dimensions to be > 0: | |
@_propagate_inbounds_meta | |
@boundscheck (&)($(szcheck...)) || throw_boundserror(A, I) | |
setindex!(A, v, $(Isplat...), ind2sub($sz, $last_idx)...) | |
end | |
end | |
end | |
## get (getindex with a default value) ## | |
typealias RangeVecIntList{A<:AbstractVector{Int}} Union{Tuple{Vararg{Union{Range, AbstractVector{Int}}}}, AbstractVector{UnitRange{Int}}, AbstractVector{Range{Int}}, AbstractVector{A}} | |
get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default | |
get(A::AbstractArray, I::Tuple{}, default) = similar(A, typeof(default), 0) | |
get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default | |
function get!{T}(X::AbstractVector{T}, A::AbstractVector, I::Union{Range, AbstractVector{Int}}, default::T) | |
# 1d is not linear indexing | |
ind = findin(I, indices1(A)) | |
X[ind] = A[I[ind]] | |
Xind = indices1(X) | |
X[first(Xind):first(ind)-1] = default | |
X[last(ind)+1:last(Xind)] = default | |
X | |
end | |
function get!{T}(X::AbstractArray{T}, A::AbstractArray, I::Union{Range, AbstractVector{Int}}, default::T) | |
# Linear indexing | |
ind = findin(I, 1:length(A)) | |
X[ind] = A[I[ind]] | |
X[1:first(ind)-1] = default | |
X[last(ind)+1:length(X)] = default | |
X | |
end | |
get(A::AbstractArray, I::Range, default) = get!(similar(A, typeof(default), index_shape(A, I)), A, I, default) | |
# TODO: DEPRECATE FOR #14770 (just the partial linear indexing part) | |
function get!{T}(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) | |
fill!(X, default) | |
dst, src = indcopy(size(A), I) | |
X[dst...] = A[src...] | |
X | |
end | |
get(A::AbstractArray, I::RangeVecIntList, default) = get!(similar(A, typeof(default), index_shape(A, I...)), A, I, default) | |
## structured matrix methods ## | |
replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s | |
replace_in_print_matrix(A::AbstractVector,i::Integer,j::Integer,s::AbstractString) = s | |
## Concatenation ## | |
promote_eltype() = Bottom | |
promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) | |
#TODO: ERROR CHECK | |
cat(catdim::Integer) = Array{Any,1}(0) | |
vcat() = Array{Any,1}(0) | |
hcat() = Array{Any,1}(0) | |
typed_vcat{T}(::Type{T}) = Array{T,1}(0) | |
typed_hcat{T}(::Type{T}) = Array{T,1}(0) | |
## cat: special cases | |
vcat{T}(X::T...) = T[ X[i] for i=1:length(X) ] | |
vcat{T<:Number}(X::T...) = T[ X[i] for i=1:length(X) ] | |
hcat{T}(X::T...) = T[ X[j] for i=1:1, j=1:length(X) ] | |
hcat{T<:Number}(X::T...) = 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) | |
typed_vcat{T}(::Type{T}, X::Number...) = hvcat_fill(Array{T,1}(length(X)), X) | |
typed_hcat{T}(::Type{T}, X::Number...) = hvcat_fill(Array{T,2}(1,length(X)), X) | |
vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...) | |
vcat{T}(V::AbstractVector{T}...) = typed_vcat(T, V...) | |
function typed_vcat{T}(::Type{T}, V::AbstractVector...) | |
n::Int = 0 | |
for Vk in V | |
n += length(Vk) | |
end | |
a = similar(V[1], T, n) | |
pos = 1 | |
for k=1:length(V) | |
Vk = V[k] | |
p1 = pos+length(Vk)-1 | |
a[pos:p1] = Vk | |
pos = p1+1 | |
end | |
a | |
end | |
hcat(A::AbstractVecOrMat...) = typed_hcat(promote_eltype(A...), A...) | |
hcat{T}(A::AbstractVecOrMat{T}...) = typed_hcat(T, A...) | |
function typed_hcat{T}(::Type{T}, A::AbstractVecOrMat...) | |
nargs = length(A) | |
nrows = size(A[1], 1) | |
ncols = 0 | |
dense = true | |
for j = 1:nargs | |
Aj = A[j] | |
if size(Aj, 1) != nrows | |
throw(ArgumentError("number of rows of each array must match (got $(map(x->size(x,1), A)))")) | |
end | |
dense &= isa(Aj,Array) | |
nd = ndims(Aj) | |
ncols += (nd==2 ? size(Aj,2) : 1) | |
end | |
B = similar(A[1], T, nrows, ncols) | |
pos = 1 | |
if dense | |
for k=1:nargs | |
Ak = A[k] | |
n = length(Ak) | |
copy!(B, pos, Ak, 1, n) | |
pos += n | |
end | |
else | |
for k=1:nargs | |
Ak = A[k] | |
p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1 | |
B[:, pos:p1] = Ak | |
pos = p1+1 | |
end | |
end | |
return B | |
end | |
vcat(A::AbstractMatrix...) = typed_vcat(promote_eltype(A...), A...) | |
vcat{T}(A::AbstractMatrix{T}...) = typed_vcat(T, A...) | |
function typed_vcat{T}(::Type{T}, A::AbstractMatrix...) | |
nargs = length(A) | |
nrows = sum(a->size(a, 1), A)::Int | |
ncols = size(A[1], 2) | |
for j = 2:nargs | |
if size(A[j], 2) != ncols | |
throw(ArgumentError("number of columns of each array must match (got $(map(x->size(x,2), A)))")) | |
end | |
end | |
B = similar(A[1], T, nrows, ncols) | |
pos = 1 | |
for k=1:nargs | |
Ak = A[k] | |
p1 = pos+size(Ak,1)-1 | |
B[pos:p1, :] = Ak | |
pos = p1+1 | |
end | |
return B | |
end | |
## cat: general case | |
function cat(catdims, X...) | |
T = promote_type(map(x->isa(x,AbstractArray) ? eltype(x) : typeof(x), X)...) | |
cat_t(catdims, T, X...) | |
end | |
function cat_t(catdims, typeC::Type, X...) | |
catdims = collect(catdims) | |
nargs = length(X) | |
ndimsX = Int[isa(a,AbstractArray) ? ndims(a) : 0 for a in X] | |
ndimsC = max(maximum(ndimsX), maximum(catdims)) | |
catsizes = zeros(Int,(nargs,length(catdims))) | |
dims2cat = zeros(Int,ndimsC) | |
for k = 1:length(catdims) | |
dims2cat[catdims[k]]=k | |
end | |
dimsC = Int[d <= ndimsX[1] ? size(X[1],d) : 1 for d=1:ndimsC] | |
for k = 1:length(catdims) | |
catsizes[1,k] = dimsC[catdims[k]] | |
end | |
for i = 2:nargs | |
for d = 1:ndimsC | |
currentdim = (d <= ndimsX[i] ? size(X[i],d) : 1) | |
if dims2cat[d] != 0 | |
dimsC[d] += currentdim | |
catsizes[i,dims2cat[d]] = currentdim | |
elseif dimsC[d] != currentdim | |
throw(DimensionMismatch(string("mismatch in dimension ",d, | |
" (expected ",dimsC[d], | |
" got ",currentdim,")"))) | |
end | |
end | |
end | |
C = similar(isa(X[1],AbstractArray) ? X[1] : [X[1]], typeC, tuple(dimsC...)) | |
if length(catdims)>1 | |
fill!(C,0) | |
end | |
offsets = zeros(Int,length(catdims)) | |
for i=1:nargs | |
cat_one = [ dims2cat[d] == 0 ? (1:dimsC[d]) : (offsets[dims2cat[d]]+(1:catsizes[i,dims2cat[d]])) | |
for d=1:ndimsC ] | |
C[cat_one...] = X[i] | |
for k = 1:length(catdims) | |
offsets[k] += catsizes[i,k] | |
end | |
end | |
return C | |
end | |
""" | |
vcat(A...) | |
Concatenate along dimension 1. | |
```jldoctest | |
julia> a = [1 2 3 4 5] | |
1×5 Array{Int64,2}: | |
1 2 3 4 5 | |
julia> b = [6 7 8 9 10; 11 12 13 14 15] | |
2×5 Array{Int64,2}: | |
6 7 8 9 10 | |
11 12 13 14 15 | |
julia> vcat(a,b) | |
3×5 Array{Int64,2}: | |
1 2 3 4 5 | |
6 7 8 9 10 | |
11 12 13 14 15 | |
julia> c = ([1 2 3], [4 5 6]) | |
( | |
[1 2 3], | |
<BLANKLINE> | |
[4 5 6]) | |
julia> vcat(c...) | |
2×3 Array{Int64,2}: | |
1 2 3 | |
4 5 6 | |
``` | |
""" | |
vcat(X...) = cat(1, X...) | |
""" | |
hcat(A...) | |
Concatenate along dimension 2. | |
```jldoctest | |
julia> a = [1; 2; 3; 4; 5] | |
5-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
julia> b = [6 7; 8 9; 10 11; 12 13; 14 15] | |
5×2 Array{Int64,2}: | |
6 7 | |
8 9 | |
10 11 | |
12 13 | |
14 15 | |
julia> hcat(a,b) | |
5×3 Array{Int64,2}: | |
1 6 7 | |
2 8 9 | |
3 10 11 | |
4 12 13 | |
5 14 15 | |
julia> c = ([1; 2; 3], [4; 5; 6]) | |
([1,2,3],[4,5,6]) | |
julia> hcat(c...) | |
3×2 Array{Int64,2}: | |
1 4 | |
2 5 | |
3 6 | |
``` | |
""" | |
hcat(X...) = cat(2, X...) | |
typed_vcat(T::Type, X...) = cat_t(1, T, X...) | |
typed_hcat(T::Type, X...) = cat_t(2, T, X...) | |
cat{T}(catdims, A::AbstractArray{T}...) = cat_t(catdims, T, A...) | |
cat(catdims, A::AbstractArray...) = cat_t(catdims, promote_eltype(A...), A...) | |
# The specializations for 1 and 2 inputs are important | |
# especially when running with --inline=no, see #11158 | |
vcat(A::AbstractArray) = cat(1, A) | |
vcat(A::AbstractArray, B::AbstractArray) = cat(1, A, B) | |
vcat(A::AbstractArray...) = cat(1, A...) | |
hcat(A::AbstractArray) = cat(2, A) | |
hcat(A::AbstractArray, B::AbstractArray) = cat(2, A, B) | |
hcat(A::AbstractArray...) = cat(2, A...) | |
typed_vcat(T::Type, A::AbstractArray) = cat_t(1, T, A) | |
typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(1, T, A, B) | |
typed_vcat(T::Type, A::AbstractArray...) = cat_t(1, T, A...) | |
typed_hcat(T::Type, A::AbstractArray) = cat_t(2, T, A) | |
typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(2, T, A, B) | |
typed_hcat(T::Type, A::AbstractArray...) = cat_t(2, T, A...) | |
# 2d horizontal and vertical concatenation | |
function hvcat(nbc::Integer, as...) | |
# nbc = # of block columns | |
n = length(as) | |
mod(n,nbc) != 0 && | |
throw(ArgumentError("number of arrays $n is not a multiple of the requested number of block columns $nbc")) | |
nbr = div(n,nbc) | |
hvcat(ntuple(i->nbc, nbr), as...) | |
end | |
""" | |
hvcat(rows::Tuple{Vararg{Int}}, values...) | |
Horizontal and vertical concatenation in one call. This function is called for block matrix | |
syntax. The first argument specifies the number of arguments to concatenate in each block | |
row. | |
```jldoctest | |
julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 | |
(1,2,3,4,5,6) | |
julia> [a b c; d e f] | |
2×3 Array{Int64,2}: | |
1 2 3 | |
4 5 6 | |
julia> hvcat((3,3), a,b,c,d,e,f) | |
2×3 Array{Int64,2}: | |
1 2 3 | |
4 5 6 | |
julia> [a b;c d; e f] | |
3×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
5 6 | |
julia> hvcat((2,2,2), a,b,c,d,e,f) | |
3×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
5 6 | |
``` | |
If the first argument is a single integer `n`, then all block rows are assumed to have `n` | |
block columns. | |
""" | |
hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractMatrix...) = typed_hvcat(promote_eltype(xs...), rows, xs...) | |
hvcat{T}(rows::Tuple{Vararg{Int}}, xs::AbstractMatrix{T}...) = typed_hvcat(T, rows, xs...) | |
function typed_hvcat{T}(::Type{T}, rows::Tuple{Vararg{Int}}, as::AbstractMatrix...) | |
nbr = length(rows) # number of block rows | |
nc = 0 | |
for i=1:rows[1] | |
nc += size(as[i],2) | |
end | |
nr = 0 | |
a = 1 | |
for i = 1:nbr | |
nr += size(as[a],1) | |
a += rows[i] | |
end | |
out = similar(as[1], T, nr, nc) | |
a = 1 | |
r = 1 | |
for i = 1:nbr | |
c = 1 | |
szi = size(as[a],1) | |
for j = 1:rows[i] | |
Aj = as[a+j-1] | |
szj = size(Aj,2) | |
if size(Aj,1) != szi | |
throw(ArgumentError("mismatched height in block row $(i) (expected $szi, got $(size(Aj,1)))")) | |
end | |
if c-1+szj > nc | |
throw(ArgumentError("block row $(i) has mismatched number of columns (expected $nc, got $(c-1+szj))")) | |
end | |
out[r:r-1+szi, c:c-1+szj] = Aj | |
c += szj | |
end | |
if c != nc+1 | |
throw(ArgumentError("block row $(i) has mismatched number of columns (expected $nc, got $(c-1))")) | |
end | |
r += szi | |
a += rows[i] | |
end | |
out | |
end | |
hvcat(rows::Tuple{Vararg{Int}}) = [] | |
typed_hvcat{T}(::Type{T}, rows::Tuple{Vararg{Int}}) = Array{T,1}(0) | |
function hvcat{T<:Number}(rows::Tuple{Vararg{Int}}, xs::T...) | |
nr = length(rows) | |
nc = rows[1] | |
a = Array{T,2}(nr, nc) | |
if length(a) != length(xs) | |
throw(ArgumentError("argument count does not match specified shape (expected $(length(a)), got $(length(xs)))")) | |
end | |
k = 1 | |
@inbounds for i=1:nr | |
if nc != rows[i] | |
throw(ArgumentError("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) | |
end | |
for j=1:nc | |
a[i,j] = xs[k] | |
k += 1 | |
end | |
end | |
a | |
end | |
function hvcat_fill(a::Array, xs::Tuple) | |
k = 1 | |
nr, nc = size(a,1), size(a,2) | |
for i=1:nr | |
@inbounds for j=1:nc | |
a[i,j] = xs[k] | |
k += 1 | |
end | |
end | |
a | |
end | |
hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...) | |
function typed_hvcat{T}(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) | |
nr = length(rows) | |
nc = rows[1] | |
for i = 2:nr | |
if nc != rows[i] | |
throw(ArgumentError("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) | |
end | |
end | |
len = length(xs) | |
if nr*nc != len | |
throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))")) | |
end | |
hvcat_fill(Array{T,2}(nr, nc), xs) | |
end | |
# fallback definition of hvcat in terms of hcat and vcat | |
function hvcat(rows::Tuple{Vararg{Int}}, as...) | |
nbr = length(rows) # number of block rows | |
rs = Array{Any,1}(nbr) | |
a = 1 | |
for i = 1:nbr | |
rs[i] = hcat(as[a:a-1+rows[i]]...) | |
a += rows[i] | |
end | |
vcat(rs...) | |
end | |
function typed_hvcat{T}(::Type{T}, rows::Tuple{Vararg{Int}}, as...) | |
nbr = length(rows) # number of block rows | |
rs = Array{Any,1}(nbr) | |
a = 1 | |
for i = 1:nbr | |
rs[i] = typed_hcat(T, as[a:a-1+rows[i]]...) | |
a += rows[i] | |
end | |
T[rs...;] | |
end | |
## Reductions and scans ## | |
function isequal(A::AbstractArray, B::AbstractArray) | |
if A === B return true end | |
if indices(A) != indices(B) | |
return false | |
end | |
if isa(A,Range) != isa(B,Range) | |
return false | |
end | |
for (a, b) in zip(A, B) | |
if !isequal(a, b) | |
return false | |
end | |
end | |
return true | |
end | |
function lexcmp(A::AbstractArray, B::AbstractArray) | |
for (a, b) in zip(A, B) | |
res = lexcmp(a, b) | |
res == 0 || return res | |
end | |
return cmp(length(A), length(B)) | |
end | |
function (==)(A::AbstractArray, B::AbstractArray) | |
if indices(A) != indices(B) | |
return false | |
end | |
if isa(A,Range) != isa(B,Range) | |
return false | |
end | |
for (a, b) in zip(A, B) | |
if !(a == b) | |
return false | |
end | |
end | |
return true | |
end | |
# sub2ind and ind2sub | |
# fallbacks | |
function sub2ind(A::AbstractArray, I...) | |
@_inline_meta | |
sub2ind(indices(A), I...) | |
end | |
""" | |
ind2sub(a, index) -> subscripts | |
Returns a tuple of subscripts into array `a` corresponding to the linear index `index`. | |
```jldoctest | |
julia> A = ones(5,6,7); | |
julia> ind2sub(A,35) | |
(5,1,2) | |
julia> ind2sub(A,70) | |
(5,2,3) | |
``` | |
""" | |
function ind2sub(A::AbstractArray, ind) | |
@_inline_meta | |
ind2sub(indices(A), ind) | |
end | |
# 0-dimensional arrays and indexing with [] | |
sub2ind(::Tuple{}) = 1 | |
sub2ind(::DimsInteger) = 1 | |
sub2ind(::Indices) = 1 | |
sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind((), 1, 1, I...)) | |
# Generic cases | |
""" | |
sub2ind(dims, i, j, k...) -> index | |
The inverse of [`ind2sub`](:func:`ind2sub`), returns the linear index corresponding to the provided subscripts. | |
```jldoctest | |
julia> sub2ind((5,6,7),1,2,3) | |
66 | |
julia> sub2ind((5,6,7),1,6,3) | |
86 | |
``` | |
""" | |
sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind(dims, 1, 1, I...)) | |
sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) | |
# In 1d, there's a question of whether we're doing cartesian indexing | |
# or linear indexing. Support only the former. | |
sub2ind(inds::Indices{1}, I::Integer...) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | |
sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind(inds, 1, 1, I...)) # only OneTo is safe | |
sub2ind(inds::Tuple{OneTo}, i::Integer) = i | |
_sub2ind(::Any, L, ind) = ind | |
function _sub2ind(::Tuple{}, L, ind, i::Integer, I::Integer...) | |
@_inline_meta | |
_sub2ind((), L, ind+(i-1)*L, I...) | |
end | |
function _sub2ind(inds, L, ind, i::Integer, I::Integer...) | |
@_inline_meta | |
r1 = inds[1] | |
_sub2ind(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) | |
end | |
nextL(L, l::Integer) = L*l | |
nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) | |
offsetin(i, l::Integer) = i-1 | |
offsetin(i, r::AbstractUnitRange) = i-first(r) | |
ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) | |
""" | |
ind2sub(dims, index) -> subscripts | |
Returns a tuple of subscripts into an array with dimensions `dims`, | |
corresponding to the linear index `index`. | |
**Example**: | |
``` | |
i, j, ... = ind2sub(size(A), indmax(A)) | |
``` | |
provides the indices of the maximum element. | |
```jldoctest | |
julia> ind2sub((3,4),2) | |
(2,1) | |
julia> ind2sub((3,4),3) | |
(3,1) | |
julia> ind2sub((3,4),4) | |
(1,2) | |
``` | |
""" | |
ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub(dims, ind-1)) | |
ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub(inds, ind-1)) | |
ind2sub(inds::Indices{1}, ind::Integer) = throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | |
ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) | |
_ind2sub(::Tuple{}, ind) = (ind+1,) | |
function _ind2sub(indslast::NTuple{1}, ind) | |
@_inline_meta | |
(_lookup(ind, indslast[1]),) | |
end | |
function _ind2sub(inds, ind) | |
@_inline_meta | |
r1 = inds[1] | |
indnext, f, l = _div(ind, r1) | |
(ind-l*indnext+f, _ind2sub(tail(inds), indnext)...) | |
end | |
_lookup(ind, d::Integer) = ind+1 | |
_lookup(ind, r::AbstractUnitRange) = ind+first(r) | |
_div(ind, d::Integer) = div(ind, d), 1, d | |
_div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) | |
# Vectorized forms | |
function sub2ind{T<:Integer}(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) | |
throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) | |
end | |
sub2ind{T<:Integer}(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) = _sub2ind_vecs(inds, I1, I...) | |
sub2ind{T<:Integer}(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) = _sub2ind_vecs(inds, I1, I...) | |
function _sub2ind_vecs(inds, I::AbstractVector...) | |
I1 = I[1] | |
Iinds = indices1(I1) | |
for j = 2:length(I) | |
indices1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(indices1(I[j])))")) | |
end | |
Iout = similar(I1) | |
_sub2ind!(Iout, inds, Iinds, I) | |
Iout | |
end | |
function _sub2ind!(Iout, inds, Iinds, I) | |
@_noinline_meta | |
for i in Iinds | |
# Iout[i] = sub2ind(inds, map(Ij->Ij[i], I)...) | |
Iout[i] = sub2ind_vec(inds, i, I) | |
end | |
Iout | |
end | |
sub2ind_vec(inds, i, I) = (@_inline_meta; _sub2ind_vec(inds, (), i, I...)) | |
_sub2ind_vec(inds, out, i, I1, I...) = (@_inline_meta; _sub2ind_vec(inds, (out..., I1[i]), i, I...)) | |
_sub2ind_vec(inds, out, i) = (@_inline_meta; sub2ind(inds, out...)) | |
function ind2sub{N,T<:Integer}(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{T}) | |
M = length(ind) | |
t = ntuple(n->similar(ind),Val{N}) | |
for (i,idx) in enumerate(ind) # FIXME: change to eachindexvalue | |
sub = ind2sub(inds, idx) | |
for j = 1:N | |
t[j][i] = sub[j] | |
end | |
end | |
t | |
end | |
function ind2sub!{T<:Integer}(sub::Array{T}, dims::Tuple{Vararg{T}}, ind::T) | |
ndims = length(dims) | |
for i=1:ndims-1 | |
ind2 = div(ind-1,dims[i])+1 | |
sub[i] = ind - dims[i]*(ind2-1) | |
ind = ind2 | |
end | |
sub[ndims] = ind | |
return sub | |
end | |
## iteration utilities ## | |
""" | |
foreach(f, c...) -> Void | |
Call function `f` on each element of iterable `c`. | |
For multiple iterable arguments, `f` is called elementwise. | |
`foreach` should be used instead of `map` when the results of `f` are not | |
needed, for example in `foreach(println, array)`. | |
```jldoctest | |
julia> a = 1:3:7; | |
julia> foreach(x->println(x^2),a) | |
1 | |
16 | |
49 | |
``` | |
""" | |
foreach(f) = (f(); nothing) | |
foreach(f, itr) = (for x in itr; f(x); end; nothing) | |
foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing) | |
## map over arrays ## | |
## transform any set of dimensions | |
## dims specifies which dimensions will be transformed. for example | |
## dims==1:2 will call f on all slices A[:,:,...] | |
""" | |
mapslices(f, A, dims) | |
Transform the given dimensions of array `A` using function `f`. `f` is called on each slice | |
of `A` of the form `A[...,:,...,:,...]`. `dims` is an integer vector specifying where the | |
colons go in this expression. The results are concatenated along the remaining dimensions. | |
For example, if `dims` is `[1,2]` and `A` is 4-dimensional, `f` is called on `A[:,:,i,j]` | |
for all `i` and `j`. | |
```jldoctest | |
julia> a = reshape(collect(1:16),(2,2,2,2)) | |
2×2×2×2 Array{Int64,4}: | |
[:, :, 1, 1] = | |
1 3 | |
2 4 | |
<BLANKLINE> | |
[:, :, 2, 1] = | |
5 7 | |
6 8 | |
<BLANKLINE> | |
[:, :, 1, 2] = | |
9 11 | |
10 12 | |
<BLANKLINE> | |
[:, :, 2, 2] = | |
13 15 | |
14 16 | |
julia> mapslices(sum, a, [1,2]) | |
1×1×2×2 Array{Int64,4}: | |
[:, :, 1, 1] = | |
10 | |
<BLANKLINE> | |
[:, :, 2, 1] = | |
26 | |
<BLANKLINE> | |
[:, :, 1, 2] = | |
42 | |
<BLANKLINE> | |
[:, :, 2, 2] = | |
58 | |
``` | |
""" | |
mapslices(f, A::AbstractArray, dims) = mapslices(f, A, [dims...]) | |
function mapslices(f, A::AbstractArray, dims::AbstractVector) | |
if isempty(dims) | |
return map(f,A) | |
end | |
dimsA = [indices(A)...] | |
ndimsA = ndims(A) | |
alldims = [1:ndimsA;] | |
otherdims = setdiff(alldims, dims) | |
idx = Any[first(ind) for ind in indices(A)] | |
itershape = tuple(dimsA[otherdims]...) | |
for d in dims | |
idx[d] = Colon() | |
end | |
Aslice = A[idx...] | |
r1 = f(Aslice) | |
# determine result size and allocate | |
Rsize = copy(dimsA) | |
# TODO: maybe support removing dimensions | |
if !isa(r1, AbstractArray) || ndims(r1) == 0 | |
r1 = [r1] | |
end | |
nextra = max(0,length(dims)-ndims(r1)) | |
if eltype(Rsize) == Int | |
Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...] | |
else | |
Rsize[dims] = [indices(r1)..., ntuple(d->OneTo(1), nextra)...] | |
end | |
R = similar(r1, tuple(Rsize...,)) | |
ridx = Any[map(first, indices(R))...] | |
for d in dims | |
ridx[d] = indices(R,d) | |
end | |
R[ridx...] = r1 | |
isfirst = true | |
nidx = length(otherdims) | |
for I in CartesianRange(itershape) | |
if isfirst | |
isfirst = false | |
else | |
for i in 1:nidx | |
idx[otherdims[i]] = ridx[otherdims[i]] = I.I[i] | |
end | |
_unsafe_getindex!(Aslice, A, idx...) | |
R[ridx...] = f(Aslice) | |
end | |
end | |
return R | |
end | |
# These are needed because map(eltype, As) is not inferrable | |
promote_eltype_op(::Any) = (@_pure_meta; Any) | |
promote_eltype_op(op, A) = (@_pure_meta; promote_op(op, eltype(A))) | |
promote_eltype_op{T}(op, ::AbstractArray{T}) = (@_pure_meta; promote_op(op, T)) | |
promote_eltype_op{T}(op, ::AbstractArray{T}, A) = (@_pure_meta; promote_op(op, T, eltype(A))) | |
promote_eltype_op{T}(op, A, ::AbstractArray{T}) = (@_pure_meta; promote_op(op, eltype(A), T)) | |
promote_eltype_op{R,S}(op, ::AbstractArray{R}, ::AbstractArray{S}) = (@_pure_meta; promote_op(op, R, S)) | |
promote_eltype_op(op, A, B) = (@_pure_meta; promote_op(op, eltype(A), eltype(B))) | |
promote_eltype_op(op, A, B, C, D...) = (@_pure_meta; promote_eltype_op(op, promote_eltype_op(op, A, B), C, D...)) | |
## 1 argument | |
""" | |
map!(function, collection) | |
In-place version of [`map`](:func:`map`). | |
""" | |
map!{F}(f::F, A::AbstractArray) = map!(f, A, A) | |
function map!{F}(f::F, dest::AbstractArray, A::AbstractArray) | |
for (i,j) in zip(eachindex(dest),eachindex(A)) | |
dest[i] = f(A[j]) | |
end | |
return dest | |
end | |
# map on collections | |
map(f, A::Union{AbstractArray,AbstractSet,Associative}) = collect_similar(A, Generator(f,A)) | |
# default to returning an Array for `map` on general iterators | |
""" | |
map(f, c...) -> collection | |
Transform collection `c` by applying `f` to each element. For multiple collection arguments, | |
apply `f` elementwise. | |
```jldoctest | |
julia> map((x) -> x * 2, [1, 2, 3]) | |
3-element Array{Int64,1}: | |
2 | |
4 | |
6 | |
julia> map(+, [1, 2, 3], [10, 20, 30]) | |
3-element Array{Int64,1}: | |
11 | |
22 | |
33 | |
``` | |
""" | |
map(f, A) = collect(Generator(f,A)) | |
## 2 argument | |
function map!{F}(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) | |
for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B)) | |
dest[i] = f(A[j], B[k]) | |
end | |
return dest | |
end | |
## N argument | |
ith_all(i, ::Tuple{}) = () | |
ith_all(i, as) = (as[1][i], ith_all(i, tail(as))...) | |
function map_n!{F}(f::F, dest::AbstractArray, As) | |
for i = linearindices(As[1]) | |
dest[i] = f(ith_all(i, As)...) | |
end | |
return dest | |
end | |
""" | |
map!(function, destination, collection...) | |
Like [`map`](:func:`map`), but stores the result in `destination` rather than a new | |
collection. `destination` must be at least as large as the first collection. | |
""" | |
map!{F}(f::F, dest::AbstractArray, As::AbstractArray...) = map_n!(f, dest, As) | |
map(f) = f() | |
map(f, iters...) = collect(Generator(f, iters...)) | |
# multi-item push!, unshift! (built on top of type-specific 1-item version) | |
# (note: must not cause a dispatch loop when 1-item case is not defined) | |
push!(A, a, b) = push!(push!(A, a), b) | |
push!(A, a, b, c...) = push!(push!(A, a, b), c...) | |
unshift!(A, a, b) = unshift!(unshift!(A, b), a) | |
unshift!(A, a, b, c...) = unshift!(unshift!(A, c...), a, b) | |
## hashing collections ## | |
const hashaa_seed = UInt === UInt64 ? 0x7f53e68ceb575e76 : 0xeb575e76 | |
const hashrle_seed = UInt == UInt64 ? 0x2aab8909bfea414c : 0xbfea414c | |
function hash(a::AbstractArray, h::UInt) | |
h += hashaa_seed | |
h += hash(size(a)) | |
state = start(a) | |
done(a, state) && return h | |
x2, state = next(a, state) | |
done(a, state) && return hash(x2, h) | |
x1 = x2 | |
while !done(a, state) | |
x1 = x2 | |
x2, state = next(a, state) | |
if isequal(x2, x1) | |
# For repeated elements, use run length encoding | |
# This allows efficient hashing of sparse arrays | |
runlength = 2 | |
while !done(a, state) | |
x2, state = next(a, state) | |
isequal(x1, x2) || break | |
runlength += 1 | |
end | |
h += hashrle_seed | |
h = hash(runlength, h) | |
end | |
h = hash(x1, h) | |
end | |
!isequal(x2, x1) && (h = hash(x2, h)) | |
return h | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## Basic functions ## | |
isinteger(x::AbstractArray) = all(isinteger,x) | |
isinteger{T<:Integer,n}(x::AbstractArray{T,n}) = true | |
isreal(x::AbstractArray) = all(isreal,x) | |
isreal{T<:Real,n}(x::AbstractArray{T,n}) = true | |
ctranspose(a::AbstractArray) = error("ctranspose not implemented for $(typeof(a)). Consider adding parentheses, e.g. A*(B*C') instead of A*B*C' to avoid explicit calculation of the transposed matrix.") | |
transpose(a::AbstractArray) = error("transpose not implemented for $(typeof(a)). Consider adding parentheses, e.g. A*(B*C.') instead of A*B*C' to avoid explicit calculation of the transposed matrix.") | |
## Constructors ## | |
""" | |
vec(a::AbstractArray) -> Vector | |
Reshape array `a` as a one-dimensional column vector. | |
```jldoctest | |
julia> a = [1 2 3; 4 5 6] | |
2×3 Array{Int64,2}: | |
1 2 3 | |
4 5 6 | |
julia> vec(a) | |
6-element Array{Int64,1}: | |
1 | |
4 | |
2 | |
5 | |
3 | |
6 | |
``` | |
""" | |
vec(a::AbstractArray) = reshape(a,_length(a)) | |
vec(a::AbstractVector) = a | |
_sub(::Tuple{}, ::Tuple{}) = () | |
_sub(t::Tuple, ::Tuple{}) = t | |
_sub(t::Tuple, s::Tuple) = _sub(tail(t), tail(s)) | |
""" | |
squeeze(A, dims) | |
Remove the dimensions specified by `dims` from array `A`. | |
Elements of `dims` must be unique and within the range `1:ndims(A)`. | |
`size(A,i)` must equal 1 for all `i` in `dims`. | |
```jldoctest | |
julia> a = reshape(collect(1:4),(2,2,1,1)) | |
2×2×1×1 Array{Int64,4}: | |
[:, :, 1, 1] = | |
1 3 | |
2 4 | |
julia> squeeze(a,3) | |
2×2×1 Array{Int64,3}: | |
[:, :, 1] = | |
1 3 | |
2 4 | |
``` | |
""" | |
function squeeze(A::AbstractArray, dims::Dims) | |
for i in 1:length(dims) | |
1 <= dims[i] <= ndims(A) || throw(ArgumentError("squeezed dims must be in range 1:ndims(A)")) | |
size(A, dims[i]) == 1 || throw(ArgumentError("squeezed dims must all be size 1")) | |
for j = 1:i-1 | |
dims[j] == dims[i] && throw(ArgumentError("squeezed dims must be unique")) | |
end | |
end | |
d = () | |
for i = 1:ndims(A) | |
if !in(i, dims) | |
d = tuple(d..., size(A, i)) | |
end | |
end | |
reshape(A, d::typeof(_sub(size(A), dims))) | |
end | |
squeeze(A::AbstractArray, dim::Integer) = squeeze(A, (Int(dim),)) | |
## Unary operators ## | |
conj{T<:Real}(x::AbstractArray{T}) = x | |
conj!{T<:Real}(x::AbstractArray{T}) = x | |
real{T<:Real}(x::AbstractArray{T}) = x | |
imag{T<:Real}(x::AbstractArray{T}) = zero(x) | |
+{T<:Number}(x::AbstractArray{T}) = x | |
*{T<:Number}(x::AbstractArray{T,2}) = x | |
## Binary arithmetic operators ## | |
*(A::Number, B::AbstractArray) = A .* B | |
*(A::AbstractArray, B::Number) = A .* B | |
/(A::AbstractArray, B::Number) = A ./ B | |
\(A::Number, B::AbstractArray) = B ./ A | |
# index A[:,:,...,i,:,:,...] where "i" is in dimension "d" | |
""" | |
slicedim(A, d::Integer, i) | |
Return all the data of `A` where the index for dimension `d` equals `i`. Equivalent to | |
`A[:,:,...,i,:,:,...]` where `i` is in position `d`. | |
```jldoctest | |
julia> A = [1 2 3 4; 5 6 7 8] | |
2×4 Array{Int64,2}: | |
1 2 3 4 | |
5 6 7 8 | |
julia> slicedim(A,2,3) | |
2-element Array{Int64,1}: | |
3 | |
7 | |
``` | |
""" | |
function slicedim(A::AbstractArray, d::Integer, i) | |
d >= 1 || throw(ArgumentError("dimension must be ≥ 1")) | |
nd = ndims(A) | |
d > nd && (i == 1 || throw_boundserror(A, (ntuple(k->Colon(),nd)..., ntuple(k->1,d-1-nd)..., i))) | |
A[( n==d ? i : indices(A,n) for n in 1:nd )...] | |
end | |
function flipdim(A::AbstractVector, d::Integer) | |
d == 1 || throw(ArgumentError("dimension to flip must be 1")) | |
reverse(A) | |
end | |
""" | |
flipdim(A, d::Integer) | |
Reverse `A` in dimension `d`. | |
```jldoctest | |
julia> b = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> flipdim(b,2) | |
2×2 Array{Int64,2}: | |
2 1 | |
4 3 | |
``` | |
""" | |
function flipdim(A::AbstractArray, d::Integer) | |
nd = ndims(A) | |
1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) | |
if isempty(A) | |
return copy(A) | |
end | |
inds = indices(A) | |
B = similar(A) | |
nnd = 0 | |
for i = 1:nd | |
nnd += Int(length(inds[i])==1 || i==d) | |
end | |
indsd = inds[d] | |
sd = first(indsd)+last(indsd) | |
if nnd==nd | |
# flip along the only non-singleton dimension | |
for i in indsd | |
B[i] = A[sd-i] | |
end | |
return B | |
end | |
alli = [ indices(B,n) for n in 1:nd ] | |
for i in indsd | |
B[[ n==d ? sd-i : alli[n] for n in 1:nd ]...] = slicedim(A, d, i) | |
end | |
return B | |
end | |
function circshift(a::AbstractArray, shiftamt::Real) | |
circshift!(similar(a), a, (Integer(shiftamt),)) | |
end | |
circshift(a::AbstractArray, shiftamt::DimsInteger) = circshift!(similar(a), a, shiftamt) | |
""" | |
circshift(A, shifts) | |
Circularly shift the data in an array. The second argument is a vector giving the amount to | |
shift in each dimension. | |
```jldoctest | |
julia> b = reshape(collect(1:16), (4,4)) | |
4×4 Array{Int64,2}: | |
1 5 9 13 | |
2 6 10 14 | |
3 7 11 15 | |
4 8 12 16 | |
julia> circshift(b, (0,2)) | |
4×4 Array{Int64,2}: | |
9 13 1 5 | |
10 14 2 6 | |
11 15 3 7 | |
12 16 4 8 | |
julia> circshift(b, (-1,0)) | |
4×4 Array{Int64,2}: | |
2 6 10 14 | |
3 7 11 15 | |
4 8 12 16 | |
1 5 9 13 | |
``` | |
See also [`circshift!`](:func:`circshift!`). | |
""" | |
function circshift(a::AbstractArray, shiftamt) | |
circshift!(similar(a), a, map(Integer, (shiftamt...,))) | |
end | |
# Uses K-B-N summation | |
function cumsum_kbn{T<:AbstractFloat}(v::AbstractVector{T}) | |
r = similar(v) | |
if isempty(v); return r; end | |
inds = indices(v, 1) | |
i1 = first(inds) | |
s = r[i1] = v[i1] | |
c = zero(T) | |
for i=i1+1:last(inds) | |
vi = v[i] | |
t = s + vi | |
if abs(s) >= abs(vi) | |
c += ((s-t) + vi) | |
else | |
c += ((vi-t) + s) | |
end | |
s = t | |
r[i] = s+c | |
end | |
return r | |
end | |
# Uses K-B-N summation | |
# TODO: Needs a separate LinearSlow method, this is only fast for LinearIndexing | |
""" | |
cumsum_kbn(A, [dim::Integer=1]) | |
Cumulative sum along a dimension, using the Kahan-Babuska-Neumaier compensated summation | |
algorithm for additional accuracy. The dimension defaults to 1. | |
""" | |
function cumsum_kbn{T<:AbstractFloat}(A::AbstractArray{T}, axis::Integer=1) | |
dimsA = size(A) | |
ndimsA = ndims(A) | |
axis_size = dimsA[axis] | |
axis_stride = 1 | |
for i = 1:(axis-1) | |
axis_stride *= size(A,i) | |
end | |
if axis_size <= 1 | |
return A | |
end | |
B = similar(A) | |
C = similar(A) | |
for i = 1:length(A) | |
if div(i-1, axis_stride) % axis_size == 0 | |
B[i] = A[i] | |
C[i] = zero(T) | |
else | |
s = B[i-axis_stride] | |
Ai = A[i] | |
B[i] = t = s + Ai | |
if abs(s) >= abs(Ai) | |
C[i] = C[i-axis_stride] + ((s-t) + Ai) | |
else | |
C[i] = C[i-axis_stride] + ((Ai-t) + s) | |
end | |
end | |
end | |
return B + C | |
end | |
## Other array functions ## | |
""" | |
repmat(A, m::Int, n::Int=1) | |
Construct a matrix by repeating the given matrix `m` times in dimension 1 and `n` times in | |
dimension 2. | |
```jldoctest | |
julia> repmat([1, 2, 3], 2) | |
6-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
1 | |
2 | |
3 | |
julia> repmat([1, 2, 3], 2, 3) | |
6×3 Array{Int64,2}: | |
1 1 1 | |
2 2 2 | |
3 3 3 | |
1 1 1 | |
2 2 2 | |
3 3 3 | |
``` | |
""" | |
function repmat(a::AbstractVecOrMat, m::Int, n::Int=1) | |
o, p = size(a,1), size(a,2) | |
b = similar(a, o*m, p*n) | |
for j=1:n | |
d = (j-1)*p+1 | |
R = d:d+p-1 | |
for i=1:m | |
c = (i-1)*o+1 | |
b[c:c+o-1, R] = a | |
end | |
end | |
return b | |
end | |
function repmat(a::AbstractVector, m::Int) | |
o = length(a) | |
b = similar(a, o*m) | |
for i=1:m | |
c = (i-1)*o+1 | |
b[c:c+o-1] = a | |
end | |
return b | |
end | |
""" | |
repeat(A::AbstractArray; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A))) | |
Construct an array by repeating the entries of `A`. The i-th element of `inner` specifies | |
the number of times that the individual entries of the i-th dimension of `A` should be | |
repeated. The i-th element of `outer` specifies the number of times that a slice along the | |
i-th dimension of `A` should be repeated. If `inner` or `outer` are omitted, no repetition | |
is performed. | |
```jldoctest | |
julia> repeat(1:2, inner=2) | |
4-element Array{Int64,1}: | |
1 | |
1 | |
2 | |
2 | |
julia> repeat(1:2, outer=2) | |
4-element Array{Int64,1}: | |
1 | |
2 | |
1 | |
2 | |
julia> repeat([1 2; 3 4], inner=(2, 1), outer=(1, 3)) | |
4×6 Array{Int64,2}: | |
1 2 1 2 1 2 | |
1 2 1 2 1 2 | |
3 4 3 4 3 4 | |
3 4 3 4 3 4 | |
``` | |
""" | |
function repeat(A::AbstractArray; | |
inner=ntuple(x->1, ndims(A)), | |
outer=ntuple(x->1, ndims(A))) | |
ndims_in = ndims(A) | |
length_inner = length(inner) | |
length_outer = length(outer) | |
length_inner >= ndims_in || throw(ArgumentError("number of inner repetitions ($(length(inner))) cannot be less than number of dimensions of input ($(ndims(A)))")) | |
length_outer >= ndims_in || throw(ArgumentError("number of outer repetitions ($(length(outer))) cannot be less than number of dimensions of input ($(ndims(A)))")) | |
ndims_out = max(ndims_in, length_inner, length_outer) | |
inner = vcat(collect(inner), ones(Int,ndims_out-length_inner)) | |
outer = vcat(collect(outer), ones(Int,ndims_out-length_outer)) | |
size_in = size(A) | |
size_out = ntuple(i->inner[i]*size(A,i)*outer[i],ndims_out)::Dims | |
inner_size_out = ntuple(i->inner[i]*size(A,i),ndims_out)::Dims | |
indices_in = Vector{Int}(ndims_in) | |
indices_out = Vector{Int}(ndims_out) | |
length_out = prod(size_out) | |
R = similar(A, size_out) | |
for index_out in 1:length_out | |
ind2sub!(indices_out, size_out, index_out) | |
for t in 1:ndims_in | |
# "Project" outer repetitions into inner repetitions | |
indices_in[t] = mod1(indices_out[t], inner_size_out[t]) | |
# Find inner repetitions using flooring division | |
indices_in[t] = fld1(indices_in[t], inner[t]) | |
end | |
index_in = sub2ind(size_in, indices_in...) | |
R[index_out] = A[index_in] | |
end | |
return R | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## array.jl: Dense arrays | |
## Type aliases for convenience ## | |
typealias AbstractVector{T} AbstractArray{T,1} | |
typealias AbstractMatrix{T} AbstractArray{T,2} | |
typealias AbstractVecOrMat{T} Union{AbstractVector{T}, AbstractMatrix{T}} | |
typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}, Colon} | |
typealias DimOrInd Union{Integer, AbstractUnitRange} | |
typealias IntOrInd Union{Int, AbstractUnitRange} | |
typealias DimsOrInds{N} NTuple{N,DimOrInd} | |
typealias NeedsShaping Union{Tuple{Integer,Vararg{Integer}}, Tuple{OneTo,Vararg{OneTo}}} | |
typealias Vector{T} Array{T,1} | |
typealias Matrix{T} Array{T,2} | |
typealias VecOrMat{T} Union{Vector{T}, Matrix{T}} | |
typealias DenseVector{T} DenseArray{T,1} | |
typealias DenseMatrix{T} DenseArray{T,2} | |
typealias DenseVecOrMat{T} Union{DenseVector{T}, DenseMatrix{T}} | |
## Basic functions ## | |
import Core: arraysize, arrayset, arrayref | |
vect() = Array{Any,1}(0) | |
vect{T}(X::T...) = T[ X[i] for i=1:length(X) ] | |
function vect(X...) | |
T = promote_typeof(X...) | |
#T[ X[i] for i=1:length(X) ] | |
# TODO: this is currently much faster. should figure out why. not clear. | |
return copy!(Array{T,1}(length(X)), X) | |
end | |
size(a::Array, d) = arraysize(a, d) | |
size(a::Vector) = (arraysize(a,1),) | |
size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) | |
size(a::Array) = (@_inline_meta; _size((), a)) | |
_size{_,N}(out::NTuple{N}, A::Array{_,N}) = out | |
function _size{_,M,N}(out::NTuple{M}, A::Array{_,N}) | |
@_inline_meta | |
_size((out..., size(A,M+1)), A) | |
end | |
asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) | |
length(a::Array) = arraylen(a) | |
elsize{T}(a::Array{T}) = isbits(T) ? sizeof(T) : sizeof(Ptr) | |
sizeof(a::Array) = elsize(a) * length(a) | |
function isassigned{T}(a::Array{T}, i::Int...) | |
ii = sub2ind(size(a), i...) | |
1 <= ii <= length(a) || return false | |
ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii-1) == 1 | |
end | |
## copy ## | |
function unsafe_copy!{T}(dest::Ptr{T}, src::Ptr{T}, n) | |
# Do not use this to copy data between pointer arrays. | |
# It can't be made safe no matter how carefully you checked. | |
ccall(:memmove, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), | |
dest, src, n*sizeof(T)) | |
return dest | |
end | |
function unsafe_copy!{T}(dest::Array{T}, doffs, src::Array{T}, soffs, n) | |
if isbits(T) | |
unsafe_copy!(pointer(dest, doffs), pointer(src, soffs), n) | |
else | |
ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), | |
dest, pointer(dest, doffs), src, pointer(src, soffs), n) | |
end | |
return dest | |
end | |
function copy!{T}(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) | |
n == 0 && return dest | |
n > 0 || throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) | |
if soffs < 1 || doffs < 1 || soffs+n-1 > length(src) || doffs+n-1 > length(dest) | |
throw(BoundsError()) | |
end | |
unsafe_copy!(dest, doffs, src, soffs, n) | |
end | |
copy!{T}(dest::Array{T}, src::Array{T}) = copy!(dest, 1, src, 1, length(src)) | |
copy{T<:Array}(a::T) = ccall(:jl_array_copy, Ref{T}, (Any,), a) | |
function reinterpret{T,S}(::Type{T}, a::Array{S,1}) | |
nel = Int(div(length(a)*sizeof(S),sizeof(T))) | |
# TODO: maybe check that remainder is zero? | |
return reinterpret(T, a, (nel,)) | |
end | |
function reinterpret{T,S}(::Type{T}, a::Array{S}) | |
if sizeof(S) != sizeof(T) | |
throw(ArgumentError("result shape not specified")) | |
end | |
reinterpret(T, a, size(a)) | |
end | |
function reinterpret{T,S,N}(::Type{T}, a::Array{S}, dims::NTuple{N,Int}) | |
if !isbits(T) | |
throw(ArgumentError("cannot reinterpret Array{$(S)} to ::Type{Array{$(T)}}, type $(T) is not a bitstype")) | |
end | |
if !isbits(S) | |
throw(ArgumentError("cannot reinterpret Array{$(S)} to ::Type{Array{$(T)}}, type $(S) is not a bitstype")) | |
end | |
nel = div(length(a)*sizeof(S),sizeof(T)) | |
if prod(dims) != nel | |
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(nel)")) | |
end | |
ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims) | |
end | |
# reshaping to same # of dimensions | |
function reshape{T,N}(a::Array{T,N}, dims::NTuple{N,Int}) | |
if prod(dims) != length(a) | |
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(a))")) | |
end | |
if dims == size(a) | |
return a | |
end | |
ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims) | |
end | |
# reshaping to different # of dimensions | |
function reshape{T,N}(a::Array{T}, dims::NTuple{N,Int}) | |
if prod(dims) != length(a) | |
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(a))")) | |
end | |
ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims) | |
end | |
## Constructors ## | |
similar{T}(a::Array{T,1}) = Array{T,1}(size(a,1)) | |
similar{T}(a::Array{T,2}) = Array{T,2}(size(a,1), size(a,2)) | |
similar{T}(a::Array{T,1}, S::Type) = Array{S,1}(size(a,1)) | |
similar{T}(a::Array{T,2}, S::Type) = Array{S,2}(size(a,1), size(a,2)) | |
similar{T}(a::Array{T}, m::Int) = Array{T,1}(m) | |
similar{N}(a::Array, T::Type, dims::Dims{N}) = Array{T,N}(dims) | |
similar{T,N}(a::Array{T}, dims::Dims{N}) = Array{T,N}(dims) | |
# T[x...] constructs Array{T,1} | |
function getindex{T}(::Type{T}, vals...) | |
a = Array{T,1}(length(vals)) | |
@inbounds for i = 1:length(vals) | |
a[i] = vals[i] | |
end | |
return a | |
end | |
getindex{T}(::Type{T}) = (@_inline_meta; Array{T,1}(0)) | |
getindex{T}(::Type{T}, x) = (@_inline_meta; a = Array{T,1}(1); @inbounds a[1] = x; a) | |
getindex{T}(::Type{T}, x, y) = (@_inline_meta; a = Array{T,1}(2); @inbounds (a[1] = x; a[2] = y); a) | |
getindex{T}(::Type{T}, x, y, z) = (@_inline_meta; a = Array{T,1}(3); @inbounds (a[1] = x; a[2] = y; a[3] = z); a) | |
function getindex(::Type{Any}, vals::ANY...) | |
a = Array{Any,1}(length(vals)) | |
@inbounds for i = 1:length(vals) | |
a[i] = vals[i] | |
end | |
return a | |
end | |
getindex(::Type{Any}) = Array{Any,1}(0) | |
function fill!(a::Union{Array{UInt8}, Array{Int8}}, x::Integer) | |
ccall(:memset, Ptr{Void}, (Ptr{Void}, Cint, Csize_t), a, x, length(a)) | |
return a | |
end | |
function fill!{T<:Union{Integer,AbstractFloat}}(a::Array{T}, x) | |
xT = convert(T, x) | |
for i in eachindex(a) | |
@inbounds a[i] = xT | |
end | |
return a | |
end | |
""" | |
fill(x, dims) | |
Create an array filled with the value `x`. For example, `fill(1.0, (5,5))` returns a 5×5 | |
array of floats, with each element initialized to `1.0`. | |
```jldoctest | |
julia> fill(1.0, (5,5)) | |
5×5 Array{Float64,2}: | |
1.0 1.0 1.0 1.0 1.0 | |
1.0 1.0 1.0 1.0 1.0 | |
1.0 1.0 1.0 1.0 1.0 | |
1.0 1.0 1.0 1.0 1.0 | |
1.0 1.0 1.0 1.0 1.0 | |
``` | |
If `x` is an object reference, all elements will refer to the same object. `fill(Foo(), | |
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) | |
for (fname, felt) in ((:zeros,:zero), (:ones,:one)) | |
@eval begin | |
($fname)(T::Type, dims...) = fill!(Array{T}(dims...), ($felt)(T)) | |
($fname)(dims...) = fill!(Array{Float64}(dims...), ($felt)(Float64)) | |
($fname){T}(A::AbstractArray{T}) = fill!(similar(A), ($felt)(T)) | |
end | |
end | |
""" | |
eye([T::Type=Float64,] m::Integer, n::Integer) | |
`m`-by-`n` identity matrix. | |
The default element type is `Float64`. | |
""" | |
function eye(T::Type, m::Integer, n::Integer) | |
a = zeros(T,m,n) | |
for i = 1:min(m,n) | |
a[i,i] = one(T) | |
end | |
return a | |
end | |
""" | |
eye(m, n) | |
`m`-by-`n` identity matrix. | |
""" | |
eye(m::Integer, n::Integer) = eye(Float64, m, n) | |
eye(T::Type, n::Integer) = eye(T, n, n) | |
""" | |
eye([T::Type=Float64,] n::Integer) | |
`n`-by-`n` identity matrix. | |
The default element type is `Float64`. | |
""" | |
eye(n::Integer) = eye(Float64, n) | |
""" | |
eye(A) | |
Constructs an identity matrix of the same dimensions and type as `A`. | |
```jldoctest | |
julia> A = [1 2 3; 4 5 6; 7 8 9] | |
3×3 Array{Int64,2}: | |
1 2 3 | |
4 5 6 | |
7 8 9 | |
julia> eye(A) | |
3×3 Array{Int64,2}: | |
1 0 0 | |
0 1 0 | |
0 0 1 | |
``` | |
Note the difference from [`ones`](:func:`ones`). | |
""" | |
eye{T}(x::AbstractMatrix{T}) = eye(T, size(x, 1), size(x, 2)) | |
function one{T}(x::AbstractMatrix{T}) | |
m,n = size(x) | |
m==n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) | |
eye(T, m) | |
end | |
## Conversions ## | |
convert{T}(::Type{Vector}, x::AbstractVector{T}) = convert(Vector{T}, x) | |
convert{T}(::Type{Matrix}, x::AbstractMatrix{T}) = convert(Matrix{T}, x) | |
convert{T,n}(::Type{Array{T}}, x::Array{T,n}) = x | |
convert{T,n}(::Type{Array{T,n}}, x::Array{T,n}) = x | |
convert{T,n,S}(::Type{Array{T}}, x::AbstractArray{S, n}) = convert(Array{T, n}, x) | |
convert{T,n,S}(::Type{Array{T,n}}, x::AbstractArray{S,n}) = copy!(Array{T,n}(size(x)), x) | |
promote_rule{T,n,S}(::Type{Array{T,n}}, ::Type{Array{S,n}}) = Array{promote_type(T,S),n} | |
## copying iterators to containers | |
""" | |
collect(element_type, collection) | |
Return an `Array` with the given element type of all items in a collection or iterable. | |
The result has the same shape and number of dimensions as `collection`. | |
""" | |
collect{T}(::Type{T}, itr) = _collect(T, itr, iteratorsize(itr)) | |
_collect{T}(::Type{T}, itr, isz::HasLength) = copy!(Array{T,1}(Int(length(itr)::Integer)), itr) | |
_collect{T}(::Type{T}, itr, isz::HasShape) = copy!(similar(Array{T}, indices(itr)), itr) | |
function _collect{T}(::Type{T}, itr, isz::SizeUnknown) | |
a = Array{T,1}(0) | |
for x in itr | |
push!(a,x) | |
end | |
return a | |
end | |
# make a collection similar to `c` and appropriate for collecting `itr` | |
_similar_for(c::AbstractArray, T, itr, ::SizeUnknown) = similar(c, T, 0) | |
_similar_for(c::AbstractArray, T, itr, ::HasLength) = similar(c, T, Int(length(itr)::Integer)) | |
_similar_for(c::AbstractArray, T, itr, ::HasShape) = similar(c, T, indices(itr)) | |
_similar_for(c, T, itr, isz) = similar(c, T) | |
""" | |
collect(collection) | |
Return an `Array` of all items in a collection or iterator. For associative collections, returns | |
`Pair{KeyType, ValType}`. If the argument is array-like or is an iterator with the `HasShape()` | |
trait, the result will have the same shape and number of dimensions as the argument. | |
```jldoctest | |
julia> collect(1:2:13) | |
7-element Array{Int64,1}: | |
1 | |
3 | |
5 | |
7 | |
9 | |
11 | |
13 | |
``` | |
""" | |
collect(itr) = _collect(1:1 #= Array =#, itr, iteratoreltype(itr), iteratorsize(itr)) | |
collect_similar(cont, itr) = _collect(cont, itr, iteratoreltype(itr), iteratorsize(itr)) | |
_collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) = | |
copy!(_similar_for(cont, eltype(itr), itr, isz), itr) | |
function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) | |
a = _similar_for(cont, eltype(itr), itr, isz) | |
for x in itr | |
push!(a,x) | |
end | |
return a | |
end | |
if isdefined(Core, :Inference) | |
_default_eltype(itrt::ANY) = Core.Inference.return_type(first, Tuple{itrt}) | |
else | |
_default_eltype(itr::ANY) = Any | |
end | |
_array_for{T}(::Type{T}, itr, ::HasLength) = Array{T,1}(Int(length(itr)::Integer)) | |
_array_for{T}(::Type{T}, itr, ::HasShape) = similar(Array{T}, indices(itr)) | |
function collect(itr::Generator) | |
isz = iteratorsize(itr.iter) | |
et = _default_eltype(typeof(itr)) | |
if isa(isz, SizeUnknown) | |
return grow_to!(Array{et,1}(0), itr) | |
else | |
st = start(itr) | |
if done(itr,st) | |
return _array_for(et, itr.iter, isz) | |
end | |
v1, st = next(itr, st) | |
collect_to_with_first!(_array_for(typeof(v1), itr.iter, isz), v1, itr, st) | |
end | |
end | |
_collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = | |
grow_to!(_similar_for(c, _default_eltype(typeof(itr)), itr, isz), itr) | |
function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) | |
st = start(itr) | |
if done(itr,st) | |
return _similar_for(c, _default_eltype(typeof(itr)), itr, isz) | |
end | |
v1, st = next(itr, st) | |
collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st) | |
end | |
function collect_to_with_first!(dest::AbstractArray, v1, itr, st) | |
i1 = first(linearindices(dest)) | |
dest[i1] = v1 | |
return collect_to!(dest, itr, i1+1, st) | |
end | |
function collect_to_with_first!(dest, v1, itr, st) | |
push!(dest, v1) | |
return grow_to!(dest, itr, st) | |
end | |
function collect_to!{T}(dest::AbstractArray{T}, itr, offs, st) | |
# collect to dest array, checking the type of each result. if a result does not | |
# match, widen the result type and re-dispatch. | |
i = offs | |
while !done(itr, st) | |
el, st = next(itr, st) | |
S = typeof(el) | |
if S === T || S <: T | |
@inbounds dest[i] = el::T | |
i += 1 | |
else | |
R = typejoin(T, S) | |
new = similar(dest, R) | |
copy!(new,1, dest,1, i-1) | |
@inbounds new[i] = el | |
return collect_to!(new, itr, i+1, st) | |
end | |
end | |
return dest | |
end | |
function grow_to!(dest, itr) | |
out = grow_to!(similar(dest,Union{}), itr, start(itr)) | |
return isempty(out) ? dest : out | |
end | |
function grow_to!(dest, itr, st) | |
T = eltype(dest) | |
while !done(itr, st) | |
el, st = next(itr, st) | |
S = typeof(el) | |
if S === T || S <: T | |
push!(dest, el::T) | |
else | |
new = similar(dest, typejoin(T, S)) | |
copy!(new, dest) | |
push!(new, el) | |
return grow_to!(new, itr, st) | |
end | |
end | |
return dest | |
end | |
## Iteration ## | |
start(A::Array) = 1 | |
next(a::Array,i) = (@_propagate_inbounds_meta; (a[i],i+1)) | |
done(a::Array,i) = (@_inline_meta; i == length(a)+1) | |
## Indexing: getindex ## | |
# This is more complicated than it needs to be in order to get Win64 through bootstrap | |
getindex(A::Array, i1::Real) = arrayref(A, to_index(i1)) | |
getindex(A::Array, i1::Real, i2::Real, I::Real...) = (@_inline_meta; arrayref(A, to_index(i1), to_index(i2), to_indexes(I...)...)) # TODO: REMOVE FOR #14770 | |
# Faster contiguous indexing using copy! for UnitRange and Colon | |
function getindex(A::Array, I::UnitRange{Int}) | |
@_inline_meta | |
@boundscheck checkbounds(A, I) | |
lI = length(I) | |
X = similar(A, lI) | |
if lI > 0 | |
unsafe_copy!(X, 1, A, first(I), lI) | |
end | |
return X | |
end | |
function getindex(A::Array, c::Colon) | |
lI = length(A) | |
X = similar(A, lI) | |
if lI > 0 | |
unsafe_copy!(X, 1, A, 1, lI) | |
end | |
return X | |
end | |
# This is redundant with the abstract fallbacks, but needed for bootstrap | |
function getindex{S,T<:Real}(A::Array{S}, I::Range{T}) | |
return S[ A[to_index(i)] for i in I ] | |
end | |
## Indexing: setindex! ## | |
setindex!{T}(A::Array{T}, x, i1::Real) = arrayset(A, convert(T,x)::T, to_index(i1)) | |
setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, I::Real...) = arrayset(A, convert(T,x)::T, to_index(i1), to_index(i2), to_indexes(I...)...) # TODO: REMOVE FOR #14770 | |
# These are redundant with the abstract fallbacks but needed for bootstrap | |
function setindex!(A::Array, x, I::AbstractVector{Int}) | |
A === I && (I = copy(I)) | |
for i in I | |
A[i] = x | |
end | |
return A | |
end | |
function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) | |
setindex_shape_check(X, length(I)) | |
count = 1 | |
if X === A | |
X = copy(X) | |
I===A && (I = X::typeof(I)) | |
elseif I === A | |
I = copy(I) | |
end | |
for i in I | |
A[i] = X[count] | |
count += 1 | |
end | |
return A | |
end | |
# Faster contiguous setindex! with copy! | |
function setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int}) | |
@_inline_meta | |
@boundscheck checkbounds(A, I) | |
lI = length(I) | |
setindex_shape_check(X, lI) | |
if lI > 0 | |
unsafe_copy!(A, first(I), X, 1, lI) | |
end | |
return A | |
end | |
function setindex!{T}(A::Array{T}, X::Array{T}, c::Colon) | |
lI = length(A) | |
setindex_shape_check(X, lI) | |
if lI > 0 | |
unsafe_copy!(A, 1, X, 1, lI) | |
end | |
return A | |
end | |
setindex!(A::Array, x::Number, ::Colon) = fill!(A, x) | |
setindex!{T, N}(A::Array{T, N}, x::Number, ::Vararg{Colon, N}) = fill!(A, x) | |
# efficiently grow an array | |
_growat!(a::Vector, i::Integer, delta::Integer) = | |
ccall(:jl_array_grow_at, Void, (Any, Int, UInt), a, i - 1, delta) | |
# efficiently delete part of an array | |
_deleteat!(a::Vector, i::Integer, delta::Integer) = | |
ccall(:jl_array_del_at, Void, (Any, Int, UInt), a, i - 1, delta) | |
## Dequeue functionality ## | |
function push!{T}(a::Array{T,1}, item) | |
# convert first so we don't grow the array if the assignment won't work | |
itemT = convert(T, item) | |
ccall(:jl_array_grow_end, Void, (Any, UInt), a, 1) | |
a[end] = itemT | |
return a | |
end | |
function push!(a::Array{Any,1}, item::ANY) | |
ccall(:jl_array_grow_end, Void, (Any, UInt), a, 1) | |
arrayset(a, item, length(a)) | |
return a | |
end | |
function append!{T}(a::Array{T,1}, items::AbstractVector) | |
n = length(items) | |
ccall(:jl_array_grow_end, Void, (Any, UInt), a, n) | |
copy!(a, length(a)-n+1, items, 1, n) | |
return a | |
end | |
""" | |
prepend!(a::Vector, items) -> collection | |
Insert the elements of `items` to the beginning of `a`. | |
```jldoctest | |
julia> prepend!([3],[1,2]) | |
3-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
``` | |
""" | |
function prepend!{T}(a::Array{T,1}, items::AbstractVector) | |
n = length(items) | |
ccall(:jl_array_grow_beg, Void, (Any, UInt), a, n) | |
if a === items | |
copy!(a, 1, items, n+1, n) | |
else | |
copy!(a, 1, items, 1, n) | |
end | |
return a | |
end | |
""" | |
resize!(a::Vector, n::Integer) -> Vector | |
Resize `a` to contain `n` elements. If `n` is smaller than the current collection | |
length, the first `n` elements will be retained. If `n` is larger, the new elements are not | |
guaranteed to be initialized. | |
```jldoctest | |
julia> resize!([6, 5, 4, 3, 2, 1], 3) | |
3-element Array{Int64,1}: | |
6 | |
5 | |
4 | |
``` | |
```julia | |
julia> resize!([6, 5, 4, 3, 2, 1], 8) | |
8-element Array{Int64,1}: | |
6 | |
5 | |
4 | |
3 | |
2 | |
1 | |
0 | |
0 | |
``` | |
""" | |
function resize!(a::Vector, nl::Integer) | |
l = length(a) | |
if nl > l | |
ccall(:jl_array_grow_end, Void, (Any, UInt), a, nl-l) | |
else | |
if nl < 0 | |
throw(ArgumentError("new length must be ≥ 0")) | |
end | |
ccall(:jl_array_del_end, Void, (Any, UInt), a, l-nl) | |
end | |
return a | |
end | |
function sizehint!(a::Vector, sz::Integer) | |
ccall(:jl_array_sizehint, Void, (Any, UInt), a, sz) | |
a | |
end | |
function pop!(a::Vector) | |
if isempty(a) | |
throw(ArgumentError("array must be non-empty")) | |
end | |
item = a[end] | |
ccall(:jl_array_del_end, Void, (Any, UInt), a, 1) | |
return item | |
end | |
""" | |
unshift!(collection, items...) -> collection | |
Insert one or more `items` at the beginning of `collection`. | |
```jldoctest | |
julia> unshift!([1, 2, 3, 4], 5, 6) | |
6-element Array{Int64,1}: | |
5 | |
6 | |
1 | |
2 | |
3 | |
4 | |
``` | |
""" | |
function unshift!{T}(a::Array{T,1}, item) | |
item = convert(T, item) | |
ccall(:jl_array_grow_beg, Void, (Any, UInt), a, 1) | |
a[1] = item | |
return a | |
end | |
function shift!(a::Vector) | |
if isempty(a) | |
throw(ArgumentError("array must be non-empty")) | |
end | |
item = a[1] | |
ccall(:jl_array_del_beg, Void, (Any, UInt), a, 1) | |
return item | |
end | |
""" | |
insert!(a::Vector, index::Integer, item) | |
Insert an `item` into `a` at the given `index`. `index` is the index of `item` in | |
the resulting `a`. | |
```jldoctest | |
julia> insert!([6, 5, 4, 2, 1], 4, 3) | |
6-element Array{Int64,1}: | |
6 | |
5 | |
4 | |
3 | |
2 | |
1 | |
``` | |
""" | |
function insert!{T}(a::Array{T,1}, i::Integer, item) | |
# Throw convert error before changing the shape of the array | |
_item = convert(T, item) | |
_growat!(a, i, 1) | |
# _growat! already did bound check | |
@inbounds a[i] = _item | |
return a | |
end | |
""" | |
deleteat!(a::Vector, i::Integer) | |
Remove the item at the given `i` and return the modified `a`. Subsequent items | |
are shifted to fill the resulting gap. | |
```jldoctest | |
julia> deleteat!([6, 5, 4, 3, 2, 1], 2) | |
5-element Array{Int64,1}: | |
6 | |
4 | |
3 | |
2 | |
1 | |
``` | |
""" | |
deleteat!(a::Vector, i::Integer) = (_deleteat!(a, i, 1); a) | |
function deleteat!{T<:Integer}(a::Vector, r::UnitRange{T}) | |
n = length(a) | |
isempty(r) || _deleteat!(a, first(r), length(r)) | |
return a | |
end | |
""" | |
deleteat!(a::Vector, inds) | |
Remove the items at the indices given by `inds`, and return the modified `a`. | |
Subsequent items are shifted to fill the resulting gap. `inds` must be sorted and unique. | |
```jldoctest | |
julia> deleteat!([6, 5, 4, 3, 2, 1], 1:2:5) | |
3-element Array{Int64,1}: | |
5 | |
3 | |
1 | |
julia> deleteat!([6, 5, 4, 3, 2, 1], (2, 2)) | |
ERROR: ArgumentError: indices must be unique and sorted | |
in deleteat!(::Array{Int64,1}, ::Tuple{Int64,Int64}) at ./array.jl:727 | |
... | |
``` | |
""" | |
function deleteat!(a::Vector, inds) | |
n = length(a) | |
s = start(inds) | |
done(inds, s) && return a | |
(p, s) = next(inds, s) | |
q = p+1 | |
while !done(inds, s) | |
(i,s) = next(inds, s) | |
if !(q <= i <= n) | |
if i < q | |
throw(ArgumentError("indices must be unique and sorted")) | |
else | |
throw(BoundsError()) | |
end | |
end | |
while q < i | |
@inbounds a[p] = a[q] | |
p += 1; q += 1 | |
end | |
q = i+1 | |
end | |
while q <= n | |
@inbounds a[p] = a[q] | |
p += 1; q += 1 | |
end | |
ccall(:jl_array_del_end, Void, (Any, UInt), a, n-p+1) | |
return a | |
end | |
const _default_splice = [] | |
""" | |
splice!(a::Vector, index::Integer, [replacement]) -> item | |
Remove the item at the given index, and return the removed item. | |
Subsequent items are shifted left to fill the resulting gap. | |
If specified, replacement values from an ordered | |
collection will be spliced in place of the removed item. | |
```jldoctest | |
julia> A = [6, 5, 4, 3, 2, 1]; splice!(A, 5) | |
2 | |
julia> A | |
5-element Array{Int64,1}: | |
6 | |
5 | |
4 | |
3 | |
1 | |
julia> splice!(A, 5, -1) | |
1 | |
julia> A | |
5-element Array{Int64,1}: | |
6 | |
5 | |
4 | |
3 | |
-1 | |
julia> splice!(A, 1, [-1, -2, -3]) | |
6 | |
julia> A | |
7-element Array{Int64,1}: | |
-1 | |
-2 | |
-3 | |
5 | |
4 | |
3 | |
-1 | |
``` | |
To insert `replacement` before an index `n` without removing any items, use | |
`splice!(collection, n:n-1, replacement)`. | |
""" | |
function splice!(a::Vector, i::Integer, ins=_default_splice) | |
v = a[i] | |
m = length(ins) | |
if m == 0 | |
_deleteat!(a, i, 1) | |
elseif m == 1 | |
a[i] = ins[1] | |
else | |
_growat!(a, i, m-1) | |
k = 1 | |
for x in ins | |
a[i+k-1] = x | |
k += 1 | |
end | |
end | |
return v | |
end | |
""" | |
splice!(a::Vector, range, [replacement]) -> items | |
Remove items in the specified index range, and return a collection containing | |
the removed items. | |
Subsequent items are shifted left to fill the resulting gap. | |
If specified, replacement values from an ordered collection will be spliced in | |
place of the removed items. | |
To insert `replacement` before an index `n` without removing any items, use | |
`splice!(collection, n:n-1, replacement)`. | |
```jldoctest | |
julia> splice!(A, 4:3, 2) | |
0-element Array{Int64,1} | |
julia> A | |
8-element Array{Int64,1}: | |
-1 | |
-2 | |
-3 | |
2 | |
5 | |
4 | |
3 | |
-1 | |
``` | |
""" | |
function splice!{T<:Integer}(a::Vector, r::UnitRange{T}, ins=_default_splice) | |
v = a[r] | |
m = length(ins) | |
if m == 0 | |
deleteat!(a, r) | |
return v | |
end | |
n = length(a) | |
f = first(r) | |
l = last(r) | |
d = length(r) | |
if m < d | |
delta = d - m | |
_deleteat!(a, (f - 1 < n - l) ? f : (l - delta + 1), delta) | |
elseif m > d | |
_growat!(a, (f - 1 < n - l) ? f : (l + 1), m - d) | |
end | |
k = 1 | |
for x in ins | |
a[f+k-1] = x | |
k += 1 | |
end | |
return v | |
end | |
function empty!(a::Vector) | |
ccall(:jl_array_del_end, Void, (Any, UInt), a, length(a)) | |
return a | |
end | |
# use memcmp for lexcmp on byte arrays | |
function lexcmp(a::Array{UInt8,1}, b::Array{UInt8,1}) | |
c = ccall(:memcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}, UInt), | |
a, b, min(length(a),length(b))) | |
return c < 0 ? -1 : c > 0 ? +1 : cmp(length(a),length(b)) | |
end | |
# use memcmp for == on bit integer types | |
function =={T<:BitInteger,N}(a::Array{T,N}, b::Array{T,N}) | |
size(a) == size(b) && 0 == ccall( | |
:memcmp, Int32, (Ptr{T}, Ptr{T}, UInt), a, b, sizeof(T) * length(a)) | |
end | |
# this is ~20% faster than the generic implementation above for very small arrays | |
function =={T<:BitInteger}(a::Array{T,1}, b::Array{T,1}) | |
len = length(a) | |
len == length(b) && 0 == ccall( | |
:memcmp, Int32, (Ptr{T}, Ptr{T}, UInt), a, b, sizeof(T) * len) | |
end | |
function reverse(A::AbstractVector, s=1, n=length(A)) | |
B = similar(A) | |
for i = 1:s-1 | |
B[i] = A[i] | |
end | |
for i = s:n | |
B[i] = A[n+s-i] | |
end | |
for i = n+1:length(A) | |
B[i] = A[i] | |
end | |
return B | |
end | |
reverseind(a::AbstractVector, i::Integer) = length(a) + 1 - i | |
function reverse!(v::AbstractVector, s=1, n=length(v)) | |
if n <= s # empty case; ok | |
elseif !(1 ≤ s ≤ endof(v)) | |
throw(BoundsError(v, s)) | |
elseif !(1 ≤ n ≤ endof(v)) | |
throw(BoundsError(v, n)) | |
end | |
r = n | |
@inbounds for i in s:div(s+n-1, 2) | |
v[i], v[r] = v[r], v[i] | |
r -= 1 | |
end | |
return v | |
end | |
# concatenations of homogeneous combinations of vectors, horizontal and vertical | |
function hcat{T}(V::Vector{T}...) | |
height = length(V[1]) | |
for j = 2:length(V) | |
if length(V[j]) != height | |
throw(DimensionMismatch("vectors must have same lengths")) | |
end | |
end | |
return [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] | |
end | |
function vcat{T}(arrays::Vector{T}...) | |
n = 0 | |
for a in arrays | |
n += length(a) | |
end | |
arr = Array{T,1}(n) | |
ptr = pointer(arr) | |
if isbits(T) | |
elsz = Core.sizeof(T) | |
else | |
elsz = Core.sizeof(Ptr{Void}) | |
end | |
for a in arrays | |
na = length(a) | |
nba = na * elsz | |
if isbits(T) | |
ccall(:memcpy, Ptr{Void}, (Ptr{Void}, Ptr{Void}, UInt), | |
ptr, a, nba) | |
else | |
ccall(:jl_array_ptr_copy, Void, (Any, Ptr{Void}, Any, Ptr{Void}, Int), | |
arr, ptr, a, pointer(a), na) | |
end | |
ptr += nba | |
end | |
return arr | |
end | |
## find ## | |
""" | |
findnext(A, i::Integer) | |
Find the next linear index >= `i` of a non-zero element of `A`, or `0` if not found. | |
```jldoctest | |
julia> A = [0 0; 1 0] | |
2×2 Array{Int64,2}: | |
0 0 | |
1 0 | |
julia> findnext(A,1) | |
2 | |
julia> findnext(A,3) | |
0 | |
``` | |
""" | |
function findnext(A, start::Integer) | |
for i = start:length(A) | |
if A[i] != 0 | |
return i | |
end | |
end | |
return 0 | |
end | |
""" | |
findfirst(A) | |
Return the linear index of the first non-zero value in `A` (determined by `A[i]!=0`). | |
Returns `0` if no such value is found. | |
```jldoctest | |
julia> A = [0 0; 1 0] | |
2×2 Array{Int64,2}: | |
0 0 | |
1 0 | |
julia> findfirst(A) | |
2 | |
``` | |
""" | |
findfirst(A) = findnext(A, 1) | |
""" | |
findnext(A, v, i::Integer) | |
Find the next linear index >= `i` of an element of `A` equal to `v` (using `==`), or `0` if not found. | |
```jldoctest | |
julia> A = [1 4; 2 2] | |
2×2 Array{Int64,2}: | |
1 4 | |
2 2 | |
julia> findnext(A,4,4) | |
0 | |
julia> findnext(A,4,3) | |
3 | |
``` | |
""" | |
function findnext(A, v, start::Integer) | |
for i = start:length(A) | |
if A[i] == v | |
return i | |
end | |
end | |
return 0 | |
end | |
""" | |
findfirst(A, v) | |
Return the linear index of the first element equal to `v` in `A`. | |
Returns `0` if `v` is not found. | |
```jldoctest | |
julia> A = [4 6; 2 2] | |
2×2 Array{Int64,2}: | |
4 6 | |
2 2 | |
julia> findfirst(A,2) | |
2 | |
julia> findfirst(A,3) | |
0 | |
``` | |
""" | |
findfirst(A, v) = findnext(A, v, 1) | |
""" | |
findnext(predicate::Function, A, i::Integer) | |
Find the next linear index >= `i` of an element of `A` for which `predicate` returns `true`, or `0` if not found. | |
```jldoctest | |
julia> A = [1 4; 2 2] | |
2×2 Array{Int64,2}: | |
1 4 | |
2 2 | |
julia> findnext(isodd, A, 1) | |
1 | |
julia> findnext(isodd, A, 2) | |
0 | |
``` | |
""" | |
function findnext(testf::Function, A, start::Integer) | |
for i = start:length(A) | |
if testf(A[i]) | |
return i | |
end | |
end | |
return 0 | |
end | |
""" | |
findfirst(predicate::Function, A) | |
Return the linear index of the first element of `A` for which `predicate` returns `true`. | |
Returns `0` if there is no such element. | |
```jldoctest | |
julia> A = [1 4; 2 2] | |
2×2 Array{Int64,2}: | |
1 4 | |
2 2 | |
julia> findfirst(iseven, A) | |
2 | |
julia> findfirst(x -> x>10, A) | |
0 | |
``` | |
""" | |
findfirst(testf::Function, A) = findnext(testf, A, 1) | |
""" | |
findprev(A, i::Integer) | |
Find the previous linear index <= `i` of a non-zero element of `A`, or `0` if not found. | |
```jldoctest | |
julia> A = [0 0; 1 2] | |
2×2 Array{Int64,2}: | |
0 0 | |
1 2 | |
julia> findprev(A,2) | |
2 | |
julia> findprev(A,1) | |
0 | |
``` | |
""" | |
function findprev(A, start::Integer) | |
for i = start:-1:1 | |
A[i] != 0 && return i | |
end | |
return 0 | |
end | |
""" | |
findlast(A) | |
Return the linear index of the last non-zero value in `A` (determined by `A[i]!=0`). | |
Returns `0` if there is no non-zero value in `A`. | |
```jldoctest | |
julia> A = [1 0; 1 0] | |
2×2 Array{Int64,2}: | |
1 0 | |
1 0 | |
julia> findlast(A) | |
2 | |
julia> A = zeros(2,2) | |
2×2 Array{Float64,2}: | |
0.0 0.0 | |
0.0 0.0 | |
julia> findlast(A) | |
0 | |
``` | |
""" | |
findlast(A) = findprev(A, length(A)) | |
""" | |
findprev(A, v, i::Integer) | |
Find the previous linear index <= `i` of an element of `A` equal to `v` (using `==`), or `0` if not found. | |
```jldoctest | |
julia> A = [0 0; 1 2] | |
2×2 Array{Int64,2}: | |
0 0 | |
1 2 | |
julia> findprev(A, 1, 4) | |
2 | |
julia> findprev(A, 1, 1) | |
0 | |
``` | |
""" | |
function findprev(A, v, start::Integer) | |
for i = start:-1:1 | |
A[i] == v && return i | |
end | |
return 0 | |
end | |
""" | |
findlast(A, v) | |
Return the linear index of the last element equal to `v` in `A`. | |
Returns `0` if there is no element of `A` equal to `v`. | |
```jldoctest | |
julia> A = [1 2; 2 1] | |
2×2 Array{Int64,2}: | |
1 2 | |
2 1 | |
julia> findlast(A,1) | |
4 | |
julia> findlast(A,2) | |
3 | |
julia> findlast(A,3) | |
0 | |
``` | |
""" | |
findlast(A, v) = findprev(A, v, length(A)) | |
""" | |
findprev(predicate::Function, A, i::Integer) | |
Find the previous linear index <= `i` of an element of `A` for which `predicate` returns `true`, or | |
`0` if not found. | |
```jldoctest | |
julia> A = [4 6; 1 2] | |
2×2 Array{Int64,2}: | |
4 6 | |
1 2 | |
julia> findprev(isodd, A, 1) | |
0 | |
julia> findprev(isodd, A, 3) | |
2 | |
``` | |
""" | |
function findprev(testf::Function, A, start::Integer) | |
for i = start:-1:1 | |
testf(A[i]) && return i | |
end | |
return 0 | |
end | |
""" | |
findlast(predicate::Function, A) | |
Return the linear index of the last element of `A` for which `predicate` returns `true`. | |
Returns `0` if there is no such element. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> findlast(isodd, A) | |
2 | |
julia> findlast(x -> x > 5, A) | |
0 | |
``` | |
""" | |
findlast(testf::Function, A) = findprev(testf, A, length(A)) | |
""" | |
find(f::Function, A) | |
Return a vector `I` of the linear indexes of `A` where `f(A[I])` returns `true`. | |
If there are no such elements of `A`, find returns an empty array. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> find(isodd,A) | |
2-element Array{Int64,1}: | |
1 | |
2 | |
``` | |
""" | |
function find(testf::Function, A) | |
# use a dynamic-length array to store the indexes, then copy to a non-padded | |
# array for the return | |
tmpI = Array{Int,1}(0) | |
inds = _index_remapper(A) | |
for (i,a) = enumerate(A) | |
if testf(a) | |
push!(tmpI, inds[i]) | |
end | |
end | |
I = Array{Int,1}(length(tmpI)) | |
copy!(I, tmpI) | |
return I | |
end | |
_index_remapper(A::AbstractArray) = linearindices(A) | |
_index_remapper(iter) = Colon() # safe for objects that don't implement length | |
""" | |
find(A) | |
Return a vector of the linear indexes of the non-zeros in `A` (determined by `A[i]!=0`). A | |
common use of this is to convert a boolean array to an array of indexes of the `true` | |
elements. If there are no non-zero elements of `A`, `find` returns an empty array. | |
```jldoctest | |
julia> A = [true false; false true] | |
2×2 Array{Bool,2}: | |
true false | |
false true | |
julia> find(A) | |
2-element Array{Int64,1}: | |
1 | |
4 | |
``` | |
""" | |
function find(A) | |
nnzA = countnz(A) | |
I = Vector{Int}(nnzA) | |
count = 1 | |
inds = _index_remapper(A) | |
for (i,a) in enumerate(A) | |
if a != 0 | |
I[count] = inds[i] | |
count += 1 | |
end | |
end | |
return I | |
end | |
find(x::Number) = x == 0 ? Array{Int,1}(0) : [1] | |
find(testf::Function, x::Number) = !testf(x) ? Array{Int,1}(0) : [1] | |
findn(A::AbstractVector) = find(A) | |
""" | |
findn(A) | |
Return a vector of indexes for each dimension giving the locations of the non-zeros in `A` | |
(determined by `A[i]!=0`). | |
If there are no non-zero elements of `A`, `findn` returns a 2-tuple of empty arrays. | |
```jldoctest | |
julia> A = [1 2 0; 0 0 3; 0 4 0] | |
3×3 Array{Int64,2}: | |
1 2 0 | |
0 0 3 | |
0 4 0 | |
julia> findn(A) | |
([1,1,3,2],[1,2,2,3]) | |
julia> A = zeros(2,2) | |
2×2 Array{Float64,2}: | |
0.0 0.0 | |
0.0 0.0 | |
julia> findn(A) | |
(Int64[],Int64[]) | |
``` | |
""" | |
function findn(A::AbstractMatrix) | |
nnzA = countnz(A) | |
I = similar(A, Int, nnzA) | |
J = similar(A, Int, nnzA) | |
count = 1 | |
for j=indices(A,2), i=indices(A,1) | |
if A[i,j] != 0 | |
I[count] = i | |
J[count] = j | |
count += 1 | |
end | |
end | |
return (I, J) | |
end | |
""" | |
findnz(A) | |
Return a tuple `(I, J, V)` where `I` and `J` are the row and column indexes of the non-zero | |
values in matrix `A`, and `V` is a vector of the non-zero values. | |
```jldoctest | |
julia> A = [1 2 0; 0 0 3; 0 4 0] | |
3×3 Array{Int64,2}: | |
1 2 0 | |
0 0 3 | |
0 4 0 | |
julia> findnz(A) | |
([1,1,3,2],[1,2,2,3],[1,2,4,3]) | |
``` | |
""" | |
function findnz{T}(A::AbstractMatrix{T}) | |
nnzA = countnz(A) | |
I = zeros(Int, nnzA) | |
J = zeros(Int, nnzA) | |
NZs = Array{T,1}(nnzA) | |
count = 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 | |
end | |
end | |
end | |
return (I, J, NZs) | |
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`. | |
The collection must not be empty. | |
```jldoctest | |
julia> findmax([8,0.1,-9,pi]) | |
(8.0,1) | |
julia> findmax([1,7,7,6]) | |
(7,2) | |
julia> findmax([1,7,7,NaN]) | |
(7.0,2) | |
``` | |
""" | |
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 | |
if ai > m || m!=m | |
m = ai | |
mi = i | |
end | |
end | |
return (m, mi) | |
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`. | |
The collection must not be empty. | |
```jldoctest | |
julia> findmin([8,0.1,-9,pi]) | |
(-9.0,3) | |
julia> findmin([7,1,1,6]) | |
(1,2) | |
julia> findmin([7,1,1,NaN]) | |
(1.0,2) | |
``` | |
""" | |
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 | |
if ai < m || m!=m | |
m = ai | |
mi = i | |
end | |
end | |
return (m, mi) | |
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`. | |
The collection must not be empty. | |
```jldoctest | |
julia> indmax([8,0.1,-9,pi]) | |
1 | |
julia> indmax([1,7,7,6]) | |
2 | |
julia> indmax([1,7,7,NaN]) | |
2 | |
``` | |
""" | |
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`. | |
The collection must not be empty. | |
```jldoctest | |
julia> indmin([8,0.1,-9,pi]) | |
3 | |
julia> indmin([7,1,1,6]) | |
2 | |
julia> indmin([7,1,1,NaN]) | |
2 | |
``` | |
""" | |
indmin(a) = findmin(a)[2] | |
# similar to Matlab's ismember | |
""" | |
indexin(a, b) | |
Returns a vector containing the highest index in `b` for | |
each value in `a` that is a member of `b` . The output | |
vector contains 0 wherever `a` is not a member of `b`. | |
```jldoctest | |
julia> a = ['a', 'b', 'c', 'b', 'd', 'a']; | |
julia> b = ['a','b','c']; | |
julia> indexin(a,b) | |
6-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
2 | |
0 | |
1 | |
julia> indexin(b,a) | |
3-element Array{Int64,1}: | |
6 | |
4 | |
3 | |
``` | |
""" | |
function indexin(a::AbstractArray, b::AbstractArray) | |
bdict = Dict(zip(b, 1:length(b))) | |
[get(bdict, i, 0) for i in a] | |
end | |
""" | |
findin(a, b) | |
Returns the indices of elements in collection `a` that appear in collection `b`. | |
```jldoctest | |
julia> a = collect(1:3:15) | |
5-element Array{Int64,1}: | |
1 | |
4 | |
7 | |
10 | |
13 | |
julia> b = collect(2:4:10) | |
3-element Array{Int64,1}: | |
2 | |
6 | |
10 | |
julia> findin(a,b) # 10 is the only common element | |
1-element Array{Int64,1}: | |
4 | |
``` | |
""" | |
function findin(a, b) | |
ind = Array{Int,1}(0) | |
bset = Set(b) | |
@inbounds for (i,ai) in enumerate(a) | |
ai in bset && push!(ind, i) | |
end | |
ind | |
end | |
# Copying subregions | |
# TODO: DEPRECATE FOR #14770 | |
function indcopy(sz::Dims, I::Vector) | |
n = length(I) | |
s = sz[n] | |
for i = n+1:length(sz) | |
s *= sz[i] | |
end | |
dst = eltype(I)[findin(I[i], i < n ? (1:sz[i]) : (1:s)) for i = 1:n] | |
src = eltype(I)[I[i][findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n] | |
dst, src | |
end | |
function indcopy(sz::Dims, I::Tuple{Vararg{RangeIndex}}) | |
n = length(I) | |
s = sz[n] | |
for i = n+1:length(sz) | |
s *= sz[i] | |
end | |
dst::typeof(I) = ntuple(i-> findin(I[i], i < n ? (1:sz[i]) : (1:s)), n)::typeof(I) | |
src::typeof(I) = ntuple(i-> I[i][findin(I[i], i < n ? (1:sz[i]) : (1:s))], n)::typeof(I) | |
dst, src | |
end | |
## Filter ## | |
""" | |
filter(function, collection) | |
Return a copy of `collection`, removing elements for which `function` is `false`. For | |
associative collections, the function is passed two arguments (key and value). | |
```jldocttest | |
julia> a = 1:10 | |
1:10 | |
julia> filter(isodd, a) | |
5-element Array{Int64,1}: | |
1 | |
3 | |
5 | |
7 | |
9 | |
``` | |
""" | |
filter(f, As::AbstractArray) = As[map(f, As)::AbstractArray{Bool}] | |
function filter!(f, a::Vector) | |
insrt = 1 | |
for acurr in a | |
if f(acurr) | |
a[insrt] = acurr | |
insrt += 1 | |
end | |
end | |
deleteat!(a, insrt:length(a)) | |
return a | |
end | |
function filter(f, a::Vector) | |
r = Array{eltype(a)}(0) | |
for ai in a | |
if f(ai) | |
push!(r, ai) | |
end | |
end | |
return r | |
end | |
# set-like operators for vectors | |
# These are moderately efficient, preserve order, and remove dupes. | |
function intersect(v1, vs...) | |
ret = Array{promote_eltype(v1, vs...)}(0) | |
for v_elem in v1 | |
inall = true | |
for vsi in vs | |
if !in(v_elem, vsi) | |
inall=false; break | |
end | |
end | |
if inall | |
push!(ret, v_elem) | |
end | |
end | |
ret | |
end | |
function union(vs...) | |
ret = Array{promote_eltype(vs...)}(0) | |
seen = Set() | |
for v in vs | |
for v_elem in v | |
if !in(v_elem, seen) | |
push!(ret, v_elem) | |
push!(seen, v_elem) | |
end | |
end | |
end | |
ret | |
end | |
# setdiff only accepts two args | |
""" | |
setdiff(a, b) | |
Construct the set of elements in `a` but not `b`. Maintains order with arrays. Note that | |
both arguments must be collections, and both will be iterated over. In particular, | |
`setdiff(set,element)` where `element` is a potential member of `set`, will not work in | |
general. | |
```jldoctest | |
julia> setdiff([1,2,3],[3,4,5]) | |
2-element Array{Int64,1}: | |
1 | |
2 | |
``` | |
""" | |
function setdiff(a, b) | |
args_type = promote_type(eltype(a), eltype(b)) | |
bset = Set(b) | |
ret = Array{args_type,1}(0) | |
seen = Set{eltype(a)}() | |
for a_elem in a | |
if !in(a_elem, seen) && !in(a_elem, bset) | |
push!(ret, a_elem) | |
push!(seen, a_elem) | |
end | |
end | |
ret | |
end | |
# symdiff is associative, so a relatively clean | |
# way to implement this is by using setdiff and union, and | |
# recursing. Has the advantage of keeping order, too, but | |
# not as fast as other methods that make a single pass and | |
# store counts with a Dict. | |
symdiff(a) = a | |
symdiff(a, b) = union(setdiff(a,b), setdiff(b,a)) | |
""" | |
symdiff(a, b, rest...) | |
Construct the symmetric difference of elements in the passed in sets or arrays. | |
Maintains order with arrays. | |
```jldoctest | |
julia> symdiff([1,2,3],[3,4,5],[4,5,6]) | |
3-element Array{Int64,1}: | |
1 | |
2 | |
6 | |
``` | |
""" | |
symdiff(a, b, rest...) = symdiff(a, symdiff(b, rest...)) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## Unary operators ## | |
""" | |
conj!(A) | |
Transform an array to its complex conjugate in-place. | |
See also [`conj`](:func:`conj`). | |
""" | |
function conj!{T<:Number}(A::AbstractArray{T}) | |
for i in eachindex(A) | |
A[i] = conj(A[i]) | |
end | |
return A | |
end | |
for f in (:-, :~, :conj, :sign) | |
@eval begin | |
function ($f)(A::AbstractArray) | |
F = similar(A) | |
RF, RA = eachindex(F), eachindex(A) | |
if RF == RA | |
for i in RA | |
F[i] = ($f)(A[i]) | |
end | |
else | |
for (iF, iA) in zip(RF, RA) | |
F[iF] = ($f)(A[iA]) | |
end | |
end | |
return F | |
end | |
end | |
end | |
(-)(A::AbstractArray{Bool}) = reshape([ -A[i] for i in eachindex(A) ], size(A)) | |
real(A::AbstractArray) = reshape([ real(x) for x in A ], size(A)) | |
imag(A::AbstractArray) = reshape([ imag(x) for x in A ], size(A)) | |
function !(A::AbstractArray{Bool}) | |
F = similar(A) | |
RF, RA = eachindex(F), eachindex(A) | |
if RF == RA | |
for i in RA | |
F[i] = !A[i] | |
end | |
else | |
for (iF, iA) in zip(RF, RA) | |
F[iF] = !A[iA] | |
end | |
end | |
return F | |
end | |
## Binary arithmetic operators ## | |
promote_array_type(F, ::Type, ::Type, T::Type) = T | |
promote_array_type{S<:Real, A<:AbstractFloat}(F, ::Type{S}, ::Type{A}, ::Type) = A | |
promote_array_type{S<:Integer, A<:Integer}(F, ::Type{S}, ::Type{A}, ::Type) = A | |
promote_array_type{S<:Integer, A<:Integer}(::typeof(./), ::Type{S}, ::Type{A}, T::Type) = T | |
promote_array_type{S<:Integer, A<:Integer}(::typeof(.\), ::Type{S}, ::Type{A}, T::Type) = T | |
promote_array_type{S<:Integer}(::typeof(./), ::Type{S}, ::Type{Bool}, T::Type) = T | |
promote_array_type{S<:Integer}(::typeof(.\), ::Type{S}, ::Type{Bool}, T::Type) = T | |
promote_array_type{S<:Integer}(F, ::Type{S}, ::Type{Bool}, T::Type) = T | |
for f in (:+, :-, :div, :mod, :&, :|, :$) | |
@eval ($f)(A::AbstractArray, B::AbstractArray) = | |
_elementwise($f, promote_eltype_op($f, A, B), A, B) | |
end | |
function _elementwise(op, ::Type{Any}, A::AbstractArray, B::AbstractArray) | |
promote_shape(A, B) # check size compatibility | |
return broadcast(op, A, B) | |
end | |
function _elementwise{T}(op, ::Type{T}, A::AbstractArray, B::AbstractArray) | |
F = similar(A, T, promote_shape(A, B)) | |
RF, RA, RB = eachindex(F), eachindex(A), eachindex(B) | |
if RF == RA == RB | |
for i in RA | |
@inbounds F[i] = op(A[i], B[i]) | |
end | |
else | |
for (iF, iA, iB) in zip(RF, RA, RB) | |
@inbounds F[iF] = op(A[iA], B[iB]) | |
end | |
end | |
return F | |
end | |
for f in (:.+, :.-, :.*, :./, :.\, :.^, :.÷, :.%, :.<<, :.>>, :div, :mod, :rem, :&, :|, :$) | |
@eval begin | |
function ($f){T}(A::Number, B::AbstractArray{T}) | |
R = promote_op($f, typeof(A), T) | |
S = promote_array_type($f, typeof(A), T, R) | |
S === Any && return [($f)(A, b) for b in B] | |
F = similar(B, S) | |
RF, RB = eachindex(F), eachindex(B) | |
if RF == RB | |
for i in RB | |
@inbounds F[i] = ($f)(A, B[i]) | |
end | |
else | |
for (iF, iB) in zip(RF, RB) | |
@inbounds F[iF] = ($f)(A, B[iB]) | |
end | |
end | |
return F | |
end | |
function ($f){T}(A::AbstractArray{T}, B::Number) | |
R = promote_op($f, T, typeof(B)) | |
S = promote_array_type($f, typeof(B), T, R) | |
S === Any && return [($f)(a, B) for a in A] | |
F = similar(A, S) | |
RF, RA = eachindex(F), eachindex(A) | |
if RF == RA | |
for i in RA | |
@inbounds F[i] = ($f)(A[i], B) | |
end | |
else | |
for (iF, iA) in zip(RF, RA) | |
@inbounds F[iF] = ($f)(A[iA], B) | |
end | |
end | |
return F | |
end | |
end | |
end | |
# familiar aliases for broadcasting operations of array ± scalar (#7226): | |
(+)(A::AbstractArray{Bool},x::Bool) = A .+ x | |
(+)(x::Bool,A::AbstractArray{Bool}) = x .+ A | |
(-)(A::AbstractArray{Bool},x::Bool) = A .- x | |
(-)(x::Bool,A::AbstractArray{Bool}) = x .- A | |
(+)(A::AbstractArray,x::Number) = A .+ x | |
(+)(x::Number,A::AbstractArray) = x .+ A | |
(-)(A::AbstractArray,x::Number) = A .- x | |
(-)(x::Number,A::AbstractArray) = x .- A | |
## data movement ## | |
function flipdim{T}(A::Array{T}, d::Integer) | |
nd = ndims(A) | |
1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) | |
sd = size(A, d) | |
if sd == 1 || isempty(A) | |
return copy(A) | |
end | |
B = similar(A) | |
nnd = 0 | |
for i = 1:nd | |
nnd += Int(size(A,i)==1 || i==d) | |
end | |
if nnd==nd | |
# flip along the only non-singleton dimension | |
for i = 1:sd | |
B[i] = A[sd+1-i] | |
end | |
return B | |
end | |
d_in = size(A) | |
leading = d_in[1:(d-1)] | |
M = prod(leading) | |
N = length(A) | |
stride = M * sd | |
if M==1 | |
for j = 0:stride:(N-stride) | |
for i = 1:sd | |
ri = sd+1-i | |
B[j + ri] = A[j + i] | |
end | |
end | |
else | |
if isbits(T) && M>200 | |
for i = 1:sd | |
ri = sd+1-i | |
for j=0:stride:(N-stride) | |
offs = j + 1 + (i-1)*M | |
boffs = j + 1 + (ri-1)*M | |
copy!(B, boffs, A, offs, M) | |
end | |
end | |
else | |
for i = 1:sd | |
ri = sd+1-i | |
for j=0:stride:(N-stride) | |
offs = j + 1 + (i-1)*M | |
boffs = j + 1 + (ri-1)*M | |
for k=0:(M-1) | |
B[boffs + k] = A[offs + k] | |
end | |
end | |
end | |
end | |
end | |
return B | |
end | |
""" | |
rotl90(A) | |
Rotate matrix `A` left 90 degrees. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rotl90(a) | |
2×2 Array{Int64,2}: | |
2 4 | |
1 3 | |
``` | |
""" | |
function rotl90(A::AbstractMatrix) | |
ind1, ind2 = indices(A) | |
B = similar(A, (ind2,ind1)) | |
n = first(ind2)+last(ind2) | |
for i=indices(A,1), j=ind2 | |
B[n-j,i] = A[i,j] | |
end | |
return B | |
end | |
""" | |
rotr90(A) | |
Rotate matrix `A` right 90 degrees. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rotr90(a) | |
2×2 Array{Int64,2}: | |
3 1 | |
4 2 | |
``` | |
""" | |
function rotr90(A::AbstractMatrix) | |
ind1, ind2 = indices(A) | |
B = similar(A, (ind2,ind1)) | |
m = first(ind1)+last(ind1) | |
for i=ind1, j=indices(A,2) | |
B[j,m-i] = A[i,j] | |
end | |
return B | |
end | |
""" | |
rot180(A) | |
Rotate matrix `A` 180 degrees. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rot180(a) | |
2×2 Array{Int64,2}: | |
4 3 | |
2 1 | |
``` | |
""" | |
function rot180(A::AbstractMatrix) | |
B = similar(A) | |
ind1, ind2 = indices(A,1), indices(A,2) | |
m, n = first(ind1)+last(ind1), first(ind2)+last(ind2) | |
for j=ind2, i=ind1 | |
B[m-i,n-j] = A[i,j] | |
end | |
return B | |
end | |
""" | |
rotl90(A, k) | |
Rotate matrix `A` left 90 degrees an integer `k` number of times. | |
If `k` is zero or a multiple of four, this is equivalent to a `copy`. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rotl90(a,1) | |
2×2 Array{Int64,2}: | |
2 4 | |
1 3 | |
julia> rotl90(a,2) | |
2×2 Array{Int64,2}: | |
4 3 | |
2 1 | |
julia> rotl90(a,3) | |
2×2 Array{Int64,2}: | |
3 1 | |
4 2 | |
julia> rotl90(a,4) | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
``` | |
""" | |
function rotl90(A::AbstractMatrix, k::Integer) | |
k = mod(k, 4) | |
k == 1 ? rotl90(A) : | |
k == 2 ? rot180(A) : | |
k == 3 ? rotr90(A) : copy(A) | |
end | |
""" | |
rotr90(A, k) | |
Rotate matrix `A` right 90 degrees an integer `k` number of times. If `k` is zero or a | |
multiple of four, this is equivalent to a `copy`. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rotr90(a,1) | |
2×2 Array{Int64,2}: | |
3 1 | |
4 2 | |
julia> rotr90(a,2) | |
2×2 Array{Int64,2}: | |
4 3 | |
2 1 | |
julia> rotr90(a,3) | |
2×2 Array{Int64,2}: | |
2 4 | |
1 3 | |
julia> rotr90(a,4) | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
``` | |
""" | |
rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) | |
""" | |
rot180(A, k) | |
Rotate matrix `A` 180 degrees an integer `k` number of times. | |
If `k` is even, this is equivalent to a `copy`. | |
```jldoctest | |
julia> a = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> rot180(a,1) | |
2×2 Array{Int64,2}: | |
4 3 | |
2 1 | |
julia> rot180(a,2) | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
``` | |
""" | |
rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) | |
## Transpose ## | |
""" | |
transpose!(dest,src) | |
Transpose array `src` and store the result in the preallocated array `dest`, which should | |
have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is | |
supported and unexpected results will happen if `src` and `dest` have overlapping memory | |
regions. | |
""" | |
transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A) | |
""" | |
ctranspose!(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) | |
function transpose!(B::AbstractVector, A::AbstractMatrix) | |
indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) | |
copy!(B, A) | |
end | |
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) | |
indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose")) | |
ccopy!(B, A) | |
end | |
function ctranspose!(B::AbstractMatrix, A::AbstractVector) | |
indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose")) | |
ccopy!(B, A) | |
end | |
const transposebaselength=64 | |
function transpose_f!(f,B::AbstractMatrix,A::AbstractMatrix) | |
inds = indices(A) | |
indices(B,1) == inds[2] && indices(B,2) == inds[1] || throw(DimensionMismatch(string(f))) | |
m, n = length(inds[1]), length(inds[2]) | |
if m*n<=4*transposebaselength | |
@inbounds begin | |
for j = inds[2] | |
for i = inds[1] | |
B[j,i] = f(A[i,j]) | |
end | |
end | |
end | |
else | |
transposeblock!(f,B,A,m,n,first(inds[1])-1,first(inds[2])-1) | |
end | |
return B | |
end | |
function transposeblock!(f,B::AbstractMatrix,A::AbstractMatrix,m::Int,n::Int,offseti::Int,offsetj::Int) | |
if m*n<=transposebaselength | |
@inbounds begin | |
for j = offsetj+(1:n) | |
for i = offseti+(1:m) | |
B[j,i] = f(A[i,j]) | |
end | |
end | |
end | |
elseif m>n | |
newm=m>>1 | |
transposeblock!(f,B,A,newm,n,offseti,offsetj) | |
transposeblock!(f,B,A,m-newm,n,offseti+newm,offsetj) | |
else | |
newn=n>>1 | |
transposeblock!(f,B,A,m,newn,offseti,offsetj) | |
transposeblock!(f,B,A,m,n-newn,offseti,offsetj+newn) | |
end | |
return B | |
end | |
function ccopy!(B, A) | |
RB, RA = eachindex(B), eachindex(A) | |
if RB == RA | |
for i = RB | |
B[i] = ctranspose(A[i]) | |
end | |
else | |
for (i,j) = zip(RB, RA) | |
B[i] = ctranspose(A[j]) | |
end | |
end | |
end | |
""" | |
transpose(A) | |
The transposition operator (`.'`). | |
""" | |
function transpose(A::AbstractMatrix) | |
ind1, ind2 = indices(A) | |
B = similar(A, (ind2, ind1)) | |
transpose!(B, A) | |
end | |
function ctranspose(A::AbstractMatrix) | |
ind1, ind2 = indices(A) | |
B = similar(A, (ind2, ind1)) | |
ctranspose!(B, A) | |
end | |
ctranspose{T<:Real}(A::AbstractVecOrMat{T}) = transpose(A) | |
transpose(x::AbstractVector) = [ transpose(v) for i=of_indices(x, OneTo(1)), v in x ] | |
ctranspose{T}(x::AbstractVector{T}) = T[ ctranspose(v) for i=of_indices(x, OneTo(1)), v in x ] | |
# see discussion in #18364 ... we try not to widen type of the resulting array | |
# from cumsum or cumprod, but in some cases (+, Bool) we may not have a choice. | |
rcum_promote_type{T<:Number}(op, ::Type{T}) = promote_op(op, T) | |
rcum_promote_type{T}(op, ::Type{T}) = T | |
# handle sums of Vector{Bool} and similar. it would be nice to handle | |
# any AbstractArray here, but it's not clear how that would be possible | |
rcum_promote_type{T,N}(op, ::Type{Array{T,N}}) = Array{rcum_promote_type(op,T), N} | |
for (f, f!, fp, op) = ((:cumsum, :cumsum!, :cumsum_pairwise!, :+), | |
(:cumprod, :cumprod!, :cumprod_pairwise!, :*) ) | |
# in-place cumsum of c = s+v[range(i1,n)], using pairwise summation | |
@eval function ($fp){T}(v::AbstractVector, c::AbstractVector{T}, s, i1, n) | |
local s_::T # for sum(v[range(i1,n)]), i.e. sum without s | |
if n < 128 | |
@inbounds s_ = v[i1] | |
@inbounds c[i1] = ($op)(s, s_) | |
for i = i1+1:i1+n-1 | |
@inbounds s_ = $(op)(s_, v[i]) | |
@inbounds c[i] = $(op)(s, s_) | |
end | |
else | |
n2 = n >> 1 | |
s_ = ($fp)(v, c, s, i1, n2) | |
s_ = $(op)(s_, ($fp)(v, c, ($op)(s, s_), i1+n2, n-n2)) | |
end | |
return s_ | |
end | |
@eval function ($f!)(result::AbstractVector, v::AbstractVector) | |
li = linearindices(v) | |
li != linearindices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) | |
n = length(li) | |
if n == 0; return result; end | |
i1 = first(li) | |
@inbounds result[i1] = v1 = v[i1] | |
n == 1 && return result | |
($fp)(v, result, v1, i1+1, n-1) | |
return result | |
end | |
@eval function ($f){T}(v::AbstractVector{T}) | |
return ($f!)(similar(v, rcum_promote_type($op, T)), v) | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# generic operations on associative collections | |
const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ | |
haskey(d::Associative, k) = in(k,keys(d)) | |
function in(p::Pair, a::Associative, valcmp=(==)) | |
v = get(a,p[1],secret_table_token) | |
if v !== secret_table_token | |
valcmp(v, p[2]) && return true | |
end | |
return false | |
end | |
function in(p, a::Associative) | |
error("""Associative collections only contain Pairs; | |
Either look for e.g. A=>B instead, or use the `keys` or `values` | |
function if you are looking for a key or value respectively.""") | |
end | |
function summary(t::Associative) | |
n = length(t) | |
return string(typeof(t), " with ", n, (n==1 ? " entry" : " entries")) | |
end | |
immutable KeyIterator{T<:Associative} | |
dict::T | |
end | |
immutable ValueIterator{T<:Associative} | |
dict::T | |
end | |
summary{T<:Union{KeyIterator,ValueIterator}}(iter::T) = | |
string(T.name, " for a ", summary(iter.dict)) | |
show(io::IO, iter::Union{KeyIterator,ValueIterator}) = show(io, collect(iter)) | |
length(v::Union{KeyIterator,ValueIterator}) = length(v.dict) | |
isempty(v::Union{KeyIterator,ValueIterator}) = isempty(v.dict) | |
_tt1{A,B}(::Type{Pair{A,B}}) = A | |
_tt2{A,B}(::Type{Pair{A,B}}) = B | |
eltype{D}(::Type{KeyIterator{D}}) = _tt1(eltype(D)) | |
eltype{D}(::Type{ValueIterator{D}}) = _tt2(eltype(D)) | |
start(v::Union{KeyIterator,ValueIterator}) = start(v.dict) | |
done(v::Union{KeyIterator,ValueIterator}, state) = done(v.dict, state) | |
function next(v::KeyIterator, state) | |
n = next(v.dict, state) | |
n[1][1], n[2] | |
end | |
function next(v::ValueIterator, state) | |
n = next(v.dict, state) | |
n[1][2], n[2] | |
end | |
in(k, v::KeyIterator) = get(v.dict, k, secret_table_token) !== secret_table_token | |
""" | |
keys(a::Associative) | |
Return an iterator over all keys in a collection. | |
`collect(keys(d))` returns an array of keys. | |
Since the keys are stored internally in a hash table, | |
the order in which they are returned may vary. | |
```jldoctest | |
julia> a = Dict('a'=>2, 'b'=>3) | |
Dict{Char,Int64} with 2 entries: | |
'b' => 3 | |
'a' => 2 | |
julia> collect(keys(a)) | |
2-element Array{Char,1}: | |
'b' | |
'a' | |
``` | |
""" | |
keys(a::Associative) = KeyIterator(a) | |
eachindex(a::Associative) = KeyIterator(a) | |
""" | |
values(a::Associative) | |
Return an iterator over all values in a collection. | |
`collect(values(d))` returns an array of values. | |
```jldoctest | |
julia> a = Dict('a'=>2, 'b'=>3) | |
Dict{Char,Int64} with 2 entries: | |
'b' => 3 | |
'a' => 2 | |
julia> collect(values(a)) | |
2-element Array{Int64,1}: | |
3 | |
2 | |
``` | |
""" | |
values(a::Associative) = ValueIterator(a) | |
function copy(a::Associative) | |
b = similar(a) | |
for (k,v) in a | |
b[k] = v | |
end | |
return b | |
end | |
""" | |
merge!(d::Associative, others::Associative...) | |
Update collection with pairs from the other collections. | |
See also [`merge`](:func:`merge`). | |
""" | |
function merge!(d::Associative, others::Associative...) | |
for other in others | |
for (k,v) in other | |
d[k] = v | |
end | |
end | |
return d | |
end | |
# very similar to `merge!`, but accepts any iterable and extends code | |
# that would otherwise only use `copy!` with arrays. | |
function copy!(dest::Union{Associative,AbstractSet}, src) | |
for x in src | |
push!(dest, x) | |
end | |
return dest | |
end | |
""" | |
keytype(type) | |
Get the key type of an associative collection type. Behaves similarly to [`eltype`](:func:`eltype`). | |
""" | |
keytype{K,V}(::Type{Associative{K,V}}) = K | |
keytype(a::Associative) = keytype(typeof(a)) | |
keytype{A<:Associative}(::Type{A}) = keytype(supertype(A)) | |
""" | |
valtype(type) | |
Get the value type of an associative collection type. Behaves similarly to [`eltype`](:func:`eltype`). | |
""" | |
valtype{K,V}(::Type{Associative{K,V}}) = V | |
valtype{A<:Associative}(::Type{A}) = valtype(supertype(A)) | |
valtype(a::Associative) = valtype(typeof(a)) | |
""" | |
merge(d::Associative, others::Associative...) | |
Construct a merged collection from the given collections. If necessary, the | |
types of the resulting collection will be promoted to accommodate the types of | |
the merged collections. If the same key is present in another collection, the | |
value for that key will be the value it has in the last collection listed. | |
```jldoctest | |
julia> a = Dict("foo" => 0.0, "bar" => 42.0) | |
Dict{String,Float64} with 2 entries: | |
"bar" => 42.0 | |
"foo" => 0.0 | |
julia> b = Dict("baz" => 17, "bar" => 4711) | |
Dict{String,Int64} with 2 entries: | |
"bar" => 4711 | |
"baz" => 17 | |
julia> merge(a, b) | |
Dict{String,Float64} with 3 entries: | |
"bar" => 4711.0 | |
"baz" => 17.0 | |
"foo" => 0.0 | |
julia> merge(b, a) | |
Dict{String,Float64} with 3 entries: | |
"bar" => 42.0 | |
"baz" => 17.0 | |
"foo" => 0.0 | |
``` | |
""" | |
function merge(d::Associative, others::Associative...) | |
K, V = keytype(d), valtype(d) | |
for other in others | |
K = promote_type(K, keytype(other)) | |
V = promote_type(V, valtype(other)) | |
end | |
merge!(Dict{K,V}(), d, others...) | |
end | |
function filter!(f, d::Associative) | |
badkeys = Array{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 | |
return d | |
end | |
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 | |
end | |
end | |
return df | |
end | |
eltype{K,V}(::Type{Associative{K,V}}) = Pair{K,V} | |
function isequal(l::Associative, r::Associative) | |
l === r && return true | |
if isa(l,ObjectIdDict) != isa(r,ObjectIdDict) | |
return false | |
end | |
if length(l) != length(r) return false end | |
for pair in l | |
if !in(pair, r, isequal) | |
return false | |
end | |
end | |
true | |
end | |
function ==(l::Associative, r::Associative) | |
l === r && return true | |
if isa(l,ObjectIdDict) != isa(r,ObjectIdDict) | |
return false | |
end | |
if length(l) != length(r) return false end | |
for pair in l | |
if !in(pair, r, ==) | |
return false | |
end | |
end | |
true | |
end | |
const hasha_seed = UInt === UInt64 ? 0x6d35bb51952d5539 : 0x952d5539 | |
function hash(a::Associative, h::UInt) | |
h = hash(hasha_seed, h) | |
for (k,v) in a | |
h $= hash(k, hash(v)) | |
end | |
return h | |
end | |
function getindex(t::Associative, key) | |
v = get(t, key, secret_table_token) | |
if v === secret_table_token | |
throw(KeyError(key)) | |
end | |
return v | |
end | |
# t[k1,k2,ks...] is syntactic sugar for t[(k1,k2,ks...)]. (Note | |
# that we need to avoid dispatch loops if setindex!(t,v,k) is not defined.) | |
getindex(t::Associative, k1, k2, ks...) = getindex(t, tuple(k1,k2,ks...)) | |
setindex!(t::Associative, v, k1, k2, ks...) = setindex!(t, v, tuple(k1,k2,ks...)) | |
push!(t::Associative, p::Pair) = setindex!(t, p.second, p.first) | |
push!(t::Associative, p::Pair, q::Pair) = push!(push!(t, p), q) | |
push!(t::Associative, p::Pair, q::Pair, r::Pair...) = push!(push!(push!(t, p), q), r...) | |
# hashing objects by identity | |
type ObjectIdDict <: Associative{Any,Any} | |
ht::Vector{Any} | |
ndel::Int | |
ObjectIdDict() = new(Vector{Any}(32), 0) | |
function ObjectIdDict(itr) | |
d = ObjectIdDict() | |
for (k,v) in itr; d[k] = v; end | |
d | |
end | |
function ObjectIdDict(pairs::Pair...) | |
d = ObjectIdDict() | |
for (k,v) in pairs; d[k] = v; end | |
d | |
end | |
ObjectIdDict(o::ObjectIdDict) = new(copy(o.ht)) | |
end | |
similar(d::ObjectIdDict) = ObjectIdDict() | |
function rehash!(t::ObjectIdDict, newsz = length(t.ht)) | |
t.ht = ccall(:jl_idtable_rehash, Any, (Any, Csize_t), t.ht, newsz) | |
t | |
end | |
function setindex!(t::ObjectIdDict, v::ANY, k::ANY) | |
if t.ndel >= ((3*length(t.ht))>>2) | |
rehash!(t, max(length(t.ht)>>1, 32)) | |
t.ndel = 0 | |
end | |
t.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any), t.ht, k, v) | |
return t | |
end | |
get(t::ObjectIdDict, key::ANY, default::ANY) = | |
ccall(:jl_eqtable_get, Any, (Any, Any, Any), t.ht, key, default) | |
function pop!(t::ObjectIdDict, key::ANY, default::ANY) | |
val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any), t.ht, key, default) | |
# TODO: this can underestimate `ndel` | |
val === default || (t.ndel += 1) | |
return val | |
end | |
function pop!(t::ObjectIdDict, key::ANY) | |
val = pop!(t, key, secret_table_token) | |
val !== secret_table_token ? val : throw(KeyError(key)) | |
end | |
function delete!(t::ObjectIdDict, key::ANY) | |
pop!(t, key, secret_table_token) | |
t | |
end | |
empty!(t::ObjectIdDict) = (t.ht = Vector{Any}(length(t.ht)); t.ndel = 0; t) | |
_oidd_nextind(a, i) = reinterpret(Int,ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i)) | |
start(t::ObjectIdDict) = _oidd_nextind(t.ht, 0) | |
done(t::ObjectIdDict, i) = (i == -1) | |
next(t::ObjectIdDict, i) = (Pair{Any,Any}(t.ht[i+1],t.ht[i+2]), _oidd_nextind(t.ht, i+2)) | |
function length(d::ObjectIdDict) | |
n = 0 | |
for pair in d | |
n+=1 | |
end | |
n | |
end | |
copy(o::ObjectIdDict) = ObjectIdDict(o) | |
get!(o::ObjectIdDict, key, default) = (o[key] = get(o, key, default)) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
using Base.Iterators.Enumerate | |
""" | |
AsyncCollector(f, results, c...; ntasks=0) -> iterator | |
Apply `f` to each element of `c` using at most `ntasks` asynchronous | |
tasks. | |
If `ntasks` is unspecified, uses `max(100, nworkers())` tasks. | |
For multiple collection arguments, apply `f` elementwise. | |
Output is collected into `results`. | |
Note: `next(::AsyncCollector, state) -> (nothing, state)` | |
Note: `for task in AsyncCollector(f, results, c...) end` is equivalent to | |
`map!(f, results, c...)`. | |
""" | |
type AsyncCollector | |
f | |
results | |
enumerator::Enumerate | |
max_tasks::Function | |
task_chnl::Channel{Tuple{Int, Any}} # to communicate with the tasks | |
AsyncCollector(f, r, en::Enumerate, mt::Function, c::Channel) = new(f, r, en, mt, c) | |
end | |
function AsyncCollector(f, results, c...; ntasks=0) | |
if ntasks == 0 | |
ntasks = max(nworkers(), 100) | |
max_tasks = ()->ntasks | |
elseif isa(ntasks, Integer) | |
max_tasks = ()->ntasks | |
elseif isa(ntasks, Function) | |
max_tasks = ntasks | |
else | |
throw(ArgumentError("ntasks must be an Integer or a zero-arg function returning the maximum number of tasks allowed.")) | |
end | |
AsyncCollector(f, results, enumerate(zip(c...)), max_tasks, Channel{Tuple{Int, Any}}(typemax(Int))) | |
end | |
type AsyncCollectorState | |
enum_state | |
active_count::Int | |
item_done::Condition | |
done::Bool | |
in_error::Bool | |
nfree::Int # number of free tasks | |
end | |
isbusy(itr::AsyncCollector, state::AsyncCollectorState) = (state.nfree == 0) | |
# Wait for @async task to end. | |
wait(state::AsyncCollectorState) = wait(state.item_done) | |
function start_collector_task(itr::AsyncCollector, state::AsyncCollectorState) | |
t = @async begin | |
try | |
for (i, args) in itr.task_chnl | |
state.nfree -= 1 | |
itr.results[i] = itr.f(args...) | |
notify(state.item_done, nothing) | |
state.nfree += 1 | |
end | |
catch e | |
# The in_error flag causes done() to end the iteration early and call sync_end(). | |
# sync_end() then re-throws "e" in the main task. | |
state.in_error = true | |
clear_collector_channel(itr) | |
notify(state.item_done, nothing) | |
rethrow(e) | |
end | |
end | |
state.active_count += 1 | |
t | |
end | |
function clear_collector_channel(itr::AsyncCollector) | |
try | |
# empty out the channel and close it, ignore any errors in doing this | |
while isready(itr.task_chnl) | |
take!(itr.task_chnl) | |
end | |
close(itr.task_chnl) | |
catch | |
end | |
nothing | |
end | |
# Open a @sync block and initialise iterator state. | |
function start(itr::AsyncCollector) | |
sync_begin() | |
state = AsyncCollectorState(start(itr.enumerator), 0, Condition(), false, false, 0) | |
for _ in 1:itr.max_tasks() | |
start_collector_task(itr, state) | |
state.nfree += 1 | |
end | |
state | |
end | |
# Close @sync block when iterator is done. | |
function done(itr::AsyncCollector, state::AsyncCollectorState) | |
if state.in_error | |
@assert !isopen(itr.task_chnl) # Channel should have been cleared and closed in the async task. | |
sync_end() | |
# state.in_error is only being set in the @async block (and an error thrown), | |
# which in turn should have been caught and thrown by the sync_end() call above. | |
# Control should not come here. | |
@assert false "Error should have been captured and thrown previously." | |
end | |
if !state.done && done(itr.enumerator, state.enum_state) | |
state.done = true | |
close(itr.task_chnl) | |
sync_end() | |
end | |
return state.done | |
end | |
function next(itr::AsyncCollector, state::AsyncCollectorState) | |
# start a task if required. | |
# Note: Shouldn't need to check on every iteration. Do this at a periodic interval? | |
if state.active_count < itr.max_tasks() | |
start_collector_task(itr, state) | |
end | |
while isbusy(itr, state) | |
wait(state) | |
if state.in_error | |
# Stop processing immediately on error. | |
return (nothing, state) | |
end | |
end | |
# Get index and mapped function arguments from enumeration iterator. | |
(i, args), state.enum_state = next(itr.enumerator, state.enum_state) | |
put!(itr.task_chnl, (i, args)) | |
return (nothing, state) | |
end | |
""" | |
AsyncGenerator(f, c...; ntasks=0) -> iterator | |
Apply `f` to each element of `c` using at most `ntasks` asynchronous tasks. | |
If `ntasks` is unspecified, uses `max(100, nworkers())` tasks. | |
For multiple collection arguments, apply f elementwise. | |
Results are returned by the iterator as they become available. | |
Note: `collect(AsyncGenerator(f, c...; ntasks=1))` is equivalent to | |
`map(f, c...)`. | |
""" | |
type AsyncGenerator | |
collector::AsyncCollector | |
end | |
function AsyncGenerator(f, c...; ntasks=0) | |
AsyncGenerator(AsyncCollector(f, Dict{Int,Any}(), c...; ntasks=ntasks)) | |
end | |
type AsyncGeneratorState | |
i::Int | |
async_state::AsyncCollectorState | |
end | |
start(itr::AsyncGenerator) = AsyncGeneratorState(0, start(itr.collector)) | |
# Done when source async collector is done and all results have been consumed. | |
function done(itr::AsyncGenerator, state::AsyncGeneratorState) | |
done(itr.collector, state.async_state) && isempty(itr.collector.results) | |
end | |
# Pump the source async collector if it is not already busy. | |
function pump_source(itr::AsyncGenerator, state::AsyncGeneratorState) | |
if !isbusy(itr.collector, state.async_state) && | |
!done(itr.collector, state.async_state) | |
ignored, state.async_state = next(itr.collector, state.async_state) | |
return true | |
else | |
return false | |
end | |
end | |
function next(itr::AsyncGenerator, state::AsyncGeneratorState) | |
state.i += 1 | |
results = itr.collector.results | |
while !haskey(results, state.i) | |
# Wait for results to become available. | |
if !pump_source(itr,state) && !haskey(results, state.i) | |
wait(state.async_state) | |
end | |
end | |
r = results[state.i] | |
delete!(results, state.i) | |
return (r, state) | |
end | |
# pass-through iterator traits to the iterable | |
# on which the mapping function is being applied | |
iteratorsize(itr::AsyncGenerator) = iteratorsize(itr.collector.enumerator) | |
size(itr::AsyncGenerator) = size(itr.collector.enumerator) | |
length(itr::AsyncGenerator) = length(itr.collector.enumerator) | |
""" | |
asyncmap(f, c...) -> collection | |
Transform collection `c` by applying `@async f` to each element. | |
For multiple collection arguments, apply f elementwise. | |
""" | |
asyncmap(f, c...) = collect(AsyncGenerator(f, c...)) | |
""" | |
asyncmap!(f, c) | |
In-place version of `asyncmap()`. | |
""" | |
asyncmap!(f, c) = (for x in AsyncCollector(f, c, c) end; c) | |
""" | |
asyncmap!(f, results, c...) | |
Like `asyncmap()`, but stores output in `results` rather returning a collection. | |
""" | |
asyncmap!(f, r, c1, c...) = (for x in AsyncCollector(f, r, c1, c...) end; r) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
using Core.Intrinsics: llvmcall | |
import Base: setindex!, getindex, unsafe_convert | |
import Base.Sys: ARCH, WORD_SIZE | |
export | |
Atomic, | |
atomic_cas!, | |
atomic_xchg!, | |
atomic_add!, atomic_sub!, | |
atomic_and!, atomic_nand!, atomic_or!, atomic_xor!, | |
atomic_max!, atomic_min!, | |
atomic_fence | |
# Disable 128-bit types on 32-bit Intel sytems due to LLVM problems; | |
# see <https://github.com/JuliaLang/julia/issues/14818> (fixed on LLVM 3.9) | |
# 128-bit atomics do not exist on AArch32. | |
if (VersionNumber(Base.libllvm_version) < v"3.9-" && ARCH === :i686) || | |
startswith(string(ARCH), "arm") | |
const inttypes = (Int8, Int16, Int32, Int64, | |
UInt8, UInt16, UInt32, UInt64) | |
else | |
const inttypes = (Int8, Int16, Int32, Int64, Int128, | |
UInt8, UInt16, UInt32, UInt64, UInt128) | |
end | |
const floattypes = (Float16, Float32, Float64) | |
# TODO: Support Bool, Ptr | |
const atomictypes = (inttypes..., floattypes...) | |
typealias IntTypes Union{inttypes...} | |
typealias FloatTypes Union{floattypes...} | |
typealias AtomicTypes Union{atomictypes...} | |
""" | |
Threads.Atomic{T} | |
Holds a reference to an object of type `T`, ensuring that it is only | |
accessed atomically, i.e. in a thread-safe manner. | |
Only certain "simple" types can be used atomically, namely the | |
bitstypes integer and float-point types. These are `Int8`...`Int128`, | |
`UInt8`...`UInt128`, and `Float16`...`Float64`. | |
New atomic objects can be created from a non-atomic values; if none is | |
specified, the atomic object is initialized with zero. | |
Atomic objects can be accessed using the `[]` notation: | |
```Julia | |
x::Atomic{Int} | |
x[] = 1 | |
val = x[] | |
``` | |
Atomic operations use an `atomic_` prefix, such as `atomic_add!`, | |
`atomic_xchg!`, etc. | |
""" | |
type Atomic{T<:AtomicTypes} | |
value::T | |
Atomic() = new(zero(T)) | |
Atomic(value) = new(value) | |
end | |
Atomic() = Atomic{Int}() | |
""" | |
Threads.atomic_cas!{T}(x::Atomic{T}, cmp::T, newval::T) | |
Atomically compare-and-set `x` | |
Atomically compares the value in `x` with `cmp`. If equal, write | |
`newval` to `x`. Otherwise, leaves `x` unmodified. Returns the old | |
value in `x`. By comparing the returned value to `cmp` (via `===`) one | |
knows whether `x` was modified and now holds the new value `newval`. | |
For further details, see LLVM's `cmpxchg` instruction. | |
This function can be used to implement transactional semantics. Before | |
the transaction, one records the value in `x`. After the transaction, | |
the new value is stored only if `x` has not been modified in the mean | |
time. | |
""" | |
function atomic_cas! end | |
""" | |
Threads.atomic_xchg!{T}(x::Atomic{T}, newval::T) | |
Atomically exchange the value in `x` | |
Atomically exchanges the value in `x` with `newval`. Returns the old | |
value. | |
For further details, see LLVM's `atomicrmw xchg` instruction. | |
""" | |
function atomic_xchg! end | |
""" | |
Threads.atomic_add!{T}(x::Atomic{T}, val::T) | |
Atomically add `val` to `x` | |
Performs `x[] += val` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw add` instruction. | |
""" | |
function atomic_add! end | |
""" | |
Threads.atomic_sub!{T}(x::Atomic{T}, val::T) | |
Atomically subtract `val` from `x` | |
Performs `x[] -= val` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw sub` instruction. | |
""" | |
function atomic_sub! end | |
""" | |
Threads.atomic_and!{T}(x::Atomic{T}, val::T) | |
Atomically bitwise-and `x` with `val` | |
Performs `x[] &= val` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw and` instruction. | |
""" | |
function atomic_and! end | |
""" | |
Threads.atomic_nand!{T}(x::Atomic{T}, val::T) | |
Atomically bitwise-nand (not-and) `x` with `val` | |
Performs `x[] = ~(x[] & val)` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw nand` instruction. | |
""" | |
function atomic_nand! end | |
""" | |
Threads.atomic_or!{T}(x::Atomic{T}, val::T) | |
Atomically bitwise-or `x` with `val` | |
Performs `x[] |= val` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw or` instruction. | |
""" | |
function atomic_or! end | |
""" | |
Threads.atomic_xor!{T}(x::Atomic{T}, val::T) | |
Atomically bitwise-xor (exclusive-or) `x` with `val` | |
Performs `x[] \$= val` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw xor` instruction. | |
""" | |
function atomic_xor! end | |
""" | |
Threads.atomic_max!{T}(x::Atomic{T}, val::T) | |
Atomically store the maximum of `x` and `val` in `x` | |
Performs `x[] = max(x[], val)` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw min` instruction. | |
""" | |
function atomic_max! end | |
""" | |
Threads.atomic_min!{T}(x::Atomic{T}, val::T) | |
Atomically store the minimum of `x` and `val` in `x` | |
Performs `x[] = min(x[], val)` atomically. Returns the old (!) value. | |
For further details, see LLVM's `atomicrmw max` instruction. | |
""" | |
function atomic_min! end | |
unsafe_convert{T}(::Type{Ptr{T}}, x::Atomic{T}) = convert(Ptr{T}, pointer_from_objref(x)) | |
setindex!{T}(x::Atomic{T}, v) = setindex!(x, convert(T, v)) | |
const llvmtypes = Dict( | |
Bool => "i1", | |
Int8 => "i8", UInt8 => "i8", | |
Int16 => "i16", UInt16 => "i16", | |
Int32 => "i32", UInt32 => "i32", | |
Int64 => "i64", UInt64 => "i64", | |
Int128 => "i128", UInt128 => "i128", | |
Float16 => "i16", # half | |
Float32 => "float", | |
Float64 => "double", | |
) | |
inttype{T<:Integer}(::Type{T}) = T | |
inttype(::Type{Float16}) = Int16 | |
inttype(::Type{Float32}) = Int32 | |
inttype(::Type{Float64}) = Int64 | |
# All atomic operations have acquire and/or release semantics, depending on | |
# whether the load or store values. Most of the time, this is what one wants | |
# anyway, and it's only moderately expensive on most hardware. | |
for typ in atomictypes | |
lt = llvmtypes[typ] | |
ilt = llvmtypes[inttype(typ)] | |
rt = VersionNumber(Base.libllvm_version) >= v"3.6" ? "$lt, $lt*" : "$lt*" | |
irt = VersionNumber(Base.libllvm_version) >= v"3.6" ? "$ilt, $ilt*" : "$ilt*" | |
if VersionNumber(Base.libllvm_version) >= v"3.8" | |
@eval getindex(x::Atomic{$typ}) = | |
llvmcall($""" | |
%rv = load atomic $rt %0 acquire, align $(WORD_SIZE ÷ 8) | |
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 $(WORD_SIZE ÷ 8) | |
ret void | |
""", Void, Tuple{Ptr{$typ},$typ}, unsafe_convert(Ptr{$typ}, x), v) | |
else | |
if typ <: Integer | |
@eval getindex(x::Atomic{$typ}) = | |
llvmcall($""" | |
%rv = load atomic $rt %0 acquire, align $(WORD_SIZE ÷ 8) | |
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 $(WORD_SIZE ÷ 8) | |
ret void | |
""", Void, Tuple{Ptr{$typ},$typ}, unsafe_convert(Ptr{$typ}, x), v) | |
else | |
@eval getindex(x::Atomic{$typ}) = | |
llvmcall($""" | |
%iptr = bitcast $lt* %0 to $ilt* | |
%irv = load atomic $irt %iptr acquire, align $(WORD_SIZE ÷ 8) | |
%rv = bitcast $ilt %irv to $lt | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) | |
@eval setindex!(x::Atomic{$typ}, v::$typ) = | |
llvmcall($""" | |
%iptr = bitcast $lt* %0 to $ilt* | |
%ival = bitcast $lt %1 to $ilt | |
store atomic $ilt %ival, $ilt* %iptr release, align $(WORD_SIZE ÷ 8) | |
ret void | |
""", Void, Tuple{Ptr{$typ},$typ}, unsafe_convert(Ptr{$typ}, x), v) | |
end | |
end | |
# Note: atomic_cas! succeeded (i.e. it stored "new") if and only if the result is "cmp" | |
if VersionNumber(Base.libllvm_version) >= v"3.5" | |
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 | |
%rv = extractvalue { $lt, i1 } %rs, 0 | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ},$typ,$typ}, | |
unsafe_convert(Ptr{$typ}, x), cmp, new) | |
else | |
@eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = | |
llvmcall($""" | |
%iptr = bitcast $lt* %0 to $ilt* | |
%icmp = bitcast $lt %1 to $ilt | |
%inew = bitcast $lt %2 to $ilt | |
%irs = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel acquire | |
%irv = extractvalue { $ilt, i1 } %irs, 0 | |
%rv = bitcast $ilt %irv to $lt | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ},$typ,$typ}, | |
unsafe_convert(Ptr{$typ}, x), cmp, new) | |
end | |
else | |
if typ <: Integer | |
@eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = | |
llvmcall($""" | |
%rv = cmpxchg $lt* %0, $lt %1, $lt %2 acq_rel | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ},$typ,$typ}, | |
unsafe_convert(Ptr{$typ}, x), cmp, new) | |
else | |
@eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = | |
llvmcall($""" | |
%iptr = bitcast $lt* %0 to $ilt* | |
%icmp = bitcast $lt %1 to $ilt | |
%inew = bitcast $lt %2 to $ilt | |
%irv = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel | |
%rv = bitcast $ilt %irv to $lt | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ},$typ,$typ}, | |
unsafe_convert(Ptr{$typ}, x), cmp, new) | |
end | |
end | |
for rmwop in [:xchg, :add, :sub, :and, :nand, :or, :xor, :max, :min] | |
rmw = string(rmwop) | |
fn = Symbol("atomic_", rmw, "!") | |
if (rmw == "max" || rmw == "min") && typ <: Unsigned | |
# LLVM distinguishes signedness in the operation, not the integer type. | |
rmw = "u" * rmw | |
end | |
if typ <: Integer | |
@eval $fn(x::Atomic{$typ}, v::$typ) = | |
llvmcall($""" | |
%rv = atomicrmw $rmw $lt* %0, $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* | |
%ival = bitcast $lt %1 to $ilt | |
%irv = atomicrmw $rmw $ilt* %iptr, $ilt %ival acq_rel | |
%rv = bitcast $ilt %irv to $lt | |
ret $lt %rv | |
""", $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) | |
end | |
end | |
end | |
# Provide atomic floating-point operations via atomic_cas! | |
const opnames = Dict{Symbol, Symbol}(:+ => :add, :- => :sub) | |
for op in [:+, :-, :max, :min] | |
opname = get(opnames, op, op) | |
@eval function $(Symbol("atomic_", opname, "!")){T<:FloatTypes}(var::Atomic{T}, val::T) | |
IT = inttype(T) | |
old = var[] | |
while true | |
new = $op(old, val) | |
cmp = old | |
old = atomic_cas!(var, cmp, new) | |
reinterpret(IT, old) == reinterpret(IT, cmp) && return new | |
# Temporary solution before we have gc transition support in codegen. | |
ccall(:jl_gc_safepoint, Void, ()) | |
end | |
end | |
end | |
""" | |
Threads.atomic_fence() | |
Insert a sequential-consistency memory fence | |
Inserts a memory fence with sequentially-consistent ordering | |
semantics. There are algorithms where this is needed, i.e. where an | |
acquire/release ordering is insufficient. | |
This is likely a very expensive operation. Given that all other atomic | |
operations in Julia already have acquire/release semantics, explicit | |
fences should not be necessary in most cases. | |
For further details, see LLVM's `fence` instruction. | |
""" | |
atomic_fence() = llvmcall(""" | |
fence seq_cst | |
ret void | |
""", Void, Tuple{}) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
""" | |
SystemError(prefix::AbstractString, [errno::Int32]) | |
A system call failed with an error code (in the `errno` global variable). | |
""" | |
type 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. | |
""" | |
type 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. | |
""" | |
type ArgumentError <: Exception | |
msg::AbstractString | |
end | |
#type UnboundError <: Exception | |
# var::Symbol | |
#end | |
""" | |
KeyError(key) | |
An indexing operation into an `Associative` (`Dict`) or `Set` like object tried to access or | |
delete a non-existent element. | |
""" | |
type 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. | |
""" | |
type MethodError <: Exception | |
f | |
args | |
end | |
""" | |
EOFError() | |
No more data was available to read from a file or stream. | |
""" | |
type EOFError <: Exception end | |
""" | |
DimensionMismatch([msg]) | |
The objects called do not have matching dimensionality. Optional argument `msg` is a | |
descriptive error string. | |
""" | |
type 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. | |
""" | |
type 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 WrappedException <: Exception | |
""" | |
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. | |
""" | |
type 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. | |
""" | |
type 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(o::ANY, f::ANY) | |
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{T}(o::T, f::Ptr{Void}) | |
@_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(o::ANY) = ccall(:jl_finalize_th, Void, (Ptr{Void}, Any,), | |
Core.getptls(), o) | |
gc(full::Bool=true) = ccall(:jl_gc_collect, Void, (Cint,), full) | |
gc_enable(on::Bool) = ccall(:jl_gc_enable, Cint, (Cint,), on)!=0 | |
immutable Nullable{T} | |
hasvalue::Bool | |
value::T | |
Nullable() = new(false) | |
Nullable(value::T, hasvalue::Bool=true) = new(hasvalue, value) | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Base64 | |
import Base: read, write, close, eof, empty! | |
export Base64EncodePipe, Base64DecodePipe, base64encode, base64decode | |
# Base64EncodePipe is a pipe-like IO object, which converts into base64 data sent | |
# to a stream. (You must close the pipe to complete the encode, separate from | |
# closing the target stream). We also have a function base64encode(f, | |
# args...) which works like sprint except that it produces | |
# base64-encoded data, along with base64encode(args...) which is equivalent | |
# to base64encode(write, args...), to return base64 strings. | |
# A Base64DecodePipe object can be used to decode base64-encoded data read from a stream | |
# , while function base64decode is useful for decoding strings | |
############################################################################# | |
""" | |
Base64EncodePipe(ostream) | |
Returns a new write-only I/O stream, which converts any bytes written to it into | |
base64-encoded ASCII bytes written to `ostream`. Calling `close` on the `Base64EncodePipe` stream | |
is necessary to complete the encoding (but does not close `ostream`). | |
""" | |
type Base64EncodePipe <: IO | |
io::IO | |
# writing works in groups of 3, so we need to cache last two bytes written | |
b0::UInt8 | |
b1::UInt8 | |
nb::UInt8 # number of bytes in cache: 0, 1, or 2 | |
function Base64EncodePipe(io::IO) | |
b = new(io,0,0,0) | |
finalizer(b, close) | |
return b | |
end | |
end | |
############################################################################# | |
# Based on code by Stefan Karpinski from https://github.com/hackerschool/WebSockets.jl (distributed under the same MIT license as Julia) | |
const b64chars = ['A':'Z';'a':'z';'0':'9';'+';'/'] | |
const base64_pad = UInt8('=') | |
function b64(x::UInt8, y::UInt8, z::UInt8) | |
n = Int(x)<<16 | Int(y)<<8 | Int(z) | |
b64chars[(n >> 18) + 1], | |
b64chars[(n >> 12) & 0b111111 + 1], | |
b64chars[(n >> 6) & 0b111111 + 1], | |
b64chars[(n ) & 0b111111 + 1] | |
end | |
function b64(x::UInt8, y::UInt8) | |
a, b, c = b64(x, y, 0x0) | |
a, b, c, base64_pad | |
end | |
function b64(x::UInt8) | |
a, b = b64(x, 0x0, 0x0) | |
a, b, base64_pad, base64_pad | |
end | |
const sentinel = typemax(UInt8) | |
const revb64chars = fill(sentinel, 256) | |
# Fill revb64chars | |
for (val, ch) in enumerate(b64chars) | |
revb64chars[UInt8(ch)] = UInt8(val - 1) | |
end | |
# Decode a block of at least 2 and at most 4 bytes, received in encvec | |
# Returns the first decoded byte and stores up to two more in cache | |
function b64decode!(encvec::Vector{UInt8}, cache::Vector{UInt8}) | |
if length(encvec) < 2 | |
throw(ArgumentError("incorrect base64 format, block must be at least 2 and at most 4 bytes")) | |
end | |
@inbounds u = revb64chars[encvec[1]] | |
@inbounds v = revb64chars[encvec[2]] | |
empty!(cache) | |
res = (u << 2) | (v >> 4) | |
if length(encvec) > 2 | |
@inbounds w = revb64chars[encvec[3]] | |
push!(cache, (v << 4) | (w >> 2)) | |
end | |
if length(encvec) > 3 | |
@inbounds z = revb64chars[encvec[4]] | |
push!(cache, (w << 6) | z) | |
end | |
res | |
end | |
############################################################################# | |
function unsafe_write(b::Base64EncodePipe, x::Ptr{UInt8}, n::UInt) | |
s = 1 # starting index | |
# finish any cached data to write: | |
if b.nb == 1 | |
if n >= 2 | |
write(b.io, b64(b.b0, unsafe_load(x, 1), unsafe_load(x, 2))...) | |
s = 3 | |
elseif n == 1 | |
b.b1 = unsafe_load(x, 1) | |
b.nb = 2 | |
return | |
else | |
return | |
end | |
elseif b.nb == 2 | |
if n >= 1 | |
write(b.io, b64(b.b0, b.b1, unsafe_load(x, 1))...) | |
s = 2 | |
else | |
return | |
end | |
end | |
# write all groups of three bytes: | |
while s + 2 <= n | |
write(b.io, b64(unsafe_load(x, s), unsafe_load(x, s + 1), unsafe_load(x, s + 2))...) | |
s += 3 | |
end | |
# cache any leftover bytes: | |
if s + 1 == n | |
b.b0 = unsafe_load(x, s) | |
b.b1 = unsafe_load(x, s + 1) | |
b.nb = 2 | |
elseif s == n | |
b.b0 = unsafe_load(x, s) | |
b.nb = 1 | |
else | |
b.nb = 0 | |
end | |
n | |
end | |
function write(b::Base64EncodePipe, x::UInt8) | |
if b.nb == 0 | |
b.b0 = x | |
b.nb = 1 | |
elseif b.nb == 1 | |
b.b1 = x | |
b.nb = 2 | |
else | |
write(b.io, b64(b.b0,b.b1,x)...) | |
b.nb = 0 | |
end | |
1 | |
end | |
function close(b::Base64EncodePipe) | |
if b.nb > 0 | |
# write leftover bytes + padding | |
if b.nb == 1 | |
write(b.io, b64(b.b0)...) | |
else # b.nb == 2 | |
write(b.io, b64(b.b0, b.b1)...) | |
end | |
b.nb = 0 | |
end | |
nothing | |
end | |
# like sprint, but returns base64 string | |
""" | |
base64encode(writefunc, args...) | |
base64encode(args...) | |
Given a `write`-like function `writefunc`, which takes an I/O stream as its first argument, | |
`base64encode(writefunc, args...)` calls `writefunc` to write `args...` to a base64-encoded | |
string, and returns the string. `base64encode(args...)` is equivalent to `base64encode(write, args...)`: | |
it converts its arguments into bytes using the standard `write` functions and returns the | |
base64-encoded string. | |
""" | |
function base64encode(f::Function, args...) | |
s = IOBuffer() | |
b = Base64EncodePipe(s) | |
f(b, args...) | |
close(b) | |
takebuf_string(s) | |
end | |
base64encode(x...) = base64encode(write, x...) | |
############################################################################# | |
""" | |
Base64DecodePipe(istream) | |
Returns a new read-only I/O stream, which decodes base64-encoded data read from `istream`. | |
""" | |
type Base64DecodePipe <: IO | |
io::IO | |
# reading works in blocks of 4 characters that are decoded into 3 bytes and 2 of them cached | |
cache::Vector{UInt8} | |
encvec::Vector{UInt8} | |
function Base64DecodePipe(io::IO) | |
b = new(io,[],[]) | |
finalizer(b, close) | |
return b | |
end | |
end | |
function read(b::Base64DecodePipe, t::Type{UInt8}) | |
if !isempty(b.cache) | |
return shift!(b.cache) | |
else | |
empty!(b.encvec) | |
while !eof(b.io) && length(b.encvec) < 4 | |
c::UInt8 = read(b.io, t) | |
@inbounds if revb64chars[c] != sentinel | |
push!(b.encvec, c) | |
end | |
end | |
return b64decode!(b.encvec,b.cache) | |
end | |
end | |
eof(b::Base64DecodePipe) = isempty(b.cache) && eof(b.io) | |
close(b::Base64DecodePipe) = nothing | |
# Decodes a base64-encoded string | |
""" | |
base64decode(string) | |
Decodes the base64-encoded `string` and returns a `Vector{UInt8}` of the decoded bytes. | |
""" | |
function base64decode(s) | |
b = IOBuffer(s) | |
try | |
return read(Base64DecodePipe(b)) | |
finally | |
close(b) | |
end | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## BitArray | |
# notes: bits are stored in contiguous chunks | |
# unused bits must always be set to 0 | |
type BitArray{N} <: DenseArray{Bool, N} | |
chunks::Vector{UInt64} | |
len::Int | |
dims::NTuple{N,Int} | |
function BitArray(dims::Vararg{Int,N}) | |
n = 1 | |
i = 1 | |
for d in dims | |
d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i")) | |
n *= d | |
i += 1 | |
end | |
nc = num_bit_chunks(n) | |
chunks = Array{UInt64}(nc) | |
nc > 0 && (chunks[end] = UInt64(0)) | |
b = new(chunks, n) | |
N != 1 && (b.dims = dims) | |
return b | |
end | |
end | |
# note: the docs for the two signatures are unified, but only | |
# the first one is recognized by the help system; it would be nice | |
# to fix this. | |
""" | |
BitArray(dims::Integer...) | |
BitArray{N}(dims::NTuple{N,Int}) | |
Construct an uninitialized `BitArray` with the given dimensions. | |
Behaves identically to the [`Array`](:func:`Array`) constructor. | |
""" | |
BitArray(dims::Integer...) = BitArray(map(Int,dims)) | |
BitArray{N}(dims::NTuple{N,Int}) = BitArray{N}(dims...) | |
typealias BitVector BitArray{1} | |
typealias BitMatrix BitArray{2} | |
BitVector() = BitArray{1}(0) | |
## utility functions ## | |
length(B::BitArray) = B.len | |
size(B::BitVector) = (B.len,) | |
size(B::BitArray) = B.dims | |
@inline function size(B::BitVector, d) | |
d < 1 && throw_boundserror(size(B), d) | |
ifelse(d == 1, B.len, 1) | |
end | |
isassigned{N}(B::BitArray{N}, i::Int) = 1 <= i <= length(B) | |
linearindexing{A<:BitArray}(::Type{A}) = LinearFast() | |
## aux functions ## | |
const _msk64 = ~UInt64(0) | |
@inline _div64(l) = l >>> 6 | |
@inline _mod64(l) = l & 63 | |
@inline _msk_end(l::Integer) = _msk64 >>> _mod64(-l) | |
@inline _msk_end(B::BitArray) = _msk_end(length(B)) | |
num_bit_chunks(n::Int) = _div64(n+63) | |
function _check_bitarray_consistency{N}(B::BitArray{N}) | |
n = length(B) | |
if N ≠ 1 | |
all(d ≥ 0 for d in B.dims) || (warn("negative d in dims: $(B.dims)"); return false) | |
prod(B.dims) ≠ n && (warn("inconsistent dims/len: prod(dims)=$(prod(B.dims)) len=$n"); return false) | |
end | |
Bc = B.chunks | |
nc = length(Bc) | |
nc == num_bit_chunks(n) || (warn("incorrect chunks length for length $n: expected=$(num_bit_chunks(n)) actual=$nc"); return false) | |
n == 0 && return true | |
Bc[end] & _msk_end(n) == Bc[end] || (warn("nonzero bits in chunk after BitArray end"); return false) | |
return true | |
end | |
@inline get_chunks_id(i::Integer) = _div64(Int(i)-1)+1, _mod64(Int(i)-1) | |
function glue_src_bitchunks(src::Vector{UInt64}, k::Int, ks1::Int, msk_s0::UInt64, ls0::Int) | |
@inbounds begin | |
chunk = ((src[k] & msk_s0) >>> ls0) | |
if ks1 > k && ls0 > 0 | |
chunk_n = (src[k + 1] & ~msk_s0) | |
chunk |= (chunk_n << (64 - ls0)) | |
end | |
end | |
return chunk | |
end | |
function copy_chunks!(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer) | |
numbits == 0 && return | |
if dest === src && pos_d > pos_s | |
return copy_chunks_rtol!(dest, pos_d, pos_s, numbits) | |
end | |
kd0, ld0 = get_chunks_id(pos_d) | |
kd1, ld1 = get_chunks_id(pos_d + numbits - 1) | |
ks0, ls0 = get_chunks_id(pos_s) | |
ks1, ls1 = get_chunks_id(pos_s + numbits - 1) | |
delta_kd = kd1 - kd0 | |
delta_ks = ks1 - ks0 | |
u = _msk64 | |
if delta_kd == 0 | |
msk_d0 = ~(u << ld0) | (u << (ld1+1)) | |
else | |
msk_d0 = ~(u << ld0) | |
msk_d1 = (u << (ld1+1)) | |
end | |
if delta_ks == 0 | |
msk_s0 = (u << ls0) & ~(u << (ls1+1)) | |
else | |
msk_s0 = (u << ls0) | |
end | |
chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0) | |
dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) | |
delta_kd == 0 && return | |
for i = 1 : kd1 - kd0 - 1 | |
chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0) | |
chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) | |
dest[kd0 + i] = chunk_s | |
chunk_s0 = chunk_s1 | |
end | |
if ks1 >= ks0 + delta_kd | |
chunk_s1 = glue_src_bitchunks(src, ks0 + delta_kd, ks1, msk_s0, ls0) | |
else | |
chunk_s1 = UInt64(0) | |
end | |
chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) | |
dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1) | |
return | |
end | |
function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Integer, pos_s::Integer, numbits::Integer) | |
pos_d == pos_s && return | |
pos_d < pos_s && return copy_chunks!(chunks, pos_d, chunks, pos_s, numbits) | |
left = numbits | |
s = min(left, 64) | |
b = left - s | |
ps = pos_s + b | |
pd = pos_d + b | |
u = _msk64 | |
while left > 0 | |
kd0, ld0 = get_chunks_id(pd) | |
kd1, ld1 = get_chunks_id(pd + s - 1) | |
ks0, ls0 = get_chunks_id(ps) | |
ks1, ls1 = get_chunks_id(ps + s - 1) | |
delta_kd = kd1 - kd0 | |
delta_ks = ks1 - ks0 | |
if delta_kd == 0 | |
msk_d0 = ~(u << ld0) | (u << (ld1+1)) | |
else | |
msk_d0 = ~(u << ld0) | |
msk_d1 = (u << (ld1+1)) | |
end | |
if delta_ks == 0 | |
msk_s0 = (u << ls0) & ~(u << (ls1+1)) | |
else | |
msk_s0 = (u << ls0) | |
end | |
chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s) | |
chunks[kd0] = (chunks[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) | |
if delta_kd != 0 | |
chunk_s = (chunk_s0 >>> (64 - ld0)) | |
chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1) | |
end | |
left -= s | |
s = min(left, 64) | |
b = left - s | |
ps = pos_s + b | |
pd = pos_d + b | |
end | |
end | |
function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Integer, numbits::Integer) | |
numbits <= 0 && return | |
k0, l0 = get_chunks_id(pos) | |
k1, l1 = get_chunks_id(pos+numbits-1) | |
u = _msk64 | |
if k1 == k0 | |
msk0 = (u << l0) & ~(u << (l1+1)) | |
else | |
msk0 = (u << l0) | |
msk1 = ~(u << (l1+1)) | |
end | |
@inbounds if x | |
Bc[k0] |= msk0 | |
for k = k0+1:k1-1 | |
Bc[k] = u | |
end | |
k1 > k0 && (Bc[k1] |= msk1) | |
else | |
Bc[k0] &= ~msk0 | |
for k = k0+1:k1-1 | |
Bc[k] = 0 | |
end | |
k1 > k0 && (Bc[k1] &= ~msk1) | |
end | |
end | |
copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Integer, src::BitArray, pos_s::Integer, numbits::Integer) = | |
copy_chunks!(dest, pos_d, src.chunks, pos_s, numbits) | |
# pack 8 Bools encoded as one contiguous UIn64 into a single byte, e.g.: | |
# 0000001:0000001:00000000:00000000:00000001:00000000:00000000:00000001 → 11001001 → 0xc9 | |
function pack8bools(z::UInt64) | |
z |= z >>> 7 | |
z |= z >>> 14 | |
z |= z >>> 28 | |
z &= 0xFF | |
return z | |
end | |
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array{Bool}, pos_s::Int, numbits::Int) | |
kd0, ld0 = get_chunks_id(pos_d) | |
kd1, ld1 = get_chunks_id(pos_d + numbits - 1) | |
delta_kd = kd1 - kd0 | |
u = _msk64 | |
if delta_kd == 0 | |
msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) | |
lt0 = ld1 | |
else | |
msk_d0 = ~(u << ld0) | |
msk_d1 = (u << (ld1+1)) | |
lt0 = 63 | |
end | |
bind = kd0 | |
ind = pos_s | |
@inbounds if ld0 > 0 | |
c = UInt64(0) | |
for j = ld0:lt0 | |
c |= (UInt64(C[ind]) << j) | |
ind += 1 | |
end | |
Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) | |
bind += 1 | |
end | |
nc = _div64(numbits - ind + pos_s) | |
nc8 = (nc >>> 3) << 3 | |
if nc8 > 0 | |
ind8 = 1 | |
C8 = reinterpret(UInt64, unsafe_wrap(Array, pointer(C, ind), nc8 << 6)) | |
@inbounds for i = 1:nc8 | |
c = UInt64(0) | |
for j = 0:7 | |
c |= (pack8bools(C8[ind8]) << (j<<3)) | |
ind8 += 1 | |
end | |
Bc[bind] = c | |
bind += 1 | |
end | |
ind += (ind8-1) << 3 | |
end | |
@inbounds for i = (nc8+1):nc | |
c = UInt64(0) | |
for j = 0:63 | |
c |= (UInt64(C[ind]) << j) | |
ind += 1 | |
end | |
Bc[bind] = c | |
bind += 1 | |
end | |
@inbounds if bind ≤ kd1 | |
@assert bind == kd1 | |
c = UInt64(0) | |
for j = 0:ld1 | |
c |= (UInt64(C[ind]) << j) | |
ind += 1 | |
end | |
Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) | |
end | |
end | |
function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array, pos_s::Int, numbits::Int) | |
bind = pos_d | |
cind = pos_s | |
lastind = pos_d + numbits - 1 | |
@inbounds while bind ≤ lastind | |
unsafe_bitsetindex!(Bc, Bool(C[cind]), bind) | |
bind += 1 | |
cind += 1 | |
end | |
end | |
# Note: the next two functions rely on the following definition of the conversion to Bool: | |
# convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError()) | |
# they're used to pre-emptively check in bulk when possible, which is much faster. | |
# Also, the functions can be overloaded for custom types T<:Real : | |
# a) in the unlikely eventuality that they use a different logic for Bool conversion | |
# b) to skip the check if not necessary | |
@inline try_bool_conversion(x::Real) = x == 0 || x == 1 || throw(InexactError()) | |
@inline unchecked_bool_convert(x::Real) = x == 1 | |
function copy_to_bitarray_chunks!{T<:Real}(Bc::Vector{UInt64}, pos_d::Int, C::Array{T}, pos_s::Int, numbits::Int) | |
@inbounds for i = (1:numbits) + pos_s - 1 | |
try_bool_conversion(C[i]) | |
end | |
kd0, ld0 = get_chunks_id(pos_d) | |
kd1, ld1 = get_chunks_id(pos_d + numbits - 1) | |
delta_kd = kd1 - kd0 | |
u = _msk64 | |
if delta_kd == 0 | |
msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) | |
lt0 = ld1 | |
else | |
msk_d0 = ~(u << ld0) | |
msk_d1 = (u << (ld1+1)) | |
lt0 = 63 | |
end | |
bind = kd0 | |
ind = pos_s | |
@inbounds if ld0 > 0 | |
c = UInt64(0) | |
for j = ld0:lt0 | |
c |= (UInt64(unchecked_bool_convert(C[ind])) << j) | |
ind += 1 | |
end | |
Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) | |
bind += 1 | |
end | |
nc = _div64(numbits - ind + pos_s) | |
@inbounds for i = 1:nc | |
c = UInt64(0) | |
for j = 0:63 | |
c |= (UInt64(unchecked_bool_convert(C[ind])) << j) | |
ind += 1 | |
end | |
Bc[bind] = c | |
bind += 1 | |
end | |
@inbounds if bind ≤ kd1 | |
@assert bind == kd1 | |
c = UInt64(0) | |
for j = 0:ld1 | |
c |= (UInt64(unchecked_bool_convert(C[ind])) << j) | |
ind += 1 | |
end | |
Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) | |
end | |
end | |
# auxiliary definitions used when filling a BitArray via a Vector{Bool} cache | |
# (e.g. when constructing from an iterable, or in broadcast!) | |
const bitcache_chunks = 64 # this can be changed | |
const bitcache_size = 64 * bitcache_chunks # do not change this | |
dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) = | |
copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6)) | |
## custom iterator ## | |
start(B::BitArray) = 0 | |
next(B::BitArray, i::Int) = (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1) | |
done(B::BitArray, i::Int) = i >= length(B) | |
## similar, fill!, copy! etc ## | |
similar(B::BitArray) = BitArray(size(B)) | |
similar(B::BitArray, dims::Int...) = BitArray(dims) | |
similar(B::BitArray, dims::Dims) = BitArray(dims...) | |
similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(dims) | |
# changing type to a non-Bool returns an Array | |
# (this triggers conversions like float(bitvector) etc.) | |
similar(B::BitArray, T::Type, dims::Dims) = Array{T}(dims) | |
function fill!(B::BitArray, x) | |
y = convert(Bool, x) | |
isempty(B) && return B | |
Bc = B.chunks | |
if !y | |
fill!(Bc, 0) | |
else | |
fill!(Bc, _msk64) | |
Bc[end] &= _msk_end(B) | |
end | |
return B | |
end | |
""" | |
falses(dims) | |
Create a `BitArray` with all values set to `false`. | |
```jldoctest | |
julia> falses(2,3) | |
2×3 BitArray{2}: | |
false false false | |
false false false | |
``` | |
""" | |
falses(dims::Dims) = fill!(BitArray(dims), false) | |
falses(dims::Integer...) = falses(map(Int,dims)) | |
""" | |
falses(A) | |
Create a `BitArray` with all values set to `false` of the same shape as `A`. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> falses(A) | |
2×2 BitArray{2}: | |
false false | |
false false | |
``` | |
""" | |
falses(A::AbstractArray) = falses(size(A)) | |
""" | |
trues(dims) | |
Create a `BitArray` with all values set to `true`. | |
```jldoctest | |
julia> trues(2,3) | |
2×3 BitArray{2}: | |
true true true | |
true true true | |
``` | |
""" | |
trues(dims::Dims) = fill!(BitArray(dims), true) | |
trues(dims::Integer...) = trues(map(Int,dims)) | |
""" | |
trues(A) | |
Create a `BitArray` with all values set to `true` of the same shape as `A`. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> trues(A) | |
2×2 BitArray{2}: | |
true true | |
true true | |
``` | |
""" | |
trues(A::AbstractArray) = trues(size(A)) | |
function one(x::BitMatrix) | |
m, n = size(x) | |
m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) | |
a = falses(n, n) | |
for i = 1:n | |
a[i,i] = true | |
end | |
return a | |
end | |
function copy!(dest::BitArray, src::BitArray) | |
length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) | |
destc = dest.chunks; srcc = src.chunks | |
nc = min(length(destc), length(srcc)) | |
nc == 0 && return dest | |
@inbounds begin | |
for i = 1 : nc - 1 | |
destc[i] = srcc[i] | |
end | |
if length(src) == length(dest) | |
destc[nc] = srcc[nc] | |
else | |
msk_s = _msk_end(src) | |
msk_d = ~msk_s | |
destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc]) | |
end | |
end | |
return dest | |
end | |
function unsafe_copy!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) | |
copy_to_bitarray_chunks!(dest.chunks, doffs, src, soffs, n) | |
return dest | |
end | |
function copy!(dest::BitArray, doffs::Integer, src::Array, soffs::Integer, n::Integer) | |
n == 0 && return dest | |
soffs < 1 && throw(BoundsError(src, soffs)) | |
doffs < 1 && throw(BoundsError(dest, doffs)) | |
soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1)) | |
doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1)) | |
return unsafe_copy!(dest, doffs, src, soffs, n) | |
end | |
function copy!(dest::BitArray, src::Array) | |
length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) | |
length(src) == 0 && return det | |
return unsafe_copy!(dest, 1, src, 1, length(src)) | |
end | |
function reshape{N}(B::BitArray, dims::NTuple{N,Int}) | |
prod(dims) == length(B) || | |
throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))")) | |
dims == size(B) && return B | |
Br = BitArray{N}(ntuple(i->0,N)...) | |
Br.chunks = B.chunks | |
Br.len = prod(dims) | |
N != 1 && (Br.dims = dims) | |
return Br | |
end | |
## Conversions ## | |
convert{T,N}(::Type{Array{T}}, B::BitArray{N}) = convert(Array{T,N}, B) | |
convert{T,N}(::Type{Array{T,N}}, B::BitArray{N}) = _convert(Array{T,N}, B) # see #15801 | |
function _convert{T,N}(::Type{Array{T,N}}, B::BitArray{N}) | |
A = Array{T}(size(B)) | |
Bc = B.chunks | |
@inbounds for i = 1:length(A) | |
A[i] = unsafe_bitgetindex(Bc, i) | |
end | |
return A | |
end | |
convert{T,N}(::Type{BitArray}, A::AbstractArray{T,N}) = convert(BitArray{N}, A) | |
function convert{T,N}(::Type{BitArray{N}}, A::AbstractArray{T,N}) | |
B = BitArray(size(A)) | |
Bc = B.chunks | |
l = length(B) | |
l == 0 && return B | |
ind = 1 | |
@inbounds begin | |
for i = 1:length(Bc)-1 | |
c = UInt64(0) | |
for j = 0:63 | |
c |= (UInt64(A[ind] != 0) << j) | |
ind += 1 | |
end | |
Bc[i] = c | |
end | |
c = UInt64(0) | |
for j = 0:_mod64(l-1) | |
c |= (UInt64(A[ind] != 0) << j) | |
ind += 1 | |
end | |
Bc[end] = c | |
end | |
return B | |
end | |
function convert{N}(::Type{BitArray{N}}, A::Array{Bool,N}) | |
B = BitArray(size(A)) | |
Bc = B.chunks | |
l = length(B) | |
l == 0 && return B | |
copy_to_bitarray_chunks!(Bc, 1, A, 1, l) | |
return B | |
end | |
convert{N}(::Type{BitArray{N}}, B::BitArray{N}) = B | |
convert{T,N}(::Type{AbstractArray{T,N}}, B::BitArray{N}) = convert(Array{T,N}, B) | |
reinterpret{N}(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) = reinterpret(B, dims) | |
reinterpret{N}(B::BitArray, dims::NTuple{N,Int}) = reshape(B, dims) | |
## Constructors from generic iterables ## | |
BitArray{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A) | |
""" | |
BitArray(itr) | |
Construct a `BitArray` generated by the given iterable object. The shape is inferred from | |
the `itr` object. | |
```jldoctest | |
julia> BitArray([1 0; 0 1]) | |
2×2 BitArray{2}: | |
true false | |
false true | |
julia> BitArray(x+y == 3 for x = 1:2, y = 1:3) | |
2×3 BitArray{2}: | |
false true false | |
true false false | |
julia> BitArray(x+y == 3 for x = 1:2 for y = 1:3) | |
6-element BitArray{1}: | |
false | |
true | |
false | |
true | |
false | |
false | |
``` | |
""" | |
BitArray(itr) = gen_bitarray(iteratorsize(itr), itr) | |
# generic constructor from an iterable without compile-time info | |
# (we pass start(itr) explicitly to avoid a type-instability with filters) | |
gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr, start(itr)) | |
# generic iterable with known shape | |
function gen_bitarray(::HasShape, itr) | |
B = BitArray(size(itr)) | |
for (I,x) in zip(CartesianRange(indices(itr)), itr) | |
B[I] = x | |
end | |
return B | |
end | |
# generator with known shape or length | |
function gen_bitarray(::HasShape, itr::Generator) | |
B = BitArray(size(itr)) | |
return fill_bitarray_from_itr!(B, itr, start(itr)) | |
end | |
function gen_bitarray(::HasLength, itr) | |
n = length(itr) | |
B = BitArray(n) | |
return fill_bitarray_from_itr!(B, itr, start(itr)) | |
end | |
gen_bitarray(::IsInfinite, itr) = throw(ArgumentError("infinite-size iterable used in BitArray constructor")) | |
# The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both | |
# use a Vector{Bool} cache for performance reasons | |
function gen_bitarray_from_itr(itr, st) | |
B = empty!(BitArray(bitcache_size)) | |
C = Vector{Bool}(bitcache_size) | |
Bc = B.chunks | |
ind = 1 | |
cind = 1 | |
while !done(itr, st) | |
x, st = next(itr, st) | |
@inbounds C[ind] = x | |
ind += 1 | |
if ind > bitcache_size | |
resize!(B, length(B) + bitcache_size) | |
dumpbitcache(Bc, cind, C) | |
cind += bitcache_chunks | |
ind = 1 | |
end | |
end | |
if ind > 1 | |
@inbounds C[ind:bitcache_size] = false | |
resize!(B, length(B) + ind - 1) | |
dumpbitcache(Bc, cind, C) | |
end | |
return B | |
end | |
function fill_bitarray_from_itr!(B::BitArray, itr, st) | |
n = length(B) | |
C = Vector{Bool}(bitcache_size) | |
Bc = B.chunks | |
ind = 1 | |
cind = 1 | |
while !done(itr, st) | |
x, st = next(itr, st) | |
@inbounds C[ind] = x | |
ind += 1 | |
if ind > bitcache_size | |
dumpbitcache(Bc, cind, C) | |
cind += bitcache_chunks | |
ind = 1 | |
end | |
end | |
if ind > 1 | |
@inbounds C[ind:bitcache_size] = false | |
dumpbitcache(Bc, cind, C) | |
end | |
return B | |
end | |
## Indexing: getindex ## | |
@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int) | |
i1, i2 = get_chunks_id(i) | |
u = UInt64(1) << i2 | |
@inbounds r = (Bc[i1] & u) != 0 | |
return r | |
end | |
@inline function getindex(B::BitArray, i::Int) | |
@boundscheck checkbounds(B, i) | |
unsafe_bitgetindex(B.chunks, i) | |
end | |
## Indexing: setindex! ## | |
@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int) | |
i1, i2 = get_chunks_id(i) | |
u = UInt64(1) << i2 | |
@inbounds begin | |
c = Bc[i1] | |
Bc[i1] = ifelse(x, c | u, c & ~u) | |
end | |
end | |
@inline function setindex!(B::BitArray, x, i::Int) | |
@boundscheck checkbounds(B, i) | |
unsafe_bitsetindex!(B.chunks, convert(Bool, x), i) | |
return B | |
end | |
# logical indexing | |
# When indexing with a BitArray, we can operate whole chunks at a time for a ~100x gain | |
@inline function setindex!(B::BitArray, x, I::BitArray) | |
@boundscheck checkbounds(B, I) | |
_unsafe_setindex!(B, x, I) | |
end | |
function _unsafe_setindex!(B::BitArray, x, I::BitArray) | |
y = convert(Bool, x) | |
Bc = B.chunks | |
Ic = I.chunks | |
length(Bc) == length(Ic) || throw_boundserror(B, I) | |
@inbounds if y | |
for i = 1:length(Bc) | |
Bc[i] |= Ic[i] | |
end | |
else | |
for i = 1:length(Bc) | |
Bc[i] &= ~Ic[i] | |
end | |
end | |
return B | |
end | |
# Assigning an array of bools is more complicated, but we can still do some | |
# work on chunks by combining X and I 64 bits at a time to improve perf by ~40% | |
@inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray) | |
@boundscheck checkbounds(B, I) | |
_unsafe_setindex!(B, X, I) | |
end | |
function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) | |
Bc = B.chunks | |
Ic = I.chunks | |
length(Bc) == length(Ic) || throw_boundserror(B, I) | |
lc = length(Bc) | |
lx = length(X) | |
last_chunk_len = _mod64(length(B)-1)+1 | |
c = 1 | |
for i = 1:lc | |
@inbounds Imsk = Ic[i] | |
@inbounds C = Bc[i] | |
u = UInt64(1) | |
for j = 1:(i < lc ? 64 : last_chunk_len) | |
if Imsk & u != 0 | |
lx < c && throw_setindex_mismatch(X, c) | |
@inbounds x = convert(Bool, X[c]) | |
C = ifelse(x, C | u, C & ~u) | |
c += 1 | |
end | |
u <<= 1 | |
end | |
@inbounds Bc[i] = C | |
end | |
if length(X) != c-1 | |
throw_setindex_mismatch(X, c-1) | |
end | |
return B | |
end | |
## Dequeue functionality ## | |
function push!(B::BitVector, item) | |
# convert first so we don't grow the bitarray if the assignment won't work | |
item = convert(Bool, item) | |
Bc = B.chunks | |
l = _mod64(length(B)) | |
if l == 0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1) | |
Bc[end] = UInt64(0) | |
end | |
B.len += 1 | |
if item | |
B[end] = true | |
end | |
return B | |
end | |
function append!(B::BitVector, items::BitVector) | |
n0 = length(B) | |
n1 = length(items) | |
n1 == 0 && return B | |
Bc = B.chunks | |
k0 = length(Bc) | |
k1 = num_bit_chunks(n0 + n1) | |
if k1 > k0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0) | |
Bc[end] = UInt64(0) | |
end | |
B.len += n1 | |
copy_chunks!(Bc, n0+1, items.chunks, 1, n1) | |
return B | |
end | |
append!(B::BitVector, items::AbstractVector{Bool}) = append!(B, BitArray(items)) | |
append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items)) | |
function prepend!(B::BitVector, items::BitVector) | |
n0 = length(B) | |
n1 = length(items) | |
n1 == 0 && return B | |
Bc = B.chunks | |
k0 = length(Bc) | |
k1 = num_bit_chunks(n0 + n1) | |
if k1 > k0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0) | |
Bc[end] = UInt64(0) | |
end | |
B.len += n1 | |
copy_chunks!(Bc, 1 + n1, Bc, 1, n0) | |
copy_chunks!(Bc, 1, items.chunks, 1, n1) | |
return B | |
end | |
prepend!(B::BitVector, items::AbstractVector{Bool}) = prepend!(B, BitArray(items)) | |
prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items)) | |
function sizehint!(B::BitVector, sz::Integer) | |
ccall(:jl_array_sizehint, Void, (Any, UInt), B.chunks, num_bit_chunks(sz)) | |
return B | |
end | |
function resize!(B::BitVector, n::Integer) | |
n0 = length(B) | |
n == n0 && return B | |
n >= 0 || throw(BoundsError(B, n)) | |
if n < n0 | |
deleteat!(B, n+1:n0) | |
return B | |
end | |
Bc = B.chunks | |
k0 = length(Bc) | |
k1 = num_bit_chunks(Int(n)) | |
if k1 > k0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, k1 - k0) | |
Bc[end] = UInt64(0) | |
end | |
B.len = n | |
return B | |
end | |
function pop!(B::BitVector) | |
isempty(B) && throw(ArgumentError("argument must not be empty")) | |
item = B[end] | |
B[end] = false | |
l = _mod64(length(B)) | |
l == 1 && ccall(:jl_array_del_end, Void, (Any, UInt), B.chunks, 1) | |
B.len -= 1 | |
return item | |
end | |
function unshift!(B::BitVector, item) | |
item = convert(Bool, item) | |
Bc = B.chunks | |
l = _mod64(length(B)) | |
if l == 0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1) | |
Bc[end] = UInt64(0) | |
end | |
B.len += 1 | |
if B.len == 1 | |
Bc[1] = item | |
return B | |
end | |
for i = length(Bc) : -1 : 2 | |
Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63) | |
end | |
Bc[1] = UInt64(item) | (Bc[1] << 1) | |
return B | |
end | |
function shift!(B::BitVector) | |
isempty(B) && throw(ArgumentError("argument must not be empty")) | |
@inbounds begin | |
item = B[1] | |
Bc = B.chunks | |
for i = 1 : length(Bc) - 1 | |
Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63) | |
end | |
l = _mod64(length(B)) | |
if l == 1 | |
ccall(:jl_array_del_end, Void, (Any, UInt), Bc, 1) | |
else | |
Bc[end] >>>= 1 | |
end | |
B.len -= 1 | |
end | |
return item | |
end | |
function insert!(B::BitVector, i::Integer, item) | |
n = length(B) | |
1 <= i <= n+1 || throw(BoundsError(B, i)) | |
item = convert(Bool, item) | |
Bc = B.chunks | |
k, j = get_chunks_id(i) | |
l = _mod64(length(B)) | |
if l == 0 | |
ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, 1) | |
Bc[end] = UInt64(0) | |
end | |
B.len += 1 | |
for t = length(Bc) : -1 : k + 1 | |
Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63) | |
end | |
msk_aft = (_msk64 << j) | |
msk_bef = ~msk_aft | |
Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1) | |
B[i] = item | |
B | |
end | |
function _deleteat!(B::BitVector, i::Integer) | |
k, j = get_chunks_id(i) | |
msk_bef = _msk64 >>> (63 - j) | |
msk_aft = ~msk_bef | |
msk_bef >>>= 1 | |
Bc = B.chunks | |
@inbounds begin | |
Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1) | |
if length(Bc) > k | |
Bc[k] |= (Bc[k + 1] << 63) | |
end | |
for t = k + 1 : length(Bc) - 1 | |
Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63) | |
end | |
l = _mod64(length(B)) | |
if l == 1 | |
ccall(:jl_array_del_end, Void, (Any, UInt), Bc, 1) | |
elseif length(Bc) > k | |
Bc[end] >>>= 1 | |
end | |
end | |
B.len -= 1 | |
return B | |
end | |
function deleteat!(B::BitVector, i::Integer) | |
n = length(B) | |
1 <= i <= n || throw(BoundsError(B, i)) | |
return _deleteat!(B, i) | |
end | |
function deleteat!(B::BitVector, r::UnitRange{Int}) | |
n = length(B) | |
i_f = first(r) | |
i_l = last(r) | |
1 <= i_f || throw(BoundsError(B, i_f)) | |
i_l <= n || throw(BoundsError(B, n+1)) | |
Bc = B.chunks | |
new_l = length(B) - length(r) | |
delta_k = num_bit_chunks(new_l) - length(Bc) | |
copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l) | |
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k) | |
B.len = new_l | |
if new_l > 0 | |
Bc[end] &= _msk_end(new_l) | |
end | |
return B | |
end | |
function deleteat!(B::BitVector, inds) | |
n = new_l = length(B) | |
s = start(inds) | |
done(inds, s) && return B | |
Bc = B.chunks | |
(p, s) = next(inds, s) | |
q = p+1 | |
new_l -= 1 | |
while !done(inds, s) | |
(i,s) = next(inds, s) | |
if !(q <= i <= n) | |
i < q && throw(ArgumentError("indices must be unique and sorted")) | |
throw(BoundsError(B, i)) | |
end | |
new_l -= 1 | |
if i > q | |
copy_chunks!(Bc, p, Bc, q, i-q) | |
p += i-q | |
end | |
q = i+1 | |
end | |
q <= n && copy_chunks!(Bc, p, Bc, q, n-q+1) | |
delta_k = num_bit_chunks(new_l) - length(Bc) | |
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k) | |
B.len = new_l | |
if new_l > 0 | |
Bc[end] &= _msk_end(new_l) | |
end | |
return B | |
end | |
function splice!(B::BitVector, i::Integer) | |
n = length(B) | |
1 <= i <= n || throw(BoundsError(B, i)) | |
v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView | |
_deleteat!(B, i) | |
return v | |
end | |
const _default_bit_splice = BitVector(0) | |
function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice) | |
n = length(B) | |
i_f = first(r) | |
i_l = last(r) | |
1 <= i_f <= n+1 || throw(BoundsError(B, i_f)) | |
i_l <= n || throw(BoundsError(B, n+1)) | |
Bins = convert(BitArray, ins) | |
if (i_f > n) | |
append!(B, Bins) | |
return BitVector(0) | |
end | |
v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView | |
Bc = B.chunks | |
lins = length(Bins) | |
ldel = length(r) | |
new_l = length(B) + lins - ldel | |
delta_k = num_bit_chunks(new_l) - length(Bc) | |
delta_k > 0 && ccall(:jl_array_grow_end, Void, (Any, UInt), Bc, delta_k) | |
copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l) | |
copy_chunks!(Bc, i_f, Bins.chunks, 1, lins) | |
delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, UInt), Bc, -delta_k) | |
B.len = new_l | |
if new_l > 0 | |
Bc[end] &= _msk_end(new_l) | |
end | |
return v | |
end | |
function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins) | |
Bins = BitArray(length(ins)) | |
i = 1 | |
for x in ins | |
Bins[i] = Bool(x) | |
i += 1 | |
end | |
return splice!(B, r, Bins) | |
end | |
function empty!(B::BitVector) | |
ccall(:jl_array_del_end, Void, (Any, UInt), B.chunks, length(B.chunks)) | |
B.len = 0 | |
return B | |
end | |
## Misc functions | |
broadcast(::typeof(abs), B::BitArray) = copy(B) | |
## Unary operators ## | |
function (-)(B::BitArray) | |
A = zeros(Int, size(B)) | |
l = length(B) | |
l == 0 && return A | |
Bc = B.chunks | |
ind = 1 | |
for i = 1:length(Bc)-1 | |
u = UInt64(1) | |
c = Bc[i] | |
for j = 1:64 | |
if c & u != 0 | |
A[ind] = -1 | |
end | |
ind += 1 | |
u <<= 1 | |
end | |
end | |
u = UInt64(1) | |
c = Bc[end] | |
for j = 0:_mod64(l-1) | |
if c & u != 0 | |
A[ind] = -1 | |
end | |
ind += 1 | |
u <<= 1 | |
end | |
return A | |
end | |
sign(B::BitArray) = copy(B) | |
function (~)(B::BitArray) | |
C = similar(B) | |
Bc = B.chunks | |
if !isempty(Bc) | |
Cc = C.chunks | |
for i = 1:length(Bc) | |
Cc[i] = ~Bc[i] | |
end | |
Cc[end] &= _msk_end(B) | |
end | |
return C | |
end | |
""" | |
flipbits!(B::BitArray{N}) -> BitArray{N} | |
Performs a bitwise not operation on `B`. See [`~`](:ref:`~ operator <~>`). | |
```jldoctest | |
julia> A = trues(2,2) | |
2×2 BitArray{2}: | |
true true | |
true true | |
julia> flipbits!(A) | |
2×2 BitArray{2}: | |
false false | |
false false | |
``` | |
""" | |
function flipbits!(B::BitArray) | |
Bc = B.chunks | |
@inbounds if !isempty(Bc) | |
for i = 1:length(Bc) | |
Bc[i] = ~Bc[i] | |
end | |
Bc[end] &= _msk_end(B) | |
end | |
return B | |
end | |
!(B::BitArray) = ~B | |
## Binary arithmetic operators ## | |
for f in (:+, :-) | |
@eval function ($f)(A::BitArray, B::BitArray) | |
r = Array{Int}(promote_shape(size(A), size(B))) | |
ai = start(A) | |
bi = start(B) | |
ri = 1 | |
while !done(A, ai) | |
a, ai = next(A, ai) | |
b, bi = next(B, bi) | |
@inbounds r[ri] = ($f)(a, b) | |
ri += 1 | |
end | |
return r | |
end | |
end | |
for (f) in (:.+, :.-) | |
for (arg1, arg2, T, t) in ((:(B::BitArray), :(x::Bool) , Int , (:b, :x)), | |
(:(B::BitArray), :(x::Number) , :(Bool, typeof(x)), (:b, :x)), | |
(:(x::Bool) , :(B::BitArray), Int , (:x, :b)), | |
(:(x::Number) , :(B::BitArray), :(typeof(x), Bool), (:x, :b))) | |
@eval function ($f)($arg1, $arg2) | |
$(if T === Int | |
quote | |
r = Array{Int}(size(B)) | |
end | |
else | |
quote | |
T = promote_op($f, $(T.args[1]), $(T.args[2])) | |
T === Any && return [($f)($(t[1]), $(t[2])) for b in B] | |
r = Array{T}(size(B)) | |
end | |
end) | |
bi = start(B) | |
ri = 1 | |
while !done(B, bi) | |
b, bi = next(B, bi) | |
@inbounds r[ri] = ($f)($(t[1]), $(t[2])) | |
ri += 1 | |
end | |
return r | |
end | |
end | |
end | |
for f in (:/, :\) | |
@eval begin | |
($f)(A::BitArray, B::BitArray) = ($f)(Array(A), Array(B)) | |
end | |
end | |
(/)(B::BitArray, x::Number) = (/)(Array(B), x) | |
(/)(x::Number, B::BitArray) = (/)(x, Array(B)) | |
function div(A::BitArray, B::BitArray) | |
shp = promote_shape(size(A), size(B)) | |
all(B) || throw(DivideError()) | |
return reshape(copy(A), shp) | |
end | |
div(A::BitArray, B::Array{Bool}) = div(A, BitArray(B)) | |
div(A::Array{Bool}, B::BitArray) = div(BitArray(A), B) | |
function div(B::BitArray, x::Bool) | |
return x ? copy(B) : throw(DivideError()) | |
end | |
function div(x::Bool, B::BitArray) | |
all(B) || throw(DivideError()) | |
return x ? trues(size(B)) : falses(size(B)) | |
end | |
function div(x::Number, B::BitArray) | |
all(B) || throw(DivideError()) | |
y = div(x, true) | |
return fill(y, size(B)) | |
end | |
function mod(A::BitArray, B::BitArray) | |
shp = promote_shape(size(A), size(B)) | |
all(B) || throw(DivideError()) | |
return falses(shp) | |
end | |
mod(A::BitArray, B::Array{Bool}) = mod(A, BitArray(B)) | |
mod(A::Array{Bool}, B::BitArray) = mod(BitArray(A), B) | |
function mod(B::BitArray, x::Bool) | |
return x ? falses(size(B)) : throw(DivideError()) | |
end | |
function mod(x::Bool, B::BitArray) | |
all(B) || throw(DivideError()) | |
return falses(size(B)) | |
end | |
function mod(x::Number, B::BitArray) | |
all(B) || throw(DivideError()) | |
y = mod(x, true) | |
return fill(y, size(B)) | |
end | |
for f in (:div, :mod) | |
@eval begin | |
function ($f)(B::BitArray, x::Number) | |
T = promote_op($f, Bool, typeof(x)) | |
T === Any && return [($f)(b, x) for b in B] | |
F = Array{T}(size(B)) | |
for i = 1:length(F) | |
F[i] = ($f)(B[i], x) | |
end | |
return F | |
end | |
end | |
end | |
function (&)(B::BitArray, x::Bool) | |
x ? copy(B) : falses(size(B)) | |
end | |
(&)(x::Bool, B::BitArray) = B & x | |
function (|)(B::BitArray, x::Bool) | |
x ? trues(size(B)) : copy(B) | |
end | |
(|)(x::Bool, B::BitArray) = B | x | |
function ($)(B::BitArray, x::Bool) | |
x ? ~B : copy(B) | |
end | |
($)(x::Bool, B::BitArray) = B $ x | |
for f in (:&, :|, :$) | |
@eval begin | |
function ($f)(A::BitArray, B::BitArray) | |
F = BitArray(promote_shape(size(A),size(B))...) | |
Fc = F.chunks | |
Ac = A.chunks | |
Bc = B.chunks | |
(isempty(Ac) || isempty(Bc)) && return F | |
for i = 1:length(Fc) | |
Fc[i] = ($f)(Ac[i], Bc[i]) | |
end | |
Fc[end] &= _msk_end(F) | |
return F | |
end | |
($f)(A::DenseArray{Bool}, B::BitArray) = ($f)(BitArray(A), B) | |
($f)(B::BitArray, A::DenseArray{Bool}) = ($f)(B, BitArray(A)) | |
($f)(x::Number, B::BitArray) = ($f)(x, Array(B)) | |
($f)(B::BitArray, x::Number) = ($f)(Array(B), x) | |
end | |
end | |
function (.^)(B::BitArray, x::Bool) | |
x ? copy(B) : trues(size(B)) | |
end | |
function (.^)(x::Bool, B::BitArray) | |
x ? trues(size(B)) : ~B | |
end | |
function (.^)(x::Number, B::BitArray) | |
z = x ^ false | |
u = x ^ true | |
reshape([ B[i] ? u : z for i = 1:length(B) ], size(B)) | |
end | |
function (.^)(B::BitArray, x::Integer) | |
x == 0 && return trues(size(B)) | |
x < 0 && throw(DomainError()) | |
return copy(B) | |
end | |
function (.^){T<:Number}(B::BitArray, x::T) | |
x == 0 && return ones(typeof(true ^ x), size(B)) | |
T <: Real && x > 0 && return convert(Array{T}, B) | |
z = nothing | |
u = nothing | |
zerr = nothing | |
uerr = nothing | |
try | |
z = false^x | |
catch err | |
zerr = err | |
end | |
try | |
u = true^x | |
catch err | |
uerr = err | |
end | |
if zerr === nothing && uerr === nothing | |
t = promote_type(typeof(z), typeof(u)) | |
elseif zerr === nothing | |
t = typeof(z) | |
else | |
t = typeof(u) | |
end | |
F = Array{t}(size(B)) | |
for i = 1:length(B) | |
if B[i] | |
if uerr === nothing | |
F[i] = u | |
else | |
throw(uerr) | |
end | |
else | |
if zerr === nothing | |
F[i] = z | |
else | |
throw(zerr) | |
end | |
end | |
end | |
return F | |
end | |
(.*)(x::Bool, B::BitArray) = x & B | |
(.*)(B::BitArray, x::Bool) = B & x | |
(.*)(x::Number, B::BitArray) = x .* Array(B) | |
(.*)(B::BitArray, x::Number) = Array(B) .* x | |
## promotion to complex ## | |
# TODO? | |
## comparison operators ## | |
function (==)(A::BitArray, B::BitArray) | |
size(A) != size(B) && return false | |
return A.chunks == B.chunks | |
end | |
## Data movement ## | |
# TODO some of this could be optimized | |
function slicedim(A::BitArray, d::Integer, i::Integer) | |
d_in = size(A) | |
leading = d_in[1:(d-1)] | |
d_out = tuple(leading..., d_in[(d+1):end]...) | |
M = prod(leading) | |
N = length(A) | |
stride = M * d_in[d] | |
B = BitArray(d_out) | |
index_offset = 1 + (i-1)*M | |
l = 1 | |
if M == 1 | |
for j = 0:stride:(N-stride) | |
B[l] = A[j + index_offset] | |
l += 1 | |
end | |
else | |
for j = 0:stride:(N-stride) | |
offs = j + index_offset | |
for k = 0:(M-1) | |
B[l] = A[offs + k] | |
l += 1 | |
end | |
end | |
end | |
return B | |
end | |
function flipdim(A::BitArray, d::Integer) | |
nd = ndims(A) | |
1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) | |
sd = size(A, d) | |
sd == 1 && return copy(A) | |
B = similar(A) | |
nnd = 0 | |
for i = 1:nd | |
nnd += Int(size(A,i)==1 || i==d) | |
end | |
if nnd == nd | |
# flip along the only non-singleton dimension | |
for i = 1:sd | |
B[i] = A[sd+1-i] | |
end | |
return B | |
end | |
d_in = size(A) | |
leading = d_in[1:(d-1)] | |
M = prod(leading) | |
N = length(A) | |
stride = M * sd | |
if M == 1 | |
for j = 0:stride:(N-stride) | |
for i = 1:sd | |
ri = sd+1-i | |
B[j + ri] = A[j + i] | |
end | |
end | |
else | |
for i = 1:sd | |
ri = sd+1-i | |
for j=0:stride:(N-stride) | |
offs = j + 1 + (i-1)*M | |
boffs = j + 1 + (ri-1)*M | |
copy_chunks!(B.chunks, boffs, A.chunks, offs, M) | |
end | |
end | |
end | |
return B | |
end | |
function reverse_bits(src::UInt64) | |
z = src | |
z = ((z >>> 1) & 0x5555555555555555) | ((z << 1) & 0xaaaaaaaaaaaaaaaa) | |
z = ((z >>> 2) & 0x3333333333333333) | ((z << 2) & 0xcccccccccccccccc) | |
z = ((z >>> 4) & 0x0f0f0f0f0f0f0f0f) | ((z << 4) & 0xf0f0f0f0f0f0f0f0) | |
z = ((z >>> 8) & 0x00ff00ff00ff00ff) | ((z << 8) & 0xff00ff00ff00ff00) | |
z = ((z >>> 16) & 0x0000ffff0000ffff) | ((z << 16) & 0xffff0000ffff0000) | |
return ((z >>> 32) & 0x00000000ffffffff) | ((z << 32) & 0xffffffff00000000) | |
end | |
function reverse!(B::BitVector) | |
# Basic idea: each chunk is divided into two blocks of size k = n % 64, and | |
# h = 64 - k. Walk from either end (with indexes i and j) reversing chunks | |
# and separately ORing their two blocks into place. | |
# | |
# chunk 3 chunk 2 chunk 1 | |
# ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐ | |
# │000000000000000│ E ││ D │ C ││ B │ A │ | |
# └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘ | |
# k h k h k | |
# yielding; | |
# ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐ | |
# │000000000000000│ A' ││ B' │ C' ││ D' │ E' │ | |
# └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘ | |
n = length(B) | |
n == 0 && return B | |
k = _mod64(n+63) + 1 | |
h = 64 - k | |
i, j = 0, length(B.chunks) | |
u = UInt64(0) | |
v = reverse_bits(B.chunks[j]) | |
B.chunks[j] = 0 | |
@inbounds while true | |
i += 1 | |
if i == j | |
break | |
end | |
u = reverse_bits(B.chunks[i]) | |
B.chunks[i] = 0 | |
B.chunks[j] |= u >>> h | |
B.chunks[i] |= v >>> h | |
j -= 1 | |
if i == j | |
break | |
end | |
v = reverse_bits(B.chunks[j]) | |
B.chunks[j] = 0 | |
B.chunks[i] |= v << k | |
B.chunks[j] |= u << k | |
end | |
if isodd(length(B.chunks)) | |
B.chunks[i] |= v >>> h | |
else | |
B.chunks[i] |= u << k | |
end | |
return B | |
end | |
reverse(v::BitVector) = reverse!(copy(v)) | |
function (<<)(B::BitVector, i::Int) | |
n = length(B) | |
i == 0 && return copy(B) | |
A = falses(n) | |
i < n && copy_chunks!(A.chunks, 1, B.chunks, i+1, n-i) | |
return A | |
end | |
function (>>>)(B::BitVector, i::Int) | |
n = length(B) | |
i == 0 && return copy(B) | |
A = falses(n) | |
i < n && copy_chunks!(A.chunks, i+1, B.chunks, 1, n-i) | |
return A | |
end | |
(>>)(B::BitVector, i::Int) = B >>> 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!`](:func:`rol!`). | |
```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) | |
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) | |
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!`](:func:`ror!`). | |
```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) | |
## countnz & find ## | |
function countnz(B::BitArray) | |
n = 0 | |
Bc = B.chunks | |
@inbounds for i = 1:length(Bc) | |
n += count_ones(Bc[i]) | |
end | |
return n | |
end | |
# returns the index of the next non-zero element, or 0 if all zeros | |
function findnext(B::BitArray, start::Integer) | |
start > 0 || throw(BoundsError(B, start)) | |
start > length(B) && return 0 | |
Bc = B.chunks | |
chunk_start = _div64(start-1)+1 | |
within_chunk_start = _mod64(start-1) | |
mask = _msk64 << within_chunk_start | |
@inbounds begin | |
if Bc[chunk_start] & mask != 0 | |
return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1 | |
end | |
for i = chunk_start+1:length(Bc) | |
if Bc[i] != 0 | |
return (i-1) << 6 + trailing_zeros(Bc[i]) + 1 | |
end | |
end | |
end | |
return 0 | |
end | |
#findfirst(B::BitArray) = findnext(B, 1) ## defined in array.jl | |
# aux function: same as findnext(~B, start), but performed without temporaries | |
function findnextnot(B::BitArray, start::Integer) | |
start > 0 || throw(BoundsError(B, start)) | |
start > length(B) && return 0 | |
Bc = B.chunks | |
l = length(Bc) | |
l == 0 && return 0 | |
chunk_start = _div64(start-1)+1 | |
within_chunk_start = _mod64(start-1) | |
mask = ~(_msk64 << within_chunk_start) | |
@inbounds if chunk_start < l | |
if Bc[chunk_start] | mask != _msk64 | |
return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1 | |
end | |
for i = chunk_start+1:l-1 | |
if Bc[i] != _msk64 | |
return (i-1) << 6 + trailing_ones(Bc[i]) + 1 | |
end | |
end | |
if Bc[l] != _msk_end(B) | |
return (l-1) << 6 + trailing_ones(Bc[l]) + 1 | |
end | |
elseif Bc[l] | mask != _msk_end(B) | |
return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1 | |
end | |
return 0 | |
end | |
findfirstnot(B::BitArray) = findnextnot(B,1) | |
# returns the index of the first matching element | |
function findnext(B::BitArray, v, start::Integer) | |
v == false && return findnextnot(B, start) | |
v == true && return findnext(B, start) | |
return 0 | |
end | |
#findfirst(B::BitArray, v) = findnext(B, 1, v) ## defined in array.jl | |
# returns the index of the first element for which the function returns true | |
function findnext(testf::Function, B::BitArray, start::Integer) | |
f0::Bool = testf(false) | |
f1::Bool = testf(true) | |
!f0 && f1 && return findnext(B, start) | |
f0 && !f1 && return findnextnot(B, start) | |
start > 0 || throw(BoundsError(B, start)) | |
start > length(B) && return 0 | |
f0 && f1 && return Int(start) | |
return 0 # last case: !f0 && !f1 | |
end | |
#findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1) ## defined in array.jl | |
# returns the index of the previous non-zero element, or 0 if all zeros | |
function findprev(B::BitArray, start::Integer) | |
start > 0 || return 0 | |
start > length(B) && throw(BoundsError(B, start)) | |
Bc = B.chunks | |
chunk_start = _div64(start-1)+1 | |
mask = _msk_end(start) | |
@inbounds begin | |
if Bc[chunk_start] & mask != 0 | |
return (chunk_start-1) << 6 + (64 - leading_zeros(Bc[chunk_start] & mask)) | |
end | |
for i = (chunk_start-1):-1:1 | |
if Bc[i] != 0 | |
return (i-1) << 6 + (64 - leading_zeros(Bc[i])) | |
end | |
end | |
end | |
return 0 | |
end | |
function findprevnot(B::BitArray, start::Integer) | |
start > 0 || return 0 | |
start > length(B) && throw(BoundsError(B, start)) | |
Bc = B.chunks | |
chunk_start = _div64(start-1)+1 | |
mask = ~_msk_end(start) | |
@inbounds begin | |
if Bc[chunk_start] | mask != _msk64 | |
return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask)) | |
end | |
for i = chunk_start-1:-1:1 | |
if Bc[i] != _msk64 | |
return (i-1) << 6 + (64 - leading_ones(Bc[i])) | |
end | |
end | |
end | |
return 0 | |
end | |
findlastnot(B::BitArray) = findprevnot(B, length(B)) | |
# returns the index of the previous matching element | |
function findprev(B::BitArray, v, start::Integer) | |
v == false && return findprevnot(B, start) | |
v == true && return findprev(B, start) | |
return 0 | |
end | |
#findlast(B::BitArray, v) = findprev(B, 1, v) ## defined in array.jl | |
# returns the index of the previous element for which the function returns true | |
function findprev(testf::Function, B::BitArray, start::Integer) | |
f0::Bool = testf(false) | |
f1::Bool = testf(true) | |
!f0 && f1 && return findprev(B, start) | |
f0 && !f1 && return findprevnot(B, start) | |
start > 0 || return 0 | |
start > length(B) && throw(BoundsError(B, start)) | |
f0 && f1 && return Int(start) | |
return 0 # last case: !f0 && !f1 | |
end | |
#findlast(testf::Function, B::BitArray) = findprev(testf, B, 1) ## defined in array.jl | |
function find(B::BitArray) | |
l = length(B) | |
nnzB = countnz(B) | |
I = Array{Int}(nnzB) | |
nnzB == 0 && return I | |
Bc = B.chunks | |
Bcount = 1 | |
Icount = 1 | |
for i = 1:length(Bc)-1 | |
u = UInt64(1) | |
c = Bc[i] | |
for j = 1:64 | |
if c & u != 0 | |
I[Icount] = Bcount | |
Icount += 1 | |
end | |
Bcount += 1 | |
u <<= 1 | |
end | |
end | |
u = UInt64(1) | |
c = Bc[end] | |
for j = 0:_mod64(l-1) | |
if c & u != 0 | |
I[Icount] = Bcount | |
Icount += 1 | |
end | |
Bcount += 1 | |
u <<= 1 | |
end | |
return I | |
end | |
findn(B::BitVector) = find(B) | |
function findn(B::BitMatrix) | |
nnzB = countnz(B) | |
I = Array{Int}(nnzB) | |
J = Array{Int}(nnzB) | |
count = 1 | |
for j = 1:size(B,2), i = 1:size(B,1) | |
if B[i,j] | |
I[count] = i | |
J[count] = j | |
count += 1 | |
end | |
end | |
return I, J | |
end | |
function findnz(B::BitMatrix) | |
I, J = findn(B) | |
return I, J, trues(length(I)) | |
end | |
## Reductions ## | |
sum(A::BitArray, region) = reducedim(+, A, region) | |
sum(B::BitArray) = countnz(B) | |
function all(B::BitArray) | |
isempty(B) && return true | |
Bc = B.chunks | |
@inbounds begin | |
for i = 1:length(Bc)-1 | |
Bc[i] == _msk64 || return false | |
end | |
Bc[end] == _msk_end(B) || return false | |
end | |
return true | |
end | |
function any(B::BitArray) | |
isempty(B) && return false | |
Bc = B.chunks | |
@inbounds begin | |
for i = 1:length(Bc) | |
Bc[i] == 0 || return true | |
end | |
end | |
return false | |
end | |
minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B) | |
maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B) | |
## map over bitarrays ## | |
# Specializing map is even more important for bitarrays than it is for generic | |
# arrays since there can be a 64x speedup by working at the level of Int64 | |
# instead of looping bit-by-bit. | |
map(f::Function, A::BitArray) = map!(f, similar(A), A) | |
map(f::Function, A::BitArray, B::BitArray) = map!(f, similar(A), A, B) | |
map!(f, A::BitArray) = map!(f, A, A) | |
map!(f::typeof(!), dest::BitArray, A::BitArray) = map!(~, dest, A) | |
map!(f::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false) | |
map!(f::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true) | |
immutable BitChunkFunctor{F<:Function} | |
f::F | |
end | |
(f::BitChunkFunctor)(x, y) = f.f(x,y) | |
map!(f::Union{typeof(*), typeof(min)}, dest::BitArray, A::BitArray, B::BitArray) = map!(&, dest, A, B) | |
map!(f::typeof(max), dest::BitArray, A::BitArray, B::BitArray) = map!(|, dest, A, B) | |
map!(f::typeof(!=), dest::BitArray, A::BitArray, B::BitArray) = map!($, dest, A, B) | |
map!(f::Union{typeof(>=), typeof(^)}, dest::BitArray, A::BitArray, B::BitArray) = map!(BitChunkFunctor((p, q) -> p | ~q), dest, A, B) | |
map!(f::typeof(<=), dest::BitArray, A::BitArray, B::BitArray) = map!(BitChunkFunctor((p, q) -> ~p | q), dest, A, B) | |
map!(f::typeof(==), dest::BitArray, A::BitArray, B::BitArray) = map!(BitChunkFunctor((p, q) -> ~(p $ q)), dest, A, B) | |
map!(f::typeof(<), dest::BitArray, A::BitArray, B::BitArray) = map!(BitChunkFunctor((p, q) -> ~p & q), dest, A, B) | |
map!(f::typeof(>), dest::BitArray, A::BitArray, B::BitArray) = map!(BitChunkFunctor((p, q) -> p & ~q), dest, A, B) | |
# If we were able to specialize the function to a known bitwise operation, | |
# map across the chunks. Otherwise, fall-back to the AbstractArray method that | |
# iterates bit-by-bit. | |
function map!(f::Union{typeof(identity), typeof(~)}, dest::BitArray, A::BitArray) | |
size(A) == size(dest) || throw(DimensionMismatch("sizes of dest and A must match")) | |
isempty(A) && return dest | |
destc = dest.chunks | |
Ac = A.chunks | |
for i = 1:(length(Ac)-1) | |
destc[i] = f(Ac[i]) | |
end | |
destc[end] = f(Ac[end]) & _msk_end(A) | |
dest | |
end | |
function map!(f::Union{BitChunkFunctor, typeof(&), typeof(|), typeof($)}, dest::BitArray, A::BitArray, B::BitArray) | |
size(A) == size(B) == size(dest) || throw(DimensionMismatch("sizes of dest, A, and B must all match")) | |
isempty(A) && return dest | |
destc = dest.chunks | |
Ac = A.chunks | |
Bc = B.chunks | |
for i = 1:(length(Ac)-1) | |
destc[i] = f(Ac[i], Bc[i]) | |
end | |
destc[end] = f(Ac[end], Bc[end]) & _msk_end(A) | |
dest | |
end | |
## Filter ## | |
function filter(f, Bs::BitArray) | |
boolmap::Array{Bool} = map(f, Bs) | |
Bs[boolmap] | |
end | |
## Transpose ## | |
transpose(B::BitVector) = reshape(copy(B), 1, length(B)) | |
# fast 8x8 bit transpose from Henry S. Warrens's "Hacker's Delight" | |
# http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt | |
function transpose8x8(x::UInt64) | |
y = x | |
t = (y $ (y >>> 7)) & 0x00aa00aa00aa00aa | |
y = y $ t $ (t << 7) | |
t = (y $ (y >>> 14)) & 0x0000cccc0000cccc | |
y = y $ t $ (t << 14) | |
t = (y $ (y >>> 28)) & 0x00000000f0f0f0f0 | |
return y $ t $ (t << 28) | |
end | |
function form_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) | |
x = UInt64(0) | |
k, l = get_chunks_id(i1 + (i2 - 1) * m) | |
r = 0 | |
for j = 1:8 | |
k > nc && break | |
x |= ((Bc[k] >>> l) & msk8) << r | |
if l + 8 >= 64 && nc > k | |
r0 = 8 - _mod64(l + 8) | |
x |= (Bc[k + 1] & (msk8 >>> r0)) << (r + r0) | |
end | |
k += cgap + (l + cinc >= 64 ? 1 : 0) | |
l = _mod64(l + cinc) | |
r += 8 | |
end | |
return x | |
end | |
# note: assumes B is filled with 0's | |
function put_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, x::UInt64, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64) | |
k, l = get_chunks_id(i1 + (i2 - 1) * m) | |
r = 0 | |
for j = 1:8 | |
k > nc && break | |
Bc[k] |= ((x >>> r) & msk8) << l | |
if l + 8 >= 64 && nc > k | |
r0 = 8 - _mod64(l + 8) | |
Bc[k + 1] |= ((x >>> (r + r0)) & (msk8 >>> r0)) | |
end | |
k += cgap + (l + cinc >= 64 ? 1 : 0) | |
l = _mod64(l + cinc) | |
r += 8 | |
end | |
return | |
end | |
function transpose(B::BitMatrix) | |
l1 = size(B, 1) | |
l2 = size(B, 2) | |
Bt = falses(l2, l1) | |
cgap1, cinc1 = _div64(l1), _mod64(l1) | |
cgap2, cinc2 = _div64(l2), _mod64(l2) | |
Bc = B.chunks | |
Btc = Bt.chunks | |
nc = length(Bc) | |
for i = 1:8:l1 | |
msk8_1 = UInt64(0xff) | |
if (l1 < i + 7) | |
msk8_1 >>>= i + 7 - l1 | |
end | |
for j = 1:8:l2 | |
x = form_8x8_chunk(Bc, i, j, l1, cgap1, cinc1, nc, msk8_1) | |
x = transpose8x8(x) | |
msk8_2 = UInt64(0xff) | |
if (l2 < j + 7) | |
msk8_2 >>>= j + 7 - l2 | |
end | |
put_8x8_chunk(Btc, j, i, x, l2, cgap2, cinc2, nc, msk8_2) | |
end | |
end | |
return Bt | |
end | |
ctranspose(B::BitArray) = transpose(B) | |
## Concatenation ## | |
function hcat(B::BitVector...) | |
height = length(B[1]) | |
for j = 2:length(B) | |
length(B[j]) == height || | |
throw(DimensionMismatch("dimensions must match")) | |
end | |
M = BitArray(height, length(B)) | |
for j = 1:length(B) | |
copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height) | |
end | |
return M | |
end | |
function vcat(V::BitVector...) | |
n = 0 | |
for Vk in V | |
n += length(Vk) | |
end | |
B = BitArray(n) | |
j = 1 | |
for Vk in V | |
copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk)) | |
j += length(Vk) | |
end | |
return B | |
end | |
function hcat(A::Union{BitMatrix,BitVector}...) | |
nargs = length(A) | |
nrows = size(A[1], 1) | |
ncols = 0 | |
dense = true | |
for j = 1:nargs | |
Aj = A[j] | |
nd = ndims(Aj) | |
ncols += (nd==2 ? size(Aj,2) : 1) | |
size(Aj, 1) == nrows || | |
throw(DimensionMismatch("row lengths must match")) | |
end | |
B = BitArray(nrows, ncols) | |
pos = 1 | |
for k = 1:nargs | |
Ak = A[k] | |
n = length(Ak) | |
copy_chunks!(B.chunks, pos, Ak.chunks, 1, n) | |
pos += n | |
end | |
return B | |
end | |
function vcat(A::BitMatrix...) | |
nargs = length(A) | |
nrows = sum(a->size(a, 1), A)::Int | |
ncols = size(A[1], 2) | |
for j = 2:nargs | |
size(A[j], 2) == ncols || | |
throw(DimensionMismatch("column lengths must match")) | |
end | |
B = BitArray(nrows, ncols) | |
Bc = B.chunks | |
nrowsA = [size(a, 1) for a in A] | |
Ac = [a.chunks for a in A] | |
pos_d = 1 | |
pos_s = ones(Int, nargs) | |
for j = 1:ncols, k = 1:nargs | |
copy_chunks!(Bc, pos_d, Ac[k], pos_s[k], nrowsA[k]) | |
pos_s[k] += nrowsA[k] | |
pos_d += nrowsA[k] | |
end | |
return B | |
end | |
function cat(catdim::Integer, X::Integer...) | |
reshape([X...], (ones(Int,catdim-1)..., length(X))) | |
end | |
# general case, specialized for BitArrays and Integers | |
function cat(catdim::Integer, X::Union{BitArray, Integer}...) | |
nargs = length(X) | |
# using integers results in conversion to Array{Int} | |
# (except in the all-Bool case) | |
has_integer = false | |
for a in X | |
if isa(a, Integer) | |
has_integer = true; break | |
end | |
end | |
dimsX = map((a->isa(a,BitArray) ? size(a) : (1,)), X) | |
ndimsX = map((a->isa(a,BitArray) ? ndims(a) : 1), X) | |
d_max = maximum(ndimsX) | |
if catdim > d_max + 1 | |
for i = 1:nargs | |
dimsX[1] == dimsX[i] || | |
throw(DimensionMismatch("all inputs must have same dimensions when concatenating along a higher dimension")) | |
end | |
elseif nargs >= 2 | |
for d = 1:d_max | |
d == catdim && continue | |
len = d <= ndimsX[1] ? dimsX[1][d] : 1 | |
for i = 2:nargs | |
len == (d <= ndimsX[i] ? dimsX[i][d] : 1) || throw(DimensionMismatch("mismatch in dimension $d")) | |
end | |
end | |
end | |
cat_ranges = ntuple(i->(catdim <= ndimsX[i] ? dimsX[i][catdim] : 1), nargs) | |
function compute_dims(d) | |
if d == catdim | |
catdim <= d_max && return sum(cat_ranges) | |
return nargs | |
else | |
d <= ndimsX[1] && return dimsX[1][d] | |
return 1 | |
end | |
end | |
ndimsC = max(catdim, d_max) | |
dimsC = ntuple(compute_dims, ndimsC)::Tuple{Vararg{Int}} | |
typeC = promote_type(map(x->isa(x,BitArray) ? eltype(x) : typeof(x), X)...) | |
if !has_integer || typeC == Bool | |
C = BitArray(dimsC) | |
else | |
C = Array{typeC}(dimsC) | |
end | |
range = 1 | |
for k = 1:nargs | |
nextrange = range + cat_ranges[k] | |
cat_one = ntuple(i->(i != catdim ? (1:dimsC[i]) : (range:nextrange-1)), | |
ndimsC) | |
# note: when C and X are BitArrays, this calls | |
# the special assign with ranges | |
C[cat_one...] = X[k] | |
range = nextrange | |
end | |
return C | |
end | |
# hvcat -> use fallbacks in abstractarray.jl | |
# BitArray I/O | |
write(s::IO, B::BitArray) = write(s, B.chunks) | |
function read!(s::IO, B::BitArray) | |
n = length(B) | |
Bc = B.chunks | |
nc = length(read!(s, Bc)) | |
if length(Bc) > 0 && Bc[end] & _msk_end(n) ≠ Bc[end] | |
Bc[end] &= _msk_end(n) # ensure that the BitArray is not broken | |
throw(DimensionMismatch("read mismatch, found non-zero bits after BitArray length")) | |
end | |
return B | |
end | |
sizeof(B::BitArray) = sizeof(B.chunks) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## boolean conversions ## | |
convert(::Type{Bool}, x::Bool) = x | |
convert(::Type{Bool}, x::Float16) = x==0 ? false : x==1 ? true : throw(InexactError()) | |
convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError()) | |
# promote Bool to any other numeric type | |
promote_rule{T<:Number}(::Type{Bool}, ::Type{T}) = T | |
typemin(::Type{Bool}) = false | |
typemax(::Type{Bool}) = true | |
## boolean operations ## | |
function !(x::Bool) | |
## We need a better heuristic to detect this automatically | |
@_pure_meta | |
return box(Bool,not_int(unbox(Bool,x))) | |
end | |
(~)(x::Bool) = !x | |
(&)(x::Bool, y::Bool) = box(Bool,and_int(unbox(Bool,x),unbox(Bool,y))) | |
(|)(x::Bool, y::Bool) = box(Bool,or_int(unbox(Bool,x),unbox(Bool,y))) | |
($)(x::Bool, y::Bool) = (x!=y) | |
>>(x::Bool, c::Unsigned) = Int(x) >> c | |
<<(x::Bool, c::Unsigned) = Int(x) << c | |
>>>(x::Bool, c::Unsigned) = Int(x) >>> c | |
>>(x::Bool, c::Int) = Int(x) >> c | |
<<(x::Bool, c::Int) = Int(x) << c | |
>>>(x::Bool, c::Int) = Int(x) >>> c | |
>>(x::Bool, c::Integer) = Int(x) >> c | |
<<(x::Bool, c::Integer) = Int(x) << c | |
>>>(x::Bool, c::Integer) = Int(x) >>> c | |
signbit(x::Bool) = false | |
sign(x::Bool) = x | |
abs(x::Bool) = x | |
abs2(x::Bool) = x | |
<(x::Bool, y::Bool) = y&!x | |
<=(x::Bool, y::Bool) = y|!x | |
## do arithmetic as Int ## | |
+(x::Bool) = Int(x) | |
-(x::Bool) = -Int(x) | |
+(x::Bool, y::Bool) = Int(x) + Int(y) | |
-(x::Bool, y::Bool) = Int(x) - Int(y) | |
*(x::Bool, y::Bool) = x & y | |
^(x::Bool, y::Bool) = x | !y | |
^(x::Integer, y::Bool) = ifelse(y, x, one(x)) | |
function +{T<:AbstractFloat}(x::Bool, y::T)::promote_type(Bool,T) | |
return ifelse(x, one(y) + y, y) | |
end | |
+(y::AbstractFloat, x::Bool) = x + y | |
function *{T<:Number}(x::Bool, y::T)::promote_type(Bool,T) | |
return ifelse(x, y, copysign(zero(y), y)) | |
end | |
function *{T<:Unsigned}(x::Bool, y::T)::promote_type(Bool,T) | |
return ifelse(x, y, zero(y)) | |
end | |
*(y::Number, x::Bool) = x * y | |
div(x::Bool, y::Bool) = y ? x : throw(DivideError()) | |
fld(x::Bool, y::Bool) = div(x,y) | |
cld(x::Bool, y::Bool) = div(x,y) | |
rem(x::Bool, y::Bool) = y ? false : throw(DivideError()) | |
mod(x::Bool, y::Bool) = rem(x,y) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# commented-out definitions are implemented in C | |
#abstract Any <: Any | |
#abstract Type{T} | |
#abstract Vararg{T} | |
#Tuple = (Any...) | |
#type Symbol | |
# #opaque | |
#end | |
#type TypeName | |
# name::Symbol | |
#end | |
#type DataType <: Type | |
# name::TypeName | |
# super::Type | |
# parameters::Tuple | |
# names::Tuple | |
# types::Tuple | |
# ctor | |
# instance | |
# size::Int32 | |
# abstract::Bool | |
# mutable::Bool | |
# pointerfree::Bool | |
#end | |
#type Union <: Type | |
# types::Tuple | |
#end | |
#type TypeVar | |
# name::Symbol | |
# lb::Type | |
# ub::Type | |
#end | |
#type TypeConstructor | |
# parameters::Tuple | |
# body | |
#end | |
#immutable Void | |
#end | |
#const nothing = Void() | |
#abstract AbstractArray{T,N} | |
#abstract DenseArray{T,N} <: AbstractArray{T,N} | |
#type Array{T,N} <: DenseArray{T,N} | |
#end | |
#type Module | |
# name::Symbol | |
#end | |
#type Method | |
#end | |
#type MethodInstance | |
#end | |
#type CodeInfo | |
#end | |
#type TypeMapLevel | |
#end | |
#type TypeMapEntry | |
#end | |
#abstract Ref{T} | |
#bitstype {32|64} Ptr{T} <: Ref{T} | |
# types for the front end | |
#type Expr | |
# head::Symbol | |
# args::Array{Any,1} | |
# typ::Any | |
#end | |
#immutable LineNumberNode | |
# line::Int | |
#end | |
#immutable LabelNode | |
# label::Int | |
#end | |
#immutable GotoNode | |
# label::Int | |
#end | |
#immutable QuoteNode | |
# value | |
#end | |
#immutable GlobalRef | |
# mod::Module | |
# name::Symbol | |
#end | |
# type Task | |
# parent::Task | |
# storage::Any | |
# consumers | |
# started::Bool | |
# done::Bool | |
# runnable::Bool | |
# end | |
import Core.Intrinsics.ccall | |
export | |
# key types | |
Any, DataType, Vararg, ANY, NTuple, | |
Tuple, Type, TypeConstructor, TypeName, TypeVar, Union, Void, | |
SimpleVector, AbstractArray, DenseArray, | |
# special objects | |
Function, CodeInfo, Method, MethodTable, TypeMapEntry, TypeMapLevel, | |
Module, Symbol, Task, Array, WeakRef, VecElement, | |
# numeric types | |
Number, Real, Integer, Bool, Ref, Ptr, | |
AbstractFloat, Float16, Float32, Float64, | |
Signed, Int, Int8, Int16, Int32, Int64, Int128, | |
Unsigned, UInt, UInt8, UInt16, UInt32, UInt64, UInt128, | |
# string types | |
Char, DirectIndexString, AbstractString, String, IO, | |
# errors | |
ErrorException, BoundsError, DivideError, DomainError, Exception, InexactError, | |
InterruptException, OutOfMemoryError, ReadOnlyMemoryError, OverflowError, | |
StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, TypeError, | |
# AST representation | |
Expr, GotoNode, LabelNode, LineNumberNode, QuoteNode, | |
GlobalRef, NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, | |
# object model functions | |
fieldtype, getfield, setfield!, nfields, throw, tuple, ===, isdefined, eval, | |
# sizeof # not exported, to avoid conflicting with Base.sizeof | |
# type reflection | |
issubtype, typeof, isa, typeassert, | |
# method reflection | |
applicable, invoke, | |
# constants | |
nothing, Main | |
typealias AnyVector Array{Any,1} | |
abstract Number | |
abstract Real <: Number | |
abstract AbstractFloat <: Real | |
abstract Integer <: Real | |
abstract Signed <: Integer | |
abstract Unsigned <: Integer | |
bitstype 16 Float16 <: AbstractFloat | |
bitstype 32 Float32 <: AbstractFloat | |
bitstype 64 Float64 <: AbstractFloat | |
bitstype 8 Bool <: Integer | |
bitstype 32 Char | |
bitstype 8 Int8 <: Signed | |
bitstype 8 UInt8 <: Unsigned | |
bitstype 16 Int16 <: Signed | |
bitstype 16 UInt16 <: Unsigned | |
bitstype 32 Int32 <: Signed | |
bitstype 32 UInt32 <: Unsigned | |
bitstype 64 Int64 <: Signed | |
bitstype 64 UInt64 <: Unsigned | |
bitstype 128 Int128 <: Signed | |
bitstype 128 UInt128 <: Unsigned | |
if Int === Int64 | |
typealias UInt UInt64 | |
else | |
typealias UInt UInt32 | |
end | |
abstract AbstractString | |
function Typeof end | |
(f::typeof(Typeof))(x::ANY) = isa(x,Type) ? Type{x} : typeof(x) | |
abstract Exception | |
type ErrorException <: Exception | |
msg::AbstractString | |
ErrorException(msg::AbstractString) = new(msg) | |
end | |
immutable BoundsError <: Exception | |
a::Any | |
i::Any | |
BoundsError() = new() | |
BoundsError(a::ANY) = new(a) | |
BoundsError(a::ANY, i::ANY) = new(a,i) | |
end | |
immutable DivideError <: Exception end | |
immutable DomainError <: Exception end | |
immutable OverflowError <: Exception end | |
immutable InexactError <: Exception end | |
immutable OutOfMemoryError <: Exception end | |
immutable ReadOnlyMemoryError<: Exception end | |
immutable SegmentationFault <: Exception end | |
immutable StackOverflowError <: Exception end | |
immutable UndefRefError <: Exception end | |
immutable UndefVarError <: Exception | |
var::Symbol | |
end | |
immutable InterruptException <: Exception end | |
type TypeError <: Exception | |
func::Symbol | |
context::AbstractString | |
expected::Type | |
got | |
end | |
abstract DirectIndexString <: AbstractString | |
immutable String <: AbstractString | |
data::Array{UInt8,1} | |
# required to make String("foo") work (#15120): | |
String(d::Array{UInt8,1}) = new(d) | |
end | |
# This should always be inlined | |
getptls() = ccall(:jl_get_ptls_states, Ptr{Void}, ()) | |
include(fname::String) = ccall(:jl_load_, Any, (Any,), fname) | |
eval(e::ANY) = eval(Main, e) | |
eval(m::Module, e::ANY) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) | |
kwfunc(f::ANY) = ccall(:jl_get_keyword_sorter, Any, (Any,), f) | |
kwftype(t::ANY) = typeof(ccall(:jl_get_kwsorter, Any, (Any,), t.name)) | |
type Box | |
contents::Any | |
Box(x::ANY) = new(x) | |
Box() = new() | |
end | |
# constructors for built-in types | |
type WeakRef | |
value | |
WeakRef() = WeakRef(nothing) | |
WeakRef(v::ANY) = ccall(:jl_gc_new_weakref_th, Ref{WeakRef}, | |
(Ptr{Void}, Any), getptls(), v) | |
end | |
TypeVar(n::Symbol) = | |
ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, Union{}, Any) | |
TypeVar(n::Symbol, ub::ANY) = | |
(isa(ub,Bool) ? | |
ccall(:jl_new_typevar_, Ref{TypeVar}, (Any, Any, Any, Any), n, Union{}, Any, ub) : | |
ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, Union{}, ub::Type)) | |
TypeVar(n::Symbol, lb::ANY, ub::ANY) = | |
(isa(ub,Bool) ? | |
ccall(:jl_new_typevar_, Ref{TypeVar}, (Any, Any, Any, Any), n, Union{}, lb::Type, ub) : | |
ccall(:jl_new_typevar, Ref{TypeVar}, (Any, Any, Any), n, lb::Type, ub::Type)) | |
TypeVar(n::Symbol, lb::ANY, ub::ANY, b::Bool) = | |
ccall(:jl_new_typevar_, Ref{TypeVar}, (Any, Any, Any, Any), n, lb::Type, ub::Type, b) | |
TypeConstructor(p::ANY, t::ANY) = | |
ccall(:jl_new_type_constructor, Ref{TypeConstructor}, (Any, Any), p::SimpleVector, t::Type) | |
Void() = nothing | |
immutable VecElement{T} | |
value::T | |
VecElement(value::T) = new(value) # disable converting constructor in Core | |
end | |
VecElement{T}(arg::T) = VecElement{T}(arg) | |
Expr(args::ANY...) = _expr(args...) | |
# 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(:((::Type{$typ})(n::$argty) = $(Expr(:new, typ, :n)))) | |
_new(:LabelNode, :Int) | |
_new(:GotoNode, :Int) | |
_new(:NewvarNode, :SlotNumber) | |
_new(:QuoteNode, :ANY) | |
_new(:SSAValue, :Int) | |
eval(:((::Type{LineNumberNode})(l::Int) = $(Expr(:new, :LineNumberNode, :l)))) | |
eval(:((::Type{GlobalRef})(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) | |
eval(:((::Type{SlotNumber})(n::Int) = $(Expr(:new, :SlotNumber, :n)))) | |
eval(:((::Type{TypedSlot})(n::Int, t::ANY) = $(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) | |
Task(f::ANY) = ccall(:jl_new_task, Ref{Task}, (Any, Int), f, 0) | |
# simple convert for use by constructors of types in Core | |
# note that there is no actual conversion defined here, | |
# so the methods and ccall's in Core aren't permitted to use convert | |
convert(::Type{Any}, x::ANY) = x | |
convert{T}(::Type{T}, x::T) = x | |
cconvert{T}(::Type{T}, x) = convert(T, x) | |
unsafe_convert{T}(::Type{T}, x::T) = x | |
typealias NTuple{N,T} Tuple{Vararg{T,N}} | |
# primitive array constructors | |
(::Type{Array{T,N}}){T,N}(d::NTuple{N,Int}) = | |
ccall(:jl_new_array, Array{T,N}, (Any,Any), Array{T,N}, d) | |
(::Type{Array{T,1}}){T}(d::NTuple{1,Int}) = Array{T,1}(getfield(d,1)) | |
(::Type{Array{T,2}}){T}(d::NTuple{2,Int}) = Array{T,2}(getfield(d,1), getfield(d,2)) | |
(::Type{Array{T,3}}){T}(d::NTuple{3,Int}) = Array{T,3}(getfield(d,1), getfield(d,2), getfield(d,3)) | |
(::Type{Array{T,N}}){T,N}(d::Vararg{Int, N}) = ccall(:jl_new_array, Array{T,N}, (Any,Any), Array{T,N}, d) | |
(::Type{Array{T,1}}){T}(m::Int) = | |
ccall(:jl_alloc_array_1d, Array{T,1}, (Any,Int), Array{T,1}, m) | |
(::Type{Array{T,2}}){T}(m::Int, n::Int) = | |
ccall(:jl_alloc_array_2d, Array{T,2}, (Any,Int,Int), Array{T,2}, m, n) | |
(::Type{Array{T,3}}){T}(m::Int, n::Int, o::Int) = | |
ccall(:jl_alloc_array_3d, Array{T,3}, (Any,Int,Int,Int), Array{T,3}, m, n, o) | |
(::Type{Array{T}}){T,N}(d::NTuple{N,Int}) = Array{T,N}(d) | |
(::Type{Array{T}}){T}(m::Int) = Array{T,1}(m) | |
(::Type{Array{T}}){T}(m::Int, n::Int) = Array{T,2}(m, n) | |
(::Type{Array{T}}){T}(m::Int, n::Int, o::Int) = Array{T,3}(m, n, o) | |
(::Type{Array{T,1}}){T}() = Array{T,1}(0) | |
(::Type{Array{T,2}}){T}() = Array{T,2}(0, 0) | |
# TODO: possibly turn these into deprecations | |
Array{T,N}(::Type{T}, d::NTuple{N,Int}) = Array{T,N}(d) | |
Array{T}(::Type{T}, d::Int...) = Array(T, d) | |
Array{T}(::Type{T}, m::Int) = Array{T,1}(m) | |
Array{T}(::Type{T}, m::Int,n::Int) = Array{T,2}(m,n) | |
Array{T}(::Type{T}, m::Int,n::Int,o::Int) = Array{T,3}(m,n,o) | |
# docsystem basics | |
macro doc(x...) | |
atdoc(x...) | |
end | |
macro __doc__(x) | |
Expr(:escape, Expr(:block, Expr(:meta, :doc), x)) | |
end | |
macro doc_str(s) | |
Expr(:escape, s) | |
end | |
atdoc = (str, expr) -> Expr(:escape, expr) | |
atdoc!(λ) = global atdoc = λ | |
# simple stand-alone print definitions for debugging | |
abstract IO | |
type CoreSTDOUT <: IO end | |
type CoreSTDERR <: IO end | |
const STDOUT = CoreSTDOUT() | |
const STDERR = CoreSTDERR() | |
io_pointer(::CoreSTDOUT) = Intrinsics.pointerref(Intrinsics.cglobal(:jl_uv_stdout, Ptr{Void}), 1, 1) | |
io_pointer(::CoreSTDERR) = Intrinsics.pointerref(Intrinsics.cglobal(:jl_uv_stderr, Ptr{Void}), 1, 1) | |
unsafe_write(io::IO, x::Ptr{UInt8}, nb::UInt) = | |
(ccall(:jl_uv_puts, Void, (Ptr{Void}, Ptr{UInt8}, UInt), io_pointer(io), x, nb); nb) | |
unsafe_write(io::IO, x::Ptr{UInt8}, nb::Int) = | |
(ccall(:jl_uv_puts, Void, (Ptr{Void}, Ptr{UInt8}, Int), io_pointer(io), x, nb); nb) | |
write(io::IO, x::UInt8) = | |
(ccall(:jl_uv_putb, Void, (Ptr{Void}, UInt8), io_pointer(io), x); 1) | |
function write(io::IO, x::String) | |
nb = sizeof(x.data) | |
unsafe_write(io, ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), x.data), nb) | |
return nb | |
end | |
show(io::IO, x::ANY) = ccall(:jl_static_show, Void, (Ptr{Void}, Any), io_pointer(io), x) | |
print(io::IO, x::Char) = ccall(:jl_uv_putc, Void, (Ptr{Void}, Char), io_pointer(io), x) | |
print(io::IO, x::String) = write(io, x) | |
print(io::IO, x::ANY) = show(io, x) | |
print(io::IO, x::ANY, a::ANY...) = (print(io, x); print(io, a...)) | |
println(io::IO) = write(io, 0x0a) # 0x0a = '\n' | |
println(io::IO, x::ANY...) = (print(io, x...); println(io)) | |
show(a::ANY) = show(STDOUT, a) | |
print(a::ANY...) = print(STDOUT, a...) | |
println(a::ANY...) = println(STDOUT, a...) | |
ccall(:jl_set_istopmod, Void, (Bool,), true) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Broadcast | |
using Base.Cartesian | |
using Base: promote_eltype_op, linearindices, tail, OneTo, to_shape, | |
_msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache | |
import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .÷, .%, .<<, .>>, .^ | |
import Base: broadcast | |
export broadcast!, bitbroadcast, dotview | |
export broadcast_getindex, broadcast_setindex! | |
## Broadcasting utilities ## | |
# fallback for broadcasting with zero arguments and some special cases | |
broadcast(f) = f() | |
@inline broadcast(f, x::Number...) = f(x...) | |
@inline broadcast{N}(f, t::NTuple{N}, ts::Vararg{NTuple{N}}) = map(f, t, ts...) | |
@inline broadcast(f, As::AbstractArray...) = broadcast_t(f, promote_eltype_op(f, As...), As...) | |
# special cases for "X .= ..." (broadcast!) assignments | |
broadcast!(::typeof(identity), X::AbstractArray, x::Number) = fill!(X, x) | |
broadcast!(f, X::AbstractArray) = fill!(X, f()) | |
broadcast!(f, X::AbstractArray, x::Number...) = fill!(X, f(x...)) | |
function broadcast!{T,S,N}(::typeof(identity), x::AbstractArray{T,N}, y::AbstractArray{S,N}) | |
check_broadcast_shape(broadcast_indices(x), broadcast_indices(y)) | |
copy!(x, y) | |
end | |
# logic for deciding the resulting container type | |
containertype(x) = containertype(typeof(x)) | |
containertype(::Type) = Any | |
containertype{T<:Tuple}(::Type{T}) = Tuple | |
containertype{T<:AbstractArray}(::Type{T}) = Array | |
containertype(ct1, ct2) = promote_containertype(containertype(ct1), containertype(ct2)) | |
@inline containertype(ct1, ct2, cts...) = promote_containertype(containertype(ct1), containertype(ct2, cts...)) | |
promote_containertype(::Type{Array}, ::Type{Array}) = Array | |
promote_containertype(::Type{Array}, ct) = Array | |
promote_containertype(ct, ::Type{Array}) = Array | |
promote_containertype(::Type{Tuple}, ::Type{Any}) = Tuple | |
promote_containertype(::Type{Any}, ::Type{Tuple}) = Tuple | |
promote_containertype{T}(::Type{T}, ::Type{T}) = T | |
## Calculate the broadcast indices of the arguments, or error if incompatible | |
# array inputs | |
broadcast_indices() = () | |
broadcast_indices(A) = broadcast_indices(containertype(A), A) | |
broadcast_indices(::Type{Any}, A) = () | |
broadcast_indices(::Type{Tuple}, A) = (OneTo(length(A)),) | |
broadcast_indices(::Type{Array}, A) = indices(A) | |
@inline broadcast_indices(A, B...) = broadcast_shape((), broadcast_indices(A), map(broadcast_indices, B)...) | |
# shape (i.e., tuple-of-indices) inputs | |
broadcast_shape(shape::Tuple) = shape | |
@inline broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs((), shape, shape1), shapes...) | |
# _bcs consolidates two shapes into a single output shape | |
_bcs(out, ::Tuple{}, ::Tuple{}) = out | |
@inline _bcs(out, ::Tuple{}, newshape) = _bcs((out..., newshape[1]), (), tail(newshape)) | |
@inline _bcs(out, shape, ::Tuple{}) = _bcs((out..., shape[1]), tail(shape), ()) | |
@inline function _bcs(out, shape, newshape) | |
newout = _bcs1(shape[1], newshape[1]) | |
_bcs((out..., newout), tail(shape), tail(newshape)) | |
end | |
# _bcs1 handles the logic for a single dimension | |
_bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size")))) | |
_bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch("arrays could not be broadcast to a common size"))) | |
_bcs1(a, b::Integer) = _bcs1(b, a) | |
_bcs1(a, b) = _bcsm(b, a) ? b : (_bcsm(a, b) ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size"))) | |
# _bcsm tests whether the second index is consistent with the first | |
_bcsm(a, b) = a == b || length(b) == 1 | |
_bcsm(a, b::Number) = b == 1 | |
_bcsm(a::Number, b::Number) = a == b || b == 1 | |
## Check that all arguments are broadcast compatible with shape | |
# comparing one input against a shape | |
check_broadcast_shape(shp) = nothing | |
check_broadcast_shape(shp, ::Tuple{}) = nothing | |
check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing | |
check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions")) | |
function check_broadcast_shape(shp, Ashp::Tuple) | |
_bcsm(shp[1], Ashp[1]) || throw(DimensionMismatch("array could not be broadcast to match destination")) | |
check_broadcast_shape(tail(shp), tail(Ashp)) | |
end | |
check_broadcast_indices(shp, A) = check_broadcast_shape(shp, broadcast_indices(A)) | |
# comparing many inputs | |
@inline function check_broadcast_indices(shp, A, As...) | |
check_broadcast_indices(shp, A) | |
check_broadcast_indices(shp, As...) | |
end | |
## Indexing manipulations | |
# newindex(I, keep, Idefault) replaces a CartesianIndex `I` with something that | |
# is appropriate for a particular broadcast array/scalar. `keep` is a | |
# NTuple{N,Bool}, where keep[d] == true means that one should preserve | |
# I[d]; if false, replace it with Idefault[d]. | |
@inline newindex(I::CartesianIndex, keep, Idefault) = CartesianIndex(_newindex(I.I, keep, Idefault)) | |
@inline _newindex(I, keep, Idefault) = | |
(ifelse(keep[1], I[1], Idefault[1]), _newindex(tail(I), tail(keep), tail(Idefault))...) | |
@inline _newindex(I, keep::Tuple{}, Idefault) = () # truncate if keep is shorter than I | |
# newindexer(shape, A) generates `keep` and `Idefault` (for use by | |
# `newindex` above) for a particular array `A`, given the | |
# broadcast_indices `shape` | |
# `keep` is equivalent to map(==, indices(A), shape) (but see #17126) | |
@inline newindexer(shape, A) = shapeindexer(shape, broadcast_indices(A)) | |
@inline shapeindexer(shape, indsA::Tuple{}) = (), () | |
@inline function shapeindexer(shape, indsA::Tuple) | |
ind1 = indsA[1] | |
keep, Idefault = shapeindexer(tail(shape), tail(indsA)) | |
(shape[1] == ind1, keep...), (first(ind1), Idefault...) | |
end | |
# Equivalent to map(x->newindexer(shape, x), As) (but see #17126) | |
map_newindexer(shape, ::Tuple{}) = (), () | |
@inline function map_newindexer(shape, As) | |
A1 = As[1] | |
keeps, Idefaults = map_newindexer(shape, tail(As)) | |
keep, Idefault = newindexer(shape, A1) | |
(keep, keeps...), (Idefault, Idefaults...) | |
end | |
@inline _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I) | |
@inline _broadcast_getindex(::Type{Any}, A, I) = A | |
@inline _broadcast_getindex(::Any, A, I) = A[I] | |
## Broadcasting core | |
# nargs encodes the number of As arguments (which matches the number | |
# of keeps). The first two type parameters are to ensure specialization. | |
@generated function _broadcast!{K,ID,AT,nargs}(f, B::AbstractArray, keeps::K, Idefaults::ID, As::AT, ::Type{Val{nargs}}) | |
quote | |
$(Expr(:meta, :noinline)) | |
# destructure the keeps and As tuples | |
@nexprs $nargs i->(A_i = As[i]) | |
@nexprs $nargs i->(keep_i = keeps[i]) | |
@nexprs $nargs i->(Idefault_i = Idefaults[i]) | |
@simd for I in CartesianRange(indices(B)) | |
# reverse-broadcast the indices | |
@nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) | |
# extract array values | |
@nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) | |
# call the function and store the result | |
@inbounds B[I] = @ncall $nargs f val | |
end | |
end | |
end | |
# For BitArray outputs, we cache the result in a "small" Vector{Bool}, | |
# and then copy in chunks into the output | |
@generated function _broadcast!{K,ID,AT,nargs}(f, B::BitArray, keeps::K, Idefaults::ID, As::AT, ::Type{Val{nargs}}) | |
quote | |
$(Expr(:meta, :noinline)) | |
# destructure the keeps and As tuples | |
@nexprs $nargs i->(A_i = As[i]) | |
@nexprs $nargs i->(keep_i = keeps[i]) | |
@nexprs $nargs i->(Idefault_i = Idefaults[i]) | |
C = Vector{Bool}(bitcache_size) | |
Bc = B.chunks | |
ind = 1 | |
cind = 1 | |
@simd for I in CartesianRange(indices(B)) | |
# reverse-broadcast the indices | |
@nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) | |
# extract array values | |
@nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) | |
# call the function and store the result | |
@inbounds C[ind] = @ncall $nargs f val | |
ind += 1 | |
if ind > bitcache_size | |
dumpbitcache(Bc, cind, C) | |
cind += bitcache_chunks | |
ind = 1 | |
end | |
end | |
if ind > 1 | |
@inbounds C[ind:bitcache_size] = false | |
dumpbitcache(Bc, cind, C) | |
end | |
end | |
end | |
""" | |
broadcast!(f, dest, As...) | |
Like [`broadcast`](:func:`broadcast`), but store the result of | |
`broadcast(f, As...)` in the `dest` array. | |
Note that `dest` is only used to store the result, and does not supply | |
arguments to `f` unless it is also listed in the `As`, | |
as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`. | |
""" | |
@inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs}) | |
shape = indices(B) | |
check_broadcast_indices(shape, As...) | |
keeps, Idefaults = map_newindexer(shape, As) | |
_broadcast!(f, B, keeps, Idefaults, As, Val{nargs}) | |
B | |
end | |
# broadcast with computed element type | |
@generated function _broadcast!{K,ID,AT,nargs}(f, B::AbstractArray, keeps::K, Idefaults::ID, As::AT, ::Type{Val{nargs}}, iter, st, count) | |
quote | |
$(Expr(:meta, :noinline)) | |
# destructure the keeps and As tuples | |
@nexprs $nargs i->(A_i = As[i]) | |
@nexprs $nargs i->(keep_i = keeps[i]) | |
@nexprs $nargs i->(Idefault_i = Idefaults[i]) | |
while !done(iter, st) | |
I, st = next(iter, st) | |
# reverse-broadcast the indices | |
@nexprs $nargs i->(I_i = newindex(I, keep_i, Idefault_i)) | |
# extract array values | |
@nexprs $nargs i->(@inbounds val_i = _broadcast_getindex(A_i, I_i)) | |
# call the function | |
V = @ncall $nargs f val | |
S = typeof(V) | |
# store the result | |
if S <: eltype(B) | |
@inbounds B[I] = V | |
else | |
R = typejoin(eltype(B), S) | |
new = similar(B, R) | |
for II in Iterators.take(iter, count) | |
new[II] = B[II] | |
end | |
new[I] = V | |
return _broadcast!(f, new, keeps, Idefaults, As, Val{nargs}, iter, st, count+1) | |
end | |
count += 1 | |
end | |
return B | |
end | |
end | |
function broadcast_t(f, ::Type{Any}, As...) | |
shape = broadcast_indices(As...) | |
iter = CartesianRange(shape) | |
if isempty(iter) | |
return similar(Array{Any}, shape) | |
end | |
nargs = length(As) | |
keeps, Idefaults = map_newindexer(shape, As) | |
st = start(iter) | |
I, st = next(iter, st) | |
val = f([ _broadcast_getindex(As[i], newindex(I, keeps[i], Idefaults[i])) for i=1:nargs ]...) | |
B = similar(Array{typeof(val)}, shape) | |
B[I] = val | |
return _broadcast!(f, B, keeps, Idefaults, As, Val{nargs}, iter, st, 1) | |
end | |
@inline broadcast_t(f, T, As...) = broadcast!(f, similar(Array{T}, broadcast_indices(As...)), As...) | |
function broadcast_c(f, ::Type{Tuple}, As...) | |
shape = broadcast_indices(As...) | |
check_broadcast_indices(shape, As...) | |
n = length(shape[1]) | |
return ntuple(k->f((_broadcast_getindex(A, k) for A in As)...), n) | |
end | |
@inline broadcast_c(f, ::Type{Any}, a...) = f(a...) | |
@inline broadcast_c(f, ::Type{Array}, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) | |
""" | |
broadcast(f, As...) | |
Broadcasts the arrays, tuples and/or scalars `As` to a container of the | |
appropriate type and dimensions. In this context, anything that is not a | |
subtype of `AbstractArray` or `Tuple` is considered a scalar. The resulting | |
container is established by the following rules: | |
- If all the arguments are scalars, it returns a scalar. | |
- If the arguments are tuples and zero or more scalars, it returns a tuple. | |
- If there is at least an array in the arguments, it returns an array | |
(and treats tuples as 1-dimensional arrays) expanding singleton dimensions. | |
A special syntax exists for broadcasting: `f.(args...)` is equivalent to | |
`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a | |
single broadcast loop. | |
```jldoctest | |
julia> A = [1, 2, 3, 4, 5] | |
5-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
julia> B = [1 2; 3 4; 5 6; 7 8; 9 10] | |
5×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
5 6 | |
7 8 | |
9 10 | |
julia> broadcast(+, A, B) | |
5×2 Array{Int64,2}: | |
2 3 | |
5 6 | |
8 9 | |
11 12 | |
14 15 | |
julia> parse.(Int, ["1", "2"]) | |
2-element Array{Int64,1}: | |
1 | |
2 | |
julia> abs.((1, -2)) | |
(1,2) | |
julia> broadcast(+, 1.0, (0, -2.0)) | |
(1.0,-1.0) | |
julia> broadcast(+, 1.0, (0, -2.0), [1]) | |
2-element Array{Float64,1}: | |
2.0 | |
0.0 | |
julia> string.(("one","two","three","four"), ": ", 1:4) | |
4-element Array{String,1}: | |
"one: 1" | |
"two: 2" | |
"three: 3" | |
"four: 4" | |
``` | |
""" | |
@inline broadcast(f, As...) = broadcast_c(f, containertype(As...), As...) | |
""" | |
bitbroadcast(f, As...) | |
Like [`broadcast`](:func:`broadcast`), but allocates a `BitArray` to store the | |
result, rather then an `Array`. | |
```jldoctest | |
julia> bitbroadcast(isodd,[1,2,3,4,5]) | |
5-element BitArray{1}: | |
true | |
false | |
true | |
false | |
true | |
``` | |
""" | |
@inline bitbroadcast(f, As...) = broadcast!(f, similar(BitArray, broadcast_indices(As...)), As...) | |
""" | |
broadcast_getindex(A, inds...) | |
Broadcasts the `inds` arrays to a common size like [`broadcast`](:func:`broadcast`) | |
and returns an array of the results `A[ks...]`, | |
where `ks` goes over the positions in the broadcast result `A`. | |
```jldoctest | |
julia> A = [1, 2, 3, 4, 5] | |
5-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
julia> B = [1 2; 3 4; 5 6; 7 8; 9 10] | |
5×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
5 6 | |
7 8 | |
9 10 | |
julia> C = broadcast(+,A,B) | |
5×2 Array{Int64,2}: | |
2 3 | |
5 6 | |
8 9 | |
11 12 | |
14 15 | |
julia> broadcast_getindex(C,[1,2,10]) | |
3-element Array{Int64,1}: | |
2 | |
5 | |
15 | |
``` | |
""" | |
broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(similar(Array{eltype(src)}, broadcast_indices(I...)), src, I...) | |
@generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...) | |
N = length(I) | |
Isplat = Expr[:(I[$d]) for d = 1:N] | |
quote | |
@nexprs $N d->(I_d = I[d]) | |
check_broadcast_indices(indices(dest), $(Isplat...)) # unnecessary if this function is never called directly | |
checkbounds(src, $(Isplat...)) | |
@nexprs $N d->(@nexprs $N k->(Ibcast_d_k = indices(I_k, d) == OneTo(1))) | |
@nloops $N i dest d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin | |
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) | |
@inbounds (@nref $N dest i) = (@nref $N src J) | |
end | |
dest | |
end | |
end | |
""" | |
broadcast_setindex!(A, X, inds...) | |
Broadcasts the `X` and `inds` arrays to a common size and stores the value from each | |
position in `X` at the indices in `A` given by the same positions in `inds`. | |
""" | |
@generated function broadcast_setindex!(A::AbstractArray, x, I::AbstractArray...) | |
N = length(I) | |
Isplat = Expr[:(I[$d]) for d = 1:N] | |
quote | |
@nexprs $N d->(I_d = I[d]) | |
checkbounds(A, $(Isplat...)) | |
shape = broadcast_indices($(Isplat...)) | |
@nextract $N shape d->(length(shape) < d ? OneTo(1) : shape[d]) | |
@nexprs $N d->(@nexprs $N k->(Ibcast_d_k = indices(I_k, d) == 1:1)) | |
if !isa(x, AbstractArray) | |
xA = convert(eltype(A), x) | |
@nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin | |
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k) | |
@inbounds (@nref $N A J) = xA | |
end | |
else | |
X = x | |
@nexprs $N d->(shapelen_d = length(shape_d)) | |
@ncall $N Base.setindex_shape_check X shapelen | |
Xstate = start(X) | |
@inbounds @nloops $N i d->shape_d d->(@nexprs $N k->(j_d_k = Ibcast_d_k ? 1 : i_d)) begin | |
@nexprs $N k->(J_k = @nref $N I_k d->j_d_k) | |
x_el, Xstate = next(X, Xstate) | |
(@nref $N A J) = x_el | |
end | |
end | |
A | |
end | |
end | |
## elementwise operators ## | |
for op in (:÷, :%, :<<, :>>, :-, :/, :\, ://, :^) | |
@eval $(Symbol(:., op))(A::AbstractArray, B::AbstractArray) = broadcast($op, A, B) | |
end | |
.+(As::AbstractArray...) = broadcast(+, As...) | |
.*(As::AbstractArray...) = broadcast(*, As...) | |
# ## element-wise comparison operators returning BitArray ## | |
.==(A::AbstractArray, B::AbstractArray) = bitbroadcast(==, A, B) | |
.<(A::AbstractArray, B::AbstractArray) = bitbroadcast(<, A, B) | |
.!=(A::AbstractArray, B::AbstractArray) = bitbroadcast(!=, A, B) | |
.<=(A::AbstractArray, B::AbstractArray) = bitbroadcast(<=, A, B) | |
function broadcast_bitarrays(scalarf, bitf, A::AbstractArray{Bool}, B::AbstractArray{Bool}) | |
local shape | |
try | |
shape = promote_shape(indices(A), indices(B)) | |
catch | |
return bitbroadcast(scalarf, A, B) | |
end | |
F = BitArray(to_shape(shape)) | |
Fc = F.chunks | |
Ac = BitArray(A).chunks | |
Bc = BitArray(B).chunks | |
if !isempty(Ac) && !isempty(Bc) | |
for i = 1:length(Fc) - 1 | |
Fc[i] = (bitf)(Ac[i], Bc[i]) | |
end | |
Fc[end] = (bitf)(Ac[end], Bc[end]) & _msk_end(F) | |
end | |
return F | |
end | |
biteq(a::UInt64, b::UInt64) = ~a $ b | |
bitlt(a::UInt64, b::UInt64) = ~a & b | |
bitneq(a::UInt64, b::UInt64) = a $ b | |
bitle(a::UInt64, b::UInt64) = ~a | b | |
.==(A::AbstractArray{Bool}, B::AbstractArray{Bool}) = broadcast_bitarrays(==, biteq, A, B) | |
.<(A::AbstractArray{Bool}, B::AbstractArray{Bool}) = broadcast_bitarrays(<, bitlt, A, B) | |
.!=(A::AbstractArray{Bool}, B::AbstractArray{Bool}) = broadcast_bitarrays(!=, bitneq, A, B) | |
.<=(A::AbstractArray{Bool}, B::AbstractArray{Bool}) = broadcast_bitarrays(<=, bitle, A, B) | |
function bitcache(op, A, B, refA, refB, l::Int, ind::Int, C::Vector{Bool}) | |
left = l - ind + 1 | |
@inbounds begin | |
for j = 1:min(bitcache_size, left) | |
C[j] = (op)(refA(A, ind), refB(B, ind)) | |
ind += 1 | |
end | |
C[left+1:bitcache_size] = false | |
end | |
return ind | |
end | |
# note: the following are not broadcasting, but need to be defined here to avoid | |
# ambiguity warnings | |
for (f, scalarf) in ((:.==, :(==)), | |
(:.< , :< ), | |
(:.!=, :!= ), | |
(:.<=, :<= )) | |
for (sigA, sigB, active, refA, refB) in ((:Any, :AbstractArray, :B, | |
:((A,ind)->A), :((B,ind)->B[ind])), | |
(:AbstractArray, :Any, :A, | |
:((A,ind)->A[ind]), :((B,ind)->B))) | |
shape = :(indices($active)) | |
@eval begin | |
function ($f)(A::$sigA, B::$sigB) | |
P = similar(BitArray, $shape) | |
F = parent(P) | |
l = length(F) | |
l == 0 && return F | |
Fc = F.chunks | |
C = Array{Bool}(bitcache_size) | |
ind = first(linearindices($active)) | |
cind = 1 | |
for i = 1:div(l + bitcache_size - 1, bitcache_size) | |
ind = bitcache($scalarf, A, B, $refA, $refB, l, ind, C) | |
dumpbitcache(Fc, cind, C) | |
cind += bitcache_chunks | |
end | |
return P | |
end | |
end | |
end | |
end | |
## specialized element-wise operators for BitArray | |
(.^)(A::BitArray, B::AbstractArray{Bool}) = (B .<= A) | |
(.^)(A::AbstractArray{Bool}, B::AbstractArray{Bool}) = (B .<= A) | |
function bitcache_pow{T}(Ac::Vector{UInt64}, B::Array{T}, l::Int, ind::Int, C::Vector{Bool}) | |
left = l - ind + 1 | |
@inbounds begin | |
for j = 1:min(bitcache_size, left) | |
C[j] = unsafe_bitgetindex(Ac, ind) ^ B[ind] | |
ind += 1 | |
end | |
C[left+1:bitcache_size] = false | |
end | |
return ind | |
end | |
function (.^){T<:Integer}(A::BitArray, B::Array{T}) | |
local shape | |
try | |
shape = promote_shape(indices(A), indices(B)) | |
catch | |
return bitbroadcast(^, A, B) | |
end | |
F = BitArray(to_shape(shape)) | |
l = length(F) | |
l == 0 && return F | |
Ac = A.chunks | |
Fc = F.chunks | |
C = Array{Bool}(bitcache_size) | |
ind = 1 | |
cind = 1 | |
for i = 1:div(l + bitcache_size - 1, bitcache_size) | |
ind = bitcache_pow(Ac, B, l, ind, C) | |
dumpbitcache(Fc, cind, C) | |
cind += bitcache_chunks | |
end | |
return F | |
end | |
for (sigA, sigB) in ((BitArray, BitArray), | |
(AbstractArray{Bool}, BitArray), | |
(BitArray, AbstractArray{Bool})) | |
@eval function (.*)(A::$sigA, B::$sigB) | |
try | |
return BitArray(A) & BitArray(B) | |
catch | |
return bitbroadcast(&, A, B) | |
end | |
end | |
end | |
############################################################ | |
# x[...] .= f.(y...) ---> broadcast!(f, dotview(x, ...), y...). | |
# The dotview function defaults to getindex, but we override it in | |
# a few cases to get the expected in-place behavior without affecting | |
# explicit calls to view. (All of this can go away if slices | |
# are changed to generate views by default.) | |
dotview(args...) = getindex(args...) | |
dotview(A::AbstractArray, args...) = view(A, args...) | |
dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) = getindex(A, args...) | |
# avoid splatting penalty in common cases: | |
for nargs = 0:5 | |
args = Symbol[Symbol("x",i) for i = 1:nargs] | |
eval(Expr(:(=), Expr(:call, :dotview, args...), | |
Expr(:call, :getindex, args...))) | |
eval(Expr(:(=), Expr(:call, :dotview, :(A::AbstractArray), args...), | |
Expr(:call, :view, :A, args...))) | |
end | |
end # module | |
# This file is automatically generated in base/Makefile | |
const MACHINE = "x86_64-apple-darwin16.0.0" | |
const libm_name = "libopenlibm" | |
const libblas_name = "libopenblas64_" | |
const liblapack_name = "libopenblas64_" | |
const USE_BLAS64 = true | |
const USE_GPL_LIBS = true | |
const libfftw_name = "libfftw3_threads" | |
const libfftwf_name = "libfftw3f_threads" | |
const libllvm_version = "3.7.1" | |
const VERSION_STRING = "0.6.0-dev" | |
const TAGGED_RELEASE_BANNER = "" | |
const SYSCONFDIR = "../etc" | |
const DATAROOTDIR = "../share" | |
const DOCDIR = "../share/doc/julia" | |
const LIBDIR = "../lib" | |
const INCLUDEDIR = "../include" | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# definitions related to C interface | |
import Core.Intrinsics: cglobal, box | |
cfunction(f::Function, r, a) = ccall(:jl_function_ptr, Ptr{Void}, (Any, Any, Any), f, r, a) | |
if ccall(:jl_is_char_signed, Ref{Bool}, ()) | |
typealias Cchar Int8 | |
else | |
typealias Cchar UInt8 | |
end | |
""" | |
Cchar | |
Equivalent to the native `char` c-type. | |
""" | |
Cchar | |
if is_windows() | |
typealias Clong Int32 | |
typealias Culong UInt32 | |
typealias Cwchar_t UInt16 | |
else | |
typealias Clong Int | |
typealias Culong UInt | |
typealias Cwchar_t Int32 | |
end | |
""" | |
Clong | |
Equivalent to the native `signed long` c-type. | |
""" | |
Clong | |
""" | |
Culong | |
Equivalent to the native `unsigned long` c-type. | |
""" | |
Culong | |
""" | |
Cwchar_t | |
Equivalent to the native `wchar_t` c-type (`Int32`). | |
""" | |
Cwchar_t | |
if !is_windows() | |
const sizeof_mode_t = ccall(:jl_sizeof_mode_t, Cint, ()) | |
if sizeof_mode_t == 2 | |
typealias Cmode_t Int16 | |
elseif sizeof_mode_t == 4 | |
typealias Cmode_t Int32 | |
elseif sizeof_mode_t == 8 | |
typealias Cmode_t Int64 | |
end | |
end | |
# construction from typed pointers | |
convert{T<:Union{Int8,UInt8}}(::Type{Cstring}, p::Ptr{T}) = box(Cstring, p) | |
convert(::Type{Cwstring}, p::Ptr{Cwchar_t}) = box(Cwstring, p) | |
convert{T<:Union{Int8,UInt8}}(::Type{Ptr{T}}, p::Cstring) = box(Ptr{T}, p) | |
convert(::Type{Ptr{Cwchar_t}}, p::Cwstring) = box(Ptr{Cwchar_t}, p) | |
# construction from untyped pointers | |
convert{T<:Union{Cstring,Cwstring}}(::Type{T}, p::Ptr{Void}) = box(T, p) | |
pointer(p::Cstring) = convert(Ptr{UInt8}, p) | |
pointer(p::Cwstring) = convert(Ptr{Cwchar_t}, p) | |
# comparisons against pointers (mainly to support `cstr==C_NULL`) | |
==(x::Union{Cstring,Cwstring}, y::Ptr) = pointer(x) == y | |
==(x::Ptr, y::Union{Cstring,Cwstring}) = x == pointer(y) | |
# here, not in pointer.jl, to avoid bootstrapping problems in coreimg.jl | |
unsafe_wrap(::Type{String}, p::Cstring, own::Bool=false) = unsafe_wrap(String, convert(Ptr{UInt8}, p), own) | |
unsafe_wrap(::Type{String}, p::Cstring, len::Integer, own::Bool=false) = | |
unsafe_wrap(String, convert(Ptr{UInt8}, p), len, own) | |
unsafe_string(s::Cstring) = unsafe_string(convert(Ptr{UInt8}, s)) | |
# convert strings to String etc. to pass as pointers | |
cconvert(::Type{Cstring}, s::String) = | |
ccall(:jl_array_cconvert_cstring, Ref{Vector{UInt8}}, | |
(Vector{UInt8},), s.data) | |
cconvert(::Type{Cstring}, s::AbstractString) = | |
cconvert(Cstring, String(s)::String) | |
function cconvert(::Type{Cwstring}, s::AbstractString) | |
v = transcode(Cwchar_t, String(s).data) | |
!isempty(v) && v[end] == 0 || push!(v, 0) | |
return v | |
end | |
eltype(::Type{Cstring}) = UInt8 | |
eltype(::Type{Cwstring}) = Cwchar_t | |
containsnul(p::Ptr, len) = | |
C_NULL != ccall(:memchr, Ptr{Cchar}, (Ptr{Cchar}, Cint, Csize_t), p, 0, len) | |
containsnul(s::String) = containsnul(unsafe_convert(Ptr{Cchar}, s), sizeof(s)) | |
containsnul(s::AbstractString) = '\0' in s | |
function unsafe_convert(::Type{Cstring}, s::Vector{UInt8}) | |
p = unsafe_convert(Ptr{Cchar}, s) | |
containsnul(p, sizeof(s)) && | |
throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) | |
return Cstring(p) | |
end | |
function unsafe_convert(::Type{Cwstring}, v::Vector{Cwchar_t}) | |
for i = 1:length(v)-1 | |
v[i] == 0 && | |
throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(v))")) | |
end | |
v[end] == 0 || | |
throw(ArgumentError("C string data must be NUL terminated: $(repr(v))")) | |
p = unsafe_convert(Ptr{Cwchar_t}, v) | |
return Cwstring(p) | |
end | |
# symbols are guaranteed not to contain embedded NUL | |
convert(::Type{Cstring}, s::Symbol) = Cstring(unsafe_convert(Ptr{Cchar}, s)) | |
if is_windows() | |
""" | |
Base.cwstring(s) | |
Converts a string `s` to a NUL-terminated `Vector{Cwchar_t}`, suitable for passing to C | |
functions expecting a `Ptr{Cwchar_t}`. The main advantage of using this over the implicit | |
conversion provided by `Cwstring` is if the function is called multiple times with the | |
same argument. | |
This is only available on Windows. | |
""" | |
function cwstring(s::AbstractString) | |
bytes = String(s).data | |
0 in bytes && throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) | |
return push!(transcode(UInt16, bytes), 0) | |
end | |
end | |
# transcoding between data in UTF-8 and UTF-16 for Windows APIs, | |
# and also UTF-32 for APIs using Cwchar_t on other platforms. | |
""" | |
transcode(T, src) | |
Convert string data between Unicode encodings. `src` is either a | |
`String` or a `Vector{UIntXX}` of UTF-XX code units, where | |
`XX` is 8, 16, or 32. `T` indicates the encoding of the return value: | |
`String` to return a (UTF-8 encoded) `String` or `UIntXX` | |
to return a `Vector{UIntXX}` of UTF-`XX` data. (The alias `Cwchar_t` | |
can also be used as the integer type, for converting `wchar_t*` strings | |
used by external C libraries.) | |
The `transcode` function succeeds as long as the input data can be | |
reasonably represented in the target encoding; it always succeeds for | |
conversions between UTF-XX encodings, even for invalid Unicode data. | |
Only conversion to/from UTF-8 is currently supported. | |
""" | |
function transcode end | |
transcode{T<:Union{UInt8,UInt16,UInt32,Int32}}(::Type{T}, src::Vector{T}) = src | |
transcode{T<:Union{Int32,UInt32}}(::Type{T}, src::String) = T[T(c) for c in src] | |
transcode{T<:Union{Int32,UInt32}}(::Type{T}, src::Vector{UInt8}) = transcode(T, String(src)) | |
function transcode{S<:Union{Int32,UInt32}}(::Type{UInt8}, src::Vector{S}) | |
buf = IOBuffer() | |
for c in src; print(buf, Char(c)); end | |
takebuf_array(buf) | |
end | |
transcode(::Type{String}, src::String) = src | |
transcode(T, src::String) = transcode(T, src.data) | |
transcode(::Type{String}, src) = String(transcode(UInt8, src)) | |
function transcode(::Type{UInt16}, src::Vector{UInt8}) | |
dst = UInt16[] | |
i, n = 1, length(src) | |
n > 0 || return dst | |
sizehint!(dst, 2n) | |
a = src[1] | |
while true | |
if i < n && -64 <= a % Int8 <= -12 # multi-byte character | |
b = src[i += 1] | |
if -64 <= (b % Int8) || a == 0xf4 && 0x8f < b | |
# invalid UTF-8 (non-continuation or too-high code point) | |
push!(dst, a) | |
a = b; continue | |
elseif a < 0xe0 # 2-byte UTF-8 | |
push!(dst, 0x3080 $ (UInt16(a) << 6) $ b) | |
elseif i < n # 3/4-byte character | |
c = src[i += 1] | |
if -64 <= (c % Int8) # invalid UTF-8 (non-continuation) | |
push!(dst, a, b) | |
a = c; continue | |
elseif a < 0xf0 # 3-byte UTF-8 | |
push!(dst, 0x2080 $ (UInt16(a) << 12) $ (UInt16(b) << 6) $ c) | |
elseif i < n | |
d = src[i += 1] | |
if -64 <= (d % Int8) # invalid UTF-8 (non-continuation) | |
push!(dst, a, b, c) | |
a = d; continue | |
elseif a == 0xf0 && b < 0x90 # overlong encoding | |
push!(dst, 0x2080 $ (UInt16(b) << 12) $ (UInt16(c) << 6) $ d) | |
else # 4-byte UTF-8 | |
push!(dst, 0xe5b8 + (UInt16(a) << 8) + (UInt16(b) << 2) + (c >> 4), | |
0xdc80 $ (UInt16(c & 0xf) << 6) $ d) | |
end | |
else # too short | |
push!(dst, a, b, c) | |
break | |
end | |
else # too short | |
push!(dst, a, b) | |
break | |
end | |
else # ASCII or invalid UTF-8 (continuation byte or too-high code point) | |
push!(dst, a) | |
end | |
i < n || break | |
a = src[i += 1] | |
end | |
return dst | |
end | |
function transcode(::Type{UInt8}, src::Vector{UInt16}) | |
n = length(src) | |
n == 0 && return UInt8[] | |
# Precompute m = sizeof(dst). This involves annoying duplication | |
# of the loop over the src array. However, this is not just an | |
# optimization: it is problematic for security reasons to grow | |
# dst dynamically, because Base.winprompt uses this function to | |
# convert passwords to UTF-8 and we don't want to make unintentional | |
# copies of the password data. | |
a = src[1] | |
i, m = 1, 0 | |
while true | |
if a < 0x80 | |
m += 1 | |
elseif a < 0x800 # 2-byte UTF-8 | |
m += 2 | |
elseif a & 0xfc00 == 0xd800 && i < length(src) | |
b = src[i += 1] | |
if (b & 0xfc00) == 0xdc00 # 2-unit UTF-16 sequence => 4-byte UTF-8 | |
m += 4 | |
else | |
m += 3 | |
a = b; continue | |
end | |
else | |
# 1-unit high UTF-16 or unpaired high surrogate | |
# either way, encode as 3-byte UTF-8 code point | |
m += 3 | |
end | |
i < n || break | |
a = src[i += 1] | |
end | |
dst = Array{UInt8}(m) | |
a = src[1] | |
i, j = 1, 0 | |
while true | |
if a < 0x80 # ASCII | |
dst[j += 1] = a % UInt8 | |
elseif a < 0x800 # 2-byte UTF-8 | |
dst[j += 1] = 0xc0 | ((a >> 6) % UInt8) | |
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) | |
elseif a & 0xfc00 == 0xd800 && i < n | |
b = src[i += 1] | |
if (b & 0xfc00) == 0xdc00 | |
# 2-unit UTF-16 sequence => 4-byte UTF-8 | |
a += 0x2840 | |
dst[j += 1] = 0xf0 | ((a >> 8) % UInt8) | |
dst[j += 1] = 0x80 | ((a % UInt8) >> 2) | |
dst[j += 1] = 0xf0 $ ((((a % UInt8) << 4) & 0x3f) $ (b >> 6) % UInt8) | |
dst[j += 1] = 0x80 | ((b % UInt8) & 0x3f) | |
else | |
dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) | |
dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) | |
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) | |
a = b; continue | |
end | |
else | |
# 1-unit high UTF-16 or unpaired high surrogate | |
# either way, encode as 3-byte UTF-8 code point | |
dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) | |
dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) | |
dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) | |
end | |
i < n || break | |
a = src[i += 1] | |
end | |
return dst | |
end | |
# deferring (or un-deferring) ctrl-c handler for external C code that | |
# is not interrupt safe (see also issue #2622). The sigatomic_begin/end | |
# functions should always be called in matched pairs, ideally via: | |
# disable_sigint() do .. end | |
# reennable_sigint is provided so that immediate ctrl-c handling is | |
# re-enabled within a sigatomic region, e.g. inside a Julia callback function | |
# within a long-running C routine. | |
sigatomic_begin() = ccall(:jl_sigatomic_begin, Void, ()) | |
sigatomic_end() = ccall(:jl_sigatomic_end, Void, ()) | |
""" | |
disable_sigint(f::Function) | |
Disable Ctrl-C handler during execution of a function on the current task, | |
for calling external code that may call julia code that is not interrupt safe. | |
Intended to be called using `do` block syntax as follows: | |
disable_sigint() do | |
# interrupt-unsafe code | |
... | |
end | |
This is not needed on worker threads (`Threads.threadid() != 1`) since the | |
`InterruptException` will only be delivered to the master thread. | |
External functions that do not call julia code or julia runtime | |
automatically disable sigint during their execution. | |
""" | |
function disable_sigint(f::Function) | |
sigatomic_begin() | |
res = f() | |
# Exception unwind sigatomic automatically | |
sigatomic_end() | |
res | |
end | |
""" | |
reenable_sigint(f::Function) | |
Re-enable Ctrl-C handler during execution of a function. | |
Temporarily reverses the effect of `disable_sigint`. | |
""" | |
function reenable_sigint(f::Function) | |
sigatomic_end() | |
res = f() | |
# Exception unwind sigatomic automatically | |
sigatomic_begin() | |
res | |
end | |
function ccallable(f::Function, rt::Type, argt::Type, name::Union{AbstractString,Symbol}=string(f)) | |
ccall(:jl_extern_c, Void, (Any, Any, Any, Cstring), f, rt, argt, name) | |
end | |
macro ccallable(rt, def) | |
if isa(def,Expr) && (def.head === :(=) || def.head === :function) | |
sig = def.args[1] | |
if sig.head === :call | |
name = sig.args[1] | |
at = map(sig.args[2:end]) do a | |
if isa(a,Expr) && a.head === :(::) | |
a.args[2] | |
else | |
:Any | |
end | |
end | |
return quote | |
$(esc(def)) | |
ccallable($(esc(name)), $(esc(rt)), $(Expr(:curly, :Tuple, map(esc, at)...)), $(string(name))) | |
end | |
end | |
end | |
error("expected method definition in @ccallable") | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Cartesian | |
export @nloops, @nref, @ncall, @nexprs, @nextract, @nall, @nany, @ntuple, @nif | |
### Cartesian-specific macros | |
""" | |
@nloops N itersym rangeexpr bodyexpr | |
@nloops N itersym rangeexpr preexpr bodyexpr | |
@nloops N itersym rangeexpr preexpr postexpr bodyexpr | |
Generate `N` nested loops, using `itersym` as the prefix for the iteration variables. | |
`rangeexpr` may be an anonymous-function expression, or a simple symbol `var` in which case | |
the range is `1:size(var,d)` for dimension `d`. | |
Optionally, you can provide "pre" and "post" expressions. These get executed first and last, | |
respectively, in the body of each loop. For example: | |
@nloops 2 i A d->j_d=min(i_d,5) begin | |
s += @nref 2 A j | |
end | |
would generate: | |
for i_2 = 1:size(A, 2) | |
j_2 = min(i_2, 5) | |
for i_1 = 1:size(A, 1) | |
j_1 = min(i_1, 5) | |
s += A[j_1,j_2] | |
end | |
end | |
If you want just a post-expression, supply `nothing` for the pre-expression. Using | |
parentheses and semicolons, you can supply multi-statement expressions. | |
""" | |
macro nloops(N, itersym, rangeexpr, args...) | |
_nloops(N, itersym, rangeexpr, args...) | |
end | |
function _nloops(N::Int, itersym::Symbol, arraysym::Symbol, args::Expr...) | |
@gensym d | |
_nloops(N, itersym, :($d->indices($arraysym, $d)), args...) | |
end | |
function _nloops(N::Int, itersym::Symbol, rangeexpr::Expr, args::Expr...) | |
if rangeexpr.head != :-> | |
throw(ArgumentError("second argument must be an anonymous function expression to compute the range")) | |
end | |
if !(1 <= length(args) <= 3) | |
throw(ArgumentError("number of arguments must be 1 ≤ length(args) ≤ 3, got $nargs")) | |
end | |
body = args[end] | |
ex = Expr(:escape, body) | |
for dim = 1:N | |
itervar = inlineanonymous(itersym, dim) | |
rng = inlineanonymous(rangeexpr, dim) | |
preexpr = length(args) > 1 ? inlineanonymous(args[1], dim) : (:(nothing)) | |
postexpr = length(args) > 2 ? inlineanonymous(args[2], dim) : (:(nothing)) | |
ex = quote | |
for $(esc(itervar)) = $(esc(rng)) | |
$(esc(preexpr)) | |
$ex | |
$(esc(postexpr)) | |
end | |
end | |
end | |
ex | |
end | |
""" | |
@nref N A indexexpr | |
Generate expressions like `A[i_1,i_2,...]`. `indexexpr` can either be an iteration-symbol | |
prefix, or an anonymous-function expression. | |
""" | |
macro nref(N, A, sym) | |
_nref(N, A, sym) | |
end | |
function _nref(N::Int, A::Symbol, ex) | |
vars = [ inlineanonymous(ex,i) for i = 1:N ] | |
Expr(:escape, Expr(:ref, A, vars...)) | |
end | |
""" | |
@ncall N f sym... | |
Generate a function call expression. `sym` represents any number of function arguments, the | |
last of which may be an anonymous-function expression and is expanded into `N` arguments. | |
For example `@ncall 3 func a` generates | |
func(a_1, a_2, a_3) | |
while `@ncall 2 func a b i->c[i]` yields | |
func(a, b, c[1], c[2]) | |
""" | |
macro ncall(N, f, sym...) | |
_ncall(N, f, sym...) | |
end | |
function _ncall(N::Int, f, args...) | |
pre = args[1:end-1] | |
ex = args[end] | |
vars = [ inlineanonymous(ex,i) for i = 1:N ] | |
Expr(:escape, Expr(:call, f, pre..., vars...)) | |
end | |
""" | |
@nexprs N expr | |
Generate `N` expressions. `expr` should be an anonymous-function expression. | |
""" | |
macro nexprs(N, ex) | |
_nexprs(N, ex) | |
end | |
function _nexprs(N::Int, ex::Expr) | |
exs = [ inlineanonymous(ex,i) for i = 1:N ] | |
Expr(:escape, Expr(:block, exs...)) | |
end | |
""" | |
@nextract N esym isym | |
Generate `N` variables `esym_1`, `esym_2`, ..., `esym_N` to extract values from `isym`. | |
`isym` can be either a `Symbol` or anonymous-function expression. | |
`@nextract 2 x y` would generate | |
x_1 = y[1] | |
x_2 = y[2] | |
while `@nextract 3 x d->y[2d-1]` yields | |
x_1 = y[1] | |
x_2 = y[3] | |
x_3 = y[5] | |
""" | |
macro nextract(N, esym, isym) | |
_nextract(N, esym, isym) | |
end | |
function _nextract(N::Int, esym::Symbol, isym::Symbol) | |
aexprs = [Expr(:escape, Expr(:(=), inlineanonymous(esym, i), :(($isym)[$i]))) for i = 1:N] | |
Expr(:block, aexprs...) | |
end | |
function _nextract(N::Int, esym::Symbol, ex::Expr) | |
aexprs = [Expr(:escape, Expr(:(=), inlineanonymous(esym, i), inlineanonymous(ex,i))) for i = 1:N] | |
Expr(:block, aexprs...) | |
end | |
""" | |
@nall N expr | |
Check whether all of the expressions generated by the anonymous-function expression `expr` | |
evaluate to `true`. | |
`@nall 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 && i_2 > 1 && i_3 > 1)`. This | |
can be convenient for bounds-checking. | |
""" | |
macro nall(N, criterion) | |
_nall(N, criterion) | |
end | |
function _nall(N::Int, criterion::Expr) | |
if criterion.head != :-> | |
throw(ArgumentError("second argument must be an anonymous function expression yielding the criterion")) | |
end | |
conds = [Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N] | |
Expr(:&&, conds...) | |
end | |
""" | |
@nany N expr | |
Check whether any of the expressions generated by the anonymous-function expression `expr` | |
evaluate to `true`. | |
`@nany 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 || i_2 > 1 || i_3 > 1)`. | |
""" | |
macro nany(N, criterion) | |
_nany(N, criterion) | |
end | |
function _nany(N::Int, criterion::Expr) | |
if criterion.head != :-> | |
error("Second argument must be an anonymous function expression yielding the criterion") | |
end | |
conds = [Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N] | |
Expr(:||, conds...) | |
end | |
""" | |
@ntuple N expr | |
Generates an `N`-tuple. `@ntuple 2 i` would generate `(i_1, i_2)`, and `@ntuple 2 k->k+1` | |
would generate `(2,3)`. | |
""" | |
macro ntuple(N, ex) | |
_ntuple(N, ex) | |
end | |
function _ntuple(N::Int, ex) | |
vars = [ inlineanonymous(ex,i) for i = 1:N ] | |
Expr(:escape, Expr(:tuple, vars...)) | |
end | |
""" | |
@nif N conditionexpr expr | |
@nif N conditionexpr expr elseexpr | |
Generates a sequence of `if ... elseif ... else ... end` statements. For example: | |
@nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK") | |
would generate: | |
if i_1 > size(A, 1) | |
error("Dimension ", 1, " too big") | |
elseif i_2 > size(A, 2) | |
error("Dimension ", 2, " too big") | |
else | |
println("All OK") | |
end | |
""" | |
macro nif(N, condition, operation...) | |
# Handle the final "else" | |
ex = esc(inlineanonymous(length(operation) > 1 ? operation[2] : operation[1], N)) | |
# Make the nested if statements | |
for i = N-1:-1:1 | |
ex = Expr(:if, esc(inlineanonymous(condition,i)), esc(inlineanonymous(operation[1],i)), ex) | |
end | |
ex | |
end | |
## Utilities | |
# Simplify expressions like :(d->3:size(A,d)-3) given an explicit value for d | |
function inlineanonymous(ex::Expr, val) | |
if ex.head != :-> | |
throw(ArgumentError("not an anonymous function")) | |
end | |
if !isa(ex.args[1], Symbol) | |
throw(ArgumentError("not a single-argument anonymous function")) | |
end | |
sym = ex.args[1] | |
ex = ex.args[2] | |
exout = lreplace(ex, sym, val) | |
exout = poplinenum(exout) | |
exprresolve(exout) | |
end | |
# Given :i and 3, this generates :i_3 | |
inlineanonymous(base::Symbol, ext) = Symbol(base,'_',ext) | |
# Replace a symbol by a value or a "coded" symbol | |
# E.g., for d = 3, | |
# lreplace(:d, :d, 3) -> 3 | |
# lreplace(:i_d, :d, 3) -> :i_3 | |
# lreplace(:i_{d-1}, :d, 3) -> :i_2 | |
# This follows LaTeX notation. | |
immutable LReplace{S<:AbstractString} | |
pat_sym::Symbol | |
pat_str::S | |
val::Int | |
end | |
LReplace(sym::Symbol, val::Integer) = LReplace(sym, string(sym), val) | |
lreplace(ex, sym::Symbol, val) = lreplace!(copy(ex), LReplace(sym, val)) | |
function lreplace!(sym::Symbol, r::LReplace) | |
sym == r.pat_sym && return r.val | |
Symbol(lreplace!(string(sym), r)) | |
end | |
function lreplace!(str::AbstractString, r::LReplace) | |
i = start(str) | |
pat = r.pat_str | |
j = start(pat) | |
matching = false | |
while !done(str, i) | |
cstr, i = next(str, i) | |
if !matching | |
if cstr != '_' || done(str, i) | |
continue | |
end | |
istart = i | |
cstr, i = next(str, i) | |
end | |
if !done(pat, j) | |
cr, j = next(pat, j) | |
if cstr == cr | |
matching = true | |
else | |
matching = false | |
j = start(pat) | |
i = istart | |
continue | |
end | |
end | |
if matching && done(pat, j) | |
if done(str, i) || next(str, i)[1] == '_' | |
# We have a match | |
return string(str[1:prevind(str, istart)], r.val, lreplace!(str[i:end], r)) | |
end | |
matching = false | |
j = start(pat) | |
i = istart | |
end | |
end | |
str | |
end | |
function lreplace!(ex::Expr, r::LReplace) | |
# Curly-brace notation, which acts like parentheses | |
if ex.head == :curly && length(ex.args) == 2 && isa(ex.args[1], Symbol) && endswith(string(ex.args[1]), "_") | |
excurly = exprresolve(lreplace!(ex.args[2], r)) | |
if isa(excurly, Number) | |
return Symbol(ex.args[1],excurly) | |
else | |
ex.args[2] = excurly | |
return ex | |
end | |
end | |
for i in 1:length(ex.args) | |
ex.args[i] = lreplace!(ex.args[i], r) | |
end | |
ex | |
end | |
lreplace!(arg, r::LReplace) = arg | |
poplinenum(arg) = arg | |
function poplinenum(ex::Expr) | |
if ex.head == :block | |
if length(ex.args) == 1 | |
return ex.args[1] | |
elseif length(ex.args) == 2 && isa(ex.args[1], LineNumberNode) | |
return ex.args[2] | |
elseif (length(ex.args) == 2 && isa(ex.args[1], Expr) && ex.args[1].head == :line) | |
return ex.args[2] | |
end | |
end | |
ex | |
end | |
## Resolve expressions at parsing time ## | |
const exprresolve_arith_dict = Dict{Symbol,Function}(:+ => +, | |
:- => -, :* => *, :/ => /, :^ => ^, :div => div) | |
const exprresolve_cond_dict = Dict{Symbol,Function}(:(==) => ==, | |
:(<) => <, :(>) => >, :(<=) => <=, :(>=) => >=) | |
function exprresolve_arith(ex::Expr) | |
if ex.head == :call && haskey(exprresolve_arith_dict, ex.args[1]) && all([isa(ex.args[i], Number) for i = 2:length(ex.args)]) | |
return true, exprresolve_arith_dict[ex.args[1]](ex.args[2:end]...) | |
end | |
false, 0 | |
end | |
exprresolve_arith(arg) = false, 0 | |
exprresolve_conditional(b::Bool) = true, b | |
function exprresolve_conditional(ex::Expr) | |
if ex.head == :call && ex.args[1] ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) | |
return true, exprresolve_cond_dict[ex.args[1]](ex.args[2], ex.args[3]) | |
end | |
false, false | |
end | |
exprresolve_conditional(arg) = false, false | |
exprresolve(arg) = arg | |
function exprresolve(ex::Expr) | |
for i = 1:length(ex.args) | |
ex.args[i] = exprresolve(ex.args[i]) | |
end | |
# Handle simple arithmetic | |
can_eval, result = exprresolve_arith(ex) | |
if can_eval | |
return result | |
elseif ex.head == :call && (ex.args[1] == :+ || ex.args[1] == :-) && length(ex.args) == 3 && ex.args[3] == 0 | |
# simplify x+0 and x-0 | |
return ex.args[2] | |
end | |
# Resolve array references | |
if ex.head == :ref && isa(ex.args[1], Array) | |
for i = 2:length(ex.args) | |
if !isa(ex.args[i], Real) | |
return ex | |
end | |
end | |
return ex.args[1][ex.args[2:end]...] | |
end | |
# Resolve conditionals | |
if ex.head == :if | |
can_eval, tf = exprresolve_conditional(ex.args[1]) | |
if can_eval | |
ex = tf?ex.args[2]:ex.args[3] | |
end | |
end | |
ex | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
abstract AbstractChannel | |
type Channel{T} <: AbstractChannel | |
cond_take::Condition # waiting for data to become available | |
cond_put::Condition # waiting for a writeable slot | |
state::Symbol | |
data::Array{T,1} | |
sz_max::Int # maximum size of channel | |
# Used when sz_max == 0, i.e., an unbuffered channel. | |
takers::Array{Condition} | |
function Channel(sz::Float64) | |
if sz == Inf | |
Channel{T}(typemax(Int)) | |
else | |
Channel{T}(convert(Int, sz)) | |
end | |
end | |
function Channel(sz::Integer) | |
if sz < 0 | |
throw(ArgumentError("Channel size must be either 0, a positive integer or Inf")) | |
end | |
new(Condition(), Condition(), :open, Array{T}(0), sz, Array{Condition}(0)) | |
end | |
# deprecated empty constructor | |
function Channel() | |
depwarn(string("The empty constructor Channel() is deprecated. ", | |
"The channel size needs to be specified explictly. ", | |
"Defaulting to Channel{$T}(32)."), :Channel) | |
Channel(32) | |
end | |
end | |
Channel(sz) = Channel{Any}(sz) | |
# deprecated empty constructor | |
Channel() = Channel{Any}() | |
closed_exception() = InvalidStateException("Channel is closed.", :closed) | |
isbuffered(c::Channel) = c.sz_max==0 ? false : true | |
""" | |
close(c::Channel) | |
Closes a channel. An exception is thrown by: | |
* `put!` on a closed channel. | |
* `take!` and `fetch` on an empty, closed channel. | |
""" | |
function close(c::Channel) | |
c.state = :closed | |
notify_error(c::Channel, closed_exception()) | |
nothing | |
end | |
isopen(c::Channel) = (c.state == :open) | |
type InvalidStateException <: Exception | |
msg::AbstractString | |
state::Symbol | |
end | |
""" | |
put!(c::Channel, v) | |
Appends an item `v` to the channel `c`. Blocks if the channel is full. | |
For unbuffered channels, blocks until a `take!` is performed by a different | |
task. | |
""" | |
function put!(c::Channel, v) | |
!isopen(c) && throw(closed_exception()) | |
isbuffered(c) ? put_buffered(c,v) : put_unbuffered(c,v) | |
end | |
function put_buffered(c::Channel, v) | |
while length(c.data) == c.sz_max | |
wait(c.cond_put) | |
end | |
push!(c.data, v) | |
notify(c.cond_take, nothing, true, false) # notify all, since some of the waiters may be on a "fetch" call. | |
v | |
end | |
function put_unbuffered(c::Channel, v) | |
while length(c.takers) == 0 | |
notify(c.cond_take, nothing, true, false) # Required to handle wait() on 0-sized channels | |
wait(c.cond_put) | |
end | |
cond_taker = shift!(c.takers) | |
notify(cond_taker, v, false, false) | |
v | |
end | |
push!(c::Channel, v) = put!(c, v) | |
""" | |
fetch(c::Channel) | |
Waits for and gets the first available item from the channel. Does not | |
remove the item. `fetch` is unsupported on an unbuffered (0-size) channel. | |
""" | |
fetch(c::Channel) = isbuffered(c) ? fetch_buffered(c) : fetch_unbuffered(c) | |
function fetch_buffered(c::Channel) | |
wait(c) | |
c.data[1] | |
end | |
fetch_unbuffered(c::Channel) = throw(ErrorException("`fetch` is not supported on an unbuffered Channel.")) | |
""" | |
take!(c::Channel) | |
Removes and returns a value from a `Channel`. Blocks until data is available. | |
For unbuffered channels, blocks until a `put!` is performed by a different | |
task. | |
""" | |
take!(c::Channel) = isbuffered(c) ? take_buffered(c) : take_unbuffered(c) | |
function take_buffered(c::Channel) | |
wait(c) | |
v = shift!(c.data) | |
notify(c.cond_put, nothing, false, false) # notify only one, since only one slot has become available for a put!. | |
v | |
end | |
shift!(c::Channel) = take!(c) | |
# 0-size channel | |
function take_unbuffered(c::Channel) | |
!isopen(c) && throw(closed_exception()) | |
cond_taker = Condition() | |
push!(c.takers, cond_taker) | |
notify(c.cond_put, nothing, false, false) | |
try | |
return wait(cond_taker) | |
catch e | |
if isa(e, InterruptException) | |
# remove self from the list of takers | |
filter!(x -> x != cond_taker, c.takers) | |
else | |
rethrow(e) | |
end | |
end | |
end | |
""" | |
isready(c::Channel) | |
Determine whether a `Channel` has a value stored to it. Returns | |
immediately, does not block. | |
For unbuffered channels returns `true` if there are tasks waiting | |
on a `put!`. | |
""" | |
isready(c::Channel) = n_avail(c) > 0 | |
n_avail(c::Channel) = isbuffered(c) ? length(c.data) : n_waiters(c.cond_put) | |
function wait(c::Channel) | |
while !isready(c) | |
!isopen(c) && throw(closed_exception()) | |
wait(c.cond_take) | |
end | |
nothing | |
end | |
function notify_error(c::Channel, err) | |
notify_error(c.cond_take, err) | |
notify_error(c.cond_put, err) | |
foreach(x->notify_error(x, err), c.takers) | |
end | |
eltype{T}(::Type{Channel{T}}) = T | |
show(io::IO, c::Channel) = print(io, "$(typeof(c))(sz_max:$(c.sz_max),sz_curr:$(n_avail(c)))") | |
type ChannelState{T} | |
hasval::Bool | |
val::T | |
ChannelState(x) = new(x) | |
end | |
start{T}(c::Channel{T}) = ChannelState{T}(false) | |
function done(c::Channel, state::ChannelState) | |
try | |
# we are waiting either for more data or channel to be closed | |
state.hasval && return false | |
state.val = take!(c) | |
state.hasval = true | |
return false | |
catch e | |
if isa(e, InvalidStateException) && e.state==:closed | |
return true | |
else | |
rethrow(e) | |
end | |
end | |
end | |
next{T}(c::Channel{T}, state) = (v=state.val; state.hasval=false; (v, state)) | |
iteratorsize{C<:Channel}(::Type{C}) = SizeUnknown() | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
convert(::Type{Char}, x::UInt32) = reinterpret(Char, x) | |
convert(::Type{Char}, x::Number) = Char(UInt32(x)) | |
convert(::Type{UInt32}, x::Char) = reinterpret(UInt32, x) | |
convert{T<:Number}(::Type{T}, x::Char) = convert(T, UInt32(x)) | |
rem{T<:Number}(x::Char, ::Type{T}) = rem(UInt32(x), T) | |
typemax(::Type{Char}) = reinterpret(Char, typemax(UInt32)) | |
typemin(::Type{Char}) = reinterpret(Char, typemin(UInt32)) | |
size(c::Char) = () | |
size(c::Char,d) = convert(Int, d) < 1 ? throw(BoundsError()) : 1 | |
ndims(c::Char) = 0 | |
ndims(::Type{Char}) = 0 | |
length(c::Char) = 1 | |
endof(c::Char) = 1 | |
getindex(c::Char) = c | |
getindex(c::Char, i::Integer) = i == 1 ? c : throw(BoundsError()) | |
getindex(c::Char, I::Integer...) = all(Predicate(x -> x == 1), I) ? c : throw(BoundsError()) | |
first(c::Char) = c | |
last(c::Char) = c | |
eltype(::Type{Char}) = Char | |
start(c::Char) = false | |
next(c::Char, state) = (c, true) | |
done(c::Char, state) = state | |
isempty(c::Char) = false | |
in(x::Char, y::Char) = x == y | |
==(x::Char, y::Char) = UInt32(x) == UInt32(y) | |
isless(x::Char, y::Char) = UInt32(x) < UInt32(y) | |
const hashchar_seed = 0xd4d64234 | |
hash(x::Char, h::UInt) = hash_uint64(((UInt64(x)+hashchar_seed)<<32) $ UInt64(h)) | |
-(x::Char, y::Char) = Int(x) - Int(y) | |
-(x::Char, y::Integer) = Char(Int32(x) - Int32(y)) | |
+(x::Char, y::Integer) = Char(Int32(x) + Int32(y)) | |
+(x::Integer, y::Char) = y + x | |
bswap(x::Char) = Char(bswap(UInt32(x))) | |
print(io::IO, c::Char) = (write(io, c); nothing) | |
const hex_chars = UInt8['0':'9';'a':'z'] | |
function show(io::IO, c::Char) | |
if c <= '\\' | |
b = c == '\0' ? 0x30 : | |
c == '\a' ? 0x61 : | |
c == '\b' ? 0x62 : | |
c == '\t' ? 0x74 : | |
c == '\n' ? 0x6e : | |
c == '\v' ? 0x76 : | |
c == '\f' ? 0x66 : | |
c == '\r' ? 0x72 : | |
c == '\e' ? 0x65 : | |
c == '\'' ? 0x27 : | |
c == '\\' ? 0x5c : 0xff | |
if b != 0xff | |
write(io, 0x27, 0x5c, b, 0x27) | |
return | |
end | |
end | |
if isprint(c) | |
write(io, 0x27, c, 0x27) | |
else | |
u = UInt32(c) | |
write(io, 0x27, 0x5c, c <= '\x7f' ? 0x78 : c <= '\uffff' ? 0x75 : 0x55) | |
d = max(2, 8 - (leading_zeros(u) >> 2)) | |
while 0 < d | |
write(io, hex_chars[((u >> ((d -= 1) << 2)) & 0xf) + 1]) | |
end | |
write(io, 0x27) | |
end | |
return | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Support for checked integer arithmetic | |
module Checked | |
export checked_neg, checked_abs, checked_add, checked_sub, checked_mul, | |
checked_div, checked_rem, checked_fld, checked_mod, checked_cld, | |
add_with_overflow, sub_with_overflow, mul_with_overflow | |
import Core.Intrinsics: box, unbox, | |
checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int, | |
checked_srem_int, | |
checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, | |
checked_urem_int | |
import Base: no_op_err, @_inline_meta | |
# define promotion behavior for checked operations | |
checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) | |
checked_sub(x::Integer, y::Integer) = checked_sub(promote(x,y)...) | |
checked_mul(x::Integer, y::Integer) = checked_mul(promote(x,y)...) | |
checked_div(x::Integer, y::Integer) = checked_div(promote(x,y)...) | |
checked_rem(x::Integer, y::Integer) = checked_rem(promote(x,y)...) | |
checked_fld(x::Integer, y::Integer) = checked_fld(promote(x,y)...) | |
checked_mod(x::Integer, y::Integer) = checked_mod(promote(x,y)...) | |
checked_cld(x::Integer, y::Integer) = checked_cld(promote(x,y)...) | |
# fallback catchall rules to prevent infinite recursion if promotion succeeds, | |
# but no method exists to handle those types | |
checked_abs{T<:Integer}(x::T) = no_op_err("checked_abs", T) | |
typealias SignedInt Union{Int8,Int16,Int32,Int64,Int128} | |
typealias UnsignedInt Union{UInt8,UInt16,UInt32,UInt64,UInt128} | |
# LLVM has several code generation bugs for checked integer arithmetic (see e.g. | |
# #4905). We thus distinguish between operations that can be implemented via | |
# intrinsics, and operations for which we have to provide work-arounds. | |
# Note: As far as this code has been tested, most checked_* functions are | |
# working fine in LLVM. (Note that division is still handled via `base/int.jl`, | |
# which always checks for overflow, and which provides its own sets of | |
# work-arounds for LLVM codegen bugs.) However, the comments in `base/int.jl` | |
# and in issue #4905 are more pessimistic. For the time being, we thus retain | |
# the ability to handle codegen bugs in LLVM, until the code here has been | |
# tested on more systems and architectures. It also seems that things depend on | |
# which compiler that was used to build LLVM (i.e. either gcc or clang). | |
# These unions are used for most checked functions: | |
# BrokenSignedInt | |
# BrokenUnsignedInt | |
# These unions are used for checked_{mul,div,rem}: | |
# BrokenSignedIntMul | |
# BrokenUnsignedIntMul | |
# This code runs early during bootstrap, and we can't use Julia's version | |
# strings yet | |
const llvm_version = Int(ccall(:jl_get_LLVM_VERSION, UInt32, ())) | |
brokenSignedInt = Union{} | |
brokenUnsignedInt = Union{} | |
brokenSignedIntMul = Int128 | |
brokenUnsignedIntMul = UInt128 | |
if Core.sizeof(Ptr{Void}) == 4 | |
brokenSignedIntMul = Union{brokenSignedIntMul, Int64} | |
brokenUnsignedIntMul = Union{brokenUnsignedIntMul, UInt64} | |
end | |
if llvm_version < 30500 | |
brokenSignedIntMul = Union{brokenSignedIntMul, Int8} | |
brokenUnsignedIntMul = Union{brokenUnsignedIntMul, UInt8} | |
end | |
typealias BrokenSignedInt brokenSignedInt | |
typealias BrokenUnsignedInt brokenUnsignedInt | |
typealias BrokenSignedIntMul brokenSignedIntMul | |
typealias BrokenUnsignedIntMul brokenUnsignedIntMul | |
# Use these definitions to test the non-LLVM implementations | |
# typealias BrokenSignedInt SignedInt | |
# typealias BrokenUnsignedInt UnsignedInt | |
# typealias BrokenSignedIntMul SignedInt | |
# typealias BrokenUnsignedIntMul UnsignedInt | |
""" | |
Base.checked_neg(x) | |
Calculates `-x`, checking for overflow errors where applicable. For | |
example, standard two's complement signed integers (e.g. `Int`) cannot | |
represent `-typemin(Int)`, thus leading to an overflow. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
function checked_neg{T<:Integer}(x::T) | |
checked_sub(T(0), x) | |
end | |
if BrokenSignedInt != Union{} | |
function checked_neg{T<:BrokenSignedInt}(x::T) | |
r = -x | |
(x<0) & (r<0) && throw(OverflowError()) | |
r | |
end | |
end | |
if BrokenUnsignedInt != Union{} | |
function checked_neg{T<:BrokenUnsignedInt}(x::T) | |
x != 0 && throw(OverflowError()) | |
T(0) | |
end | |
end | |
""" | |
Base.checked_abs(x) | |
Calculates `abs(x)`, checking for overflow errors where applicable. | |
For example, standard two's complement signed integers (e.g. `Int`) | |
cannot represent `abs(typemin(Int))`, thus leading to an overflow. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
function checked_abs end | |
function checked_abs(x::SignedInt) | |
r = ifelse(x<0, -x, x) | |
r<0 && throw(OverflowError()) | |
r | |
end | |
checked_abs(x::UnsignedInt) = x | |
checked_abs(x::Bool) = x | |
""" | |
Base.add_with_overflow(x, y) -> (r, f) | |
Calculates `r = x+y`, with the flag `f` indicating whether overflow has occurred. | |
""" | |
function add_with_overflow end | |
add_with_overflow{T<:SignedInt}(x::T, y::T) = checked_sadd_int(x, y) | |
add_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_uadd_int(x, y) | |
add_with_overflow(x::Bool, y::Bool) = x+y, false | |
if BrokenSignedInt != Union{} | |
function add_with_overflow{T<:BrokenSignedInt}(x::T, y::T) | |
r = x + y | |
# x and y have the same sign, and the result has a different sign | |
f = (x<0) == (y<0) != (r<0) | |
r, f | |
end | |
end | |
if BrokenUnsignedInt != Union{} | |
function add_with_overflow{T<:BrokenUnsignedInt}(x::T, y::T) | |
# x + y > typemax(T) | |
# Note: ~y == -y-1 | |
x + y, x > ~y | |
end | |
end | |
""" | |
Base.checked_add(x, y) | |
Calculates `x+y`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
function checked_add{T<:Integer}(x::T, y::T) | |
@_inline_meta | |
z, b = add_with_overflow(x, y) | |
b && throw(OverflowError()) | |
z | |
end | |
# Handle multiple arguments | |
checked_add(x) = x | |
checked_add(x::Bool) = +x | |
checked_add{T}(x1::T, x2::T, x3::T) = | |
checked_add(checked_add(x1, x2), x3) | |
checked_add{T}(x1::T, x2::T, x3::T, x4::T) = | |
checked_add(checked_add(x1, x2), x3, x4) | |
checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T) = | |
checked_add(checked_add(x1, x2), x3, x4, x5) | |
checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) = | |
checked_add(checked_add(x1, x2), x3, x4, x5, x6) | |
checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) = | |
checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7) | |
checked_add{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) = | |
checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7, x8) | |
""" | |
Base.sub_with_overflow(x, y) -> (r, f) | |
Calculates `r = x-y`, with the flag `f` indicating whether overflow has occurred. | |
""" | |
function sub_with_overflow end | |
sub_with_overflow{T<:SignedInt}(x::T, y::T) = checked_ssub_int(x, y) | |
sub_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_usub_int(x, y) | |
sub_with_overflow(x::Bool, y::Bool) = x-y, false | |
if BrokenSignedInt != Union{} | |
function sub_with_overflow{T<:BrokenSignedInt}(x::T, y::T) | |
r = x - y | |
# x and y have different signs, and the result has a different sign than x | |
f = (x<0) != (y<0) == (r<0) | |
r, f | |
end | |
end | |
if BrokenUnsignedInt != Union{} | |
function sub_with_overflow{T<:BrokenUnsignedInt}(x::T, y::T) | |
# x - y < 0 | |
x - y, x < y | |
end | |
end | |
""" | |
Base.checked_sub(x, y) | |
Calculates `x-y`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
function checked_sub{T<:Integer}(x::T, y::T) | |
@_inline_meta | |
z, b = sub_with_overflow(x, y) | |
b && throw(OverflowError()) | |
z | |
end | |
""" | |
Base.mul_with_overflow(x, y) -> (r, f) | |
Calculates `r = x*y`, with the flag `f` indicating whether overflow has occurred. | |
""" | |
function mul_with_overflow end | |
mul_with_overflow{T<:SignedInt}(x::T, y::T) = checked_smul_int(x, y) | |
mul_with_overflow{T<:UnsignedInt}(x::T, y::T) = checked_umul_int(x, y) | |
mul_with_overflow(x::Bool, y::Bool) = x*y, false | |
if BrokenSignedIntMul != Union{} && BrokenSignedIntMul != Int128 | |
function mul_with_overflow{T<:BrokenSignedIntMul}(x::T, y::T) | |
r = widemul(x, y) | |
f = r % T != r | |
r % T, f | |
end | |
end | |
if BrokenUnsignedIntMul != Union{} && BrokenUnsignedIntMul != UInt128 | |
function mul_with_overflow{T<:BrokenUnsignedIntMul}(x::T, y::T) | |
r = widemul(x, y) | |
f = r % T != r | |
r % T, f | |
end | |
end | |
if Int128 <: BrokenSignedIntMul | |
# Avoid BigInt | |
function mul_with_overflow{T<:Int128}(x::T, y::T) | |
f = if y > 0 | |
# x * y > typemax(T) | |
# x * y < typemin(T) | |
x > fld(typemax(T), y) || x < cld(typemin(T), y) | |
elseif y < 0 | |
# x * y > typemax(T) | |
# x * y < typemin(T) | |
# y == -1 can overflow fld | |
x < cld(typemax(T), y) || y != -1 && x > fld(typemin(T), y) | |
else | |
false | |
end | |
x*y, f | |
end | |
end | |
if UInt128 <: BrokenUnsignedIntMul | |
# Avoid BigInt | |
function mul_with_overflow{T<:UInt128}(x::T, y::T) | |
# x * y > typemax(T) | |
x * y, y > 0 && x > fld(typemax(T), y) | |
end | |
end | |
""" | |
Base.checked_mul(x, y) | |
Calculates `x*y`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
function checked_mul{T<:Integer}(x::T, y::T) | |
@_inline_meta | |
z, b = mul_with_overflow(x, y) | |
b && throw(OverflowError()) | |
z | |
end | |
# Handle multiple arguments | |
checked_mul(x) = x | |
checked_mul{T}(x1::T, x2::T, x3::T) = | |
checked_mul(checked_mul(x1, x2), x3) | |
checked_mul{T}(x1::T, x2::T, x3::T, x4::T) = | |
checked_mul(checked_mul(x1, x2), x3, x4) | |
checked_mul{T}(x1::T, x2::T, x3::T, x4::T, x5::T) = | |
checked_mul(checked_mul(x1, x2), x3, x4, x5) | |
checked_mul{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) = | |
checked_mul(checked_mul(x1, x2), x3, x4, x5, x6) | |
checked_mul{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) = | |
checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7) | |
checked_mul{T}(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) = | |
checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7, x8) | |
""" | |
Base.checked_div(x, y) | |
Calculates `div(x,y)`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
checked_div{T<:Integer}(x::T, y::T) = div(x, y) # Base.div already checks | |
""" | |
Base.checked_rem(x, y) | |
Calculates `x%y`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
checked_rem{T<:Integer}(x::T, y::T) = rem(x, y) # Base.rem already checks | |
""" | |
Base.checked_fld(x, y) | |
Calculates `fld(x,y)`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
checked_fld{T<:Integer}(x::T, y::T) = fld(x, y) # Base.fld already checks | |
""" | |
Base.checked_mod(x, y) | |
Calculates `mod(x,y)`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
checked_mod{T<:Integer}(x::T, y::T) = mod(x, y) # Base.mod already checks | |
""" | |
Base.checked_cld(x, y) | |
Calculates `cld(x,y)`, checking for overflow errors where applicable. | |
The overflow protection may impose a perceptible performance penalty. | |
""" | |
checked_cld{T<:Integer}(x::T, y::T) = cld(x, y) # Base.cld already checks | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## client.jl - frontend handling command line options, environment setup, | |
## and REPL | |
const text_colors = AnyDict( | |
:black => "\033[1m\033[30m", | |
:red => "\033[1m\033[31m", | |
:green => "\033[1m\033[32m", | |
:yellow => "\033[1m\033[33m", | |
:blue => "\033[1m\033[34m", | |
:magenta => "\033[1m\033[35m", | |
:cyan => "\033[1m\033[36m", | |
:white => "\033[1m\033[37m", | |
:normal => "\033[0m", | |
:bold => "\033[1m", | |
) | |
for i in 0:255 | |
text_colors[i] = "\033[1m\033[38;5;$(i)m" | |
end | |
# Create a docstring with an automatically generated list | |
# of colors. | |
const possible_formatting_symbols = [:normal, :bold] | |
available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors))) | |
available_text_colors = cat(1, | |
sort(intersect(available_text_colors, possible_formatting_symbols), rev=true), | |
sort(setdiff( available_text_colors, possible_formatting_symbols))) | |
const available_text_colors_docstring = | |
string(join([string("`:", key,"`") | |
for key in available_text_colors], ",\n", ", or \n")) | |
"""Dictionary of color codes for the terminal. | |
Available colors are: $available_text_colors_docstring as well as the integers 0 to 255 inclusive. | |
""" | |
text_colors | |
have_color = false | |
default_color_warn = :red | |
default_color_info = :cyan | |
if is_windows() | |
default_color_input = :normal | |
default_color_answer = :normal | |
else | |
default_color_input = :bold | |
default_color_answer = :bold | |
end | |
color_normal = text_colors[:normal] | |
function repl_color(key, default) | |
env_str = get(ENV, key, "") | |
c = tryparse(Int, env_str) | |
c_conv = isnull(c) ? Symbol(env_str) : get(c) | |
haskey(text_colors, c_conv) ? c_conv : default | |
end | |
warn_color() = repl_color("JULIA_WARN_COLOR", default_color_warn) | |
info_color() = repl_color("JULIA_INFO_COLOR", default_color_info) | |
input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)] | |
answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)] | |
function repl_cmd(cmd, out) | |
shell = shell_split(get(ENV,"JULIA_SHELL",get(ENV,"SHELL","/bin/sh"))) | |
# Note that we can't support the fish shell due to its lack of subshells | |
# See this for details: https://github.com/JuliaLang/julia/issues/4918 | |
if Base.basename(shell[1]) == "fish" | |
warn_once("cannot use the fish shell, defaulting to /bin/sh\ | |
set the JULIA_SHELL environment variable to silence this warning") | |
shell = "/bin/sh" | |
end | |
if isempty(cmd.exec) | |
throw(ArgumentError("no cmd to execute")) | |
elseif cmd.exec[1] == "cd" | |
new_oldpwd = pwd() | |
if length(cmd.exec) > 2 | |
throw(ArgumentError("cd method only takes one argument")) | |
elseif length(cmd.exec) == 2 | |
dir = cmd.exec[2] | |
if dir == "-" | |
if !haskey(ENV, "OLDPWD") | |
error("cd: OLDPWD not set") | |
end | |
cd(ENV["OLDPWD"]) | |
else | |
cd(@static is_windows() ? dir : readchomp(`$shell -c "echo $(shell_escape(dir))"`)) | |
end | |
else | |
cd() | |
end | |
ENV["OLDPWD"] = new_oldpwd | |
println(out, pwd()) | |
else | |
run(ignorestatus(@static is_windows() ? cmd : (isa(STDIN, TTY) ? `$shell -i -c "($(shell_escape(cmd))) && true"` : `$shell -c "($(shell_escape(cmd))) && true"`))) | |
end | |
nothing | |
end | |
display_error(er) = display_error(er, []) | |
function display_error(er, bt) | |
with_output_color(:red, STDERR) do io | |
print(io, "ERROR: ") | |
showerror(io, er, bt) | |
println(io) | |
end | |
end | |
function eval_user_input(ast::ANY, show_value) | |
errcount, lasterr, bt = 0, (), nothing | |
while true | |
try | |
if have_color | |
print(color_normal) | |
end | |
if errcount > 0 | |
display_error(lasterr,bt) | |
errcount, lasterr = 0, () | |
else | |
ast = expand(ast) | |
value = eval(Main, ast) | |
eval(Main, Expr(:(=), :ans, Expr(:call, ()->value))) | |
if value!==nothing && show_value | |
if have_color | |
print(answer_color()) | |
end | |
try display(value) | |
catch err | |
println(STDERR, "Evaluation succeeded, but an error occurred while showing value of type ", typeof(value), ":") | |
rethrow(err) | |
end | |
println() | |
end | |
end | |
break | |
catch err | |
if errcount > 0 | |
println(STDERR, "SYSTEM: show(lasterr) caused an error") | |
end | |
errcount, lasterr = errcount+1, err | |
if errcount > 2 | |
println(STDERR, "WARNING: it is likely that something important is broken, and Julia will not be able to continue normally") | |
break | |
end | |
bt = catch_backtrace() | |
end | |
end | |
isa(STDIN,TTY) && println() | |
end | |
syntax_deprecation_warnings(warn::Bool) = | |
ccall(:jl_parse_depwarn, Cint, (Cint,), warn) == 1 | |
function syntax_deprecation_warnings(f::Function, warn::Bool) | |
prev = syntax_deprecation_warnings(warn) | |
try | |
f() | |
finally | |
syntax_deprecation_warnings(prev) | |
end | |
end | |
function parse_input_line(s::String; filename::String="none") | |
# (expr, pos) = parse(s, 1) | |
# (ex, pos) = ccall(:jl_parse_string, Any, | |
# (Ptr{UInt8},Csize_t,Int32,Int32), | |
# s, sizeof(s), pos-1, 1) | |
# if ex!==() | |
# throw(ParseError("extra input after end of expression")) | |
# end | |
# expr | |
ccall(:jl_parse_input_line, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t), | |
s, sizeof(s), filename, sizeof(filename)) | |
end | |
parse_input_line(s::AbstractString) = parse_input_line(String(s)) | |
function parse_input_line(io::IO) | |
s = "" | |
while !eof(io) | |
s = s*readline(io) | |
e = parse_input_line(s) | |
if !(isa(e,Expr) && e.head === :incomplete) | |
return e | |
end | |
end | |
end | |
# detect the reason which caused an :incomplete expression | |
# from the error message | |
# NOTE: the error messages are defined in src/julia-parser.scm | |
incomplete_tag(ex) = :none | |
function incomplete_tag(ex::Expr) | |
Meta.isexpr(ex, :incomplete) || return :none | |
msg = ex.args[1] | |
contains(msg, "string") && return :string | |
contains(msg, "comment") && return :comment | |
contains(msg, "requires end") && return :block | |
contains(msg, "\"`\"") && return :cmd | |
contains(msg, "character") && return :char | |
return :other | |
end | |
# try to include() a file, ignoring if not found | |
try_include(path::AbstractString) = isfile(path) && include(path) | |
function process_options(opts::JLOptions) | |
if !isempty(ARGS) | |
idxs = find(x -> x == "--", ARGS) | |
length(idxs) > 0 && deleteat!(ARGS, idxs[1]) | |
end | |
repl = true | |
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) | |
while true | |
# startup worker. | |
# opts.startupfile, opts.load, etc should should not be processed for workers. | |
if opts.worker != C_NULL | |
start_worker(unsafe_string(opts.worker)) # does not return | |
end | |
# add processors | |
if opts.nprocs > 0 | |
addprocs(opts.nprocs) | |
end | |
# load processes from machine file | |
if opts.machinefile != C_NULL | |
addprocs(load_machine_file(unsafe_string(opts.machinefile))) | |
end | |
# load ~/.juliarc file | |
startup && load_juliarc() | |
# load file immediately on all processors | |
if opts.load != C_NULL | |
@sync for p in procs() | |
@async remotecall_fetch(include, p, unsafe_string(opts.load)) | |
end | |
end | |
# eval expression | |
if opts.eval != C_NULL | |
repl = false | |
eval(Main, parse_input_line(unsafe_string(opts.eval))) | |
break | |
end | |
# eval expression and show result | |
if opts.print != C_NULL | |
repl = false | |
show(eval(Main, parse_input_line(unsafe_string(opts.print)))) | |
println() | |
break | |
end | |
# eval expression but don't disable interactive mode | |
if opts.postboot != C_NULL | |
eval(Main, parse_input_line(unsafe_string(opts.postboot))) | |
end | |
# load file | |
if !isempty(ARGS) && !isempty(ARGS[1]) | |
# program | |
repl = false | |
# remove filename from ARGS | |
global PROGRAM_FILE = shift!(ARGS) | |
if !is_interactive | |
ccall(:jl_exit_on_sigint, Void, (Cint,), 1) | |
end | |
include(PROGRAM_FILE) | |
end | |
break | |
end | |
repl |= is_interactive | |
return (quiet,repl,startup,color_set,history_file) | |
end | |
function load_juliarc() | |
# If the user built us with a specific Base.SYSCONFDIR, check that location first for a juliarc.jl file | |
# If it is not found, then continue on to the relative path based on JULIA_HOME | |
if !isempty(Base.SYSCONFDIR) && isfile(joinpath(JULIA_HOME,Base.SYSCONFDIR,"julia","juliarc.jl")) | |
include(abspath(JULIA_HOME,Base.SYSCONFDIR,"julia","juliarc.jl")) | |
else | |
try_include(abspath(JULIA_HOME,"..","etc","julia","juliarc.jl")) | |
end | |
try_include(abspath(homedir(),".juliarc.jl")) | |
end | |
function load_machine_file(path::AbstractString) | |
machines = [] | |
for line in split(readstring(path),'\n'; keep=false) | |
s = map!(strip, split(line,'*'; keep=false)) | |
if length(s) > 1 | |
cnt = isnumber(s[1]) ? parse(Int,s[1]) : Symbol(s[1]) | |
push!(machines,(s[2], cnt)) | |
else | |
push!(machines,line) | |
end | |
end | |
return machines | |
end | |
import .Terminals | |
import .REPL | |
const repl_hooks = [] | |
""" | |
atreplinit(f) | |
Register a one-argument function to be called before the REPL interface is initialized in | |
interactive sessions; this is useful to customize the interface. The argument of `f` is the | |
REPL object. This function should be called from within the `.juliarc.jl` initialization | |
file. | |
""" | |
atreplinit(f::Function) = (unshift!(repl_hooks, f); nothing) | |
function _atreplinit(repl) | |
for f in repl_hooks | |
try | |
f(repl) | |
catch err | |
show(STDERR, err) | |
println(STDERR) | |
end | |
end | |
end | |
function _start() | |
empty!(ARGS) | |
append!(ARGS, Core.ARGS) | |
opts = JLOptions() | |
try | |
(quiet,repl,startup,color_set,history_file) = process_options(opts) | |
local term | |
global active_repl | |
global active_repl_backend | |
if repl | |
if !isa(STDIN,TTY) | |
global is_interactive |= !isa(STDIN, Union{File, IOStream}) | |
color_set || (global have_color = false) | |
else | |
term = Terminals.TTYTerminal(get(ENV, "TERM", @static is_windows() ? "" : "dumb"), STDIN, STDOUT, STDERR) | |
global is_interactive = true | |
color_set || (global have_color = Terminals.hascolor(term)) | |
quiet || REPL.banner(term,term) | |
if term.term_type == "dumb" | |
active_repl = REPL.BasicREPL(term) | |
quiet || warn("Terminal not fully functional") | |
else | |
active_repl = REPL.LineEditREPL(term, true) | |
active_repl.history_file = history_file | |
active_repl.hascolor = have_color | |
end | |
# Make sure any displays pushed in .juliarc.jl ends up above the | |
# REPLDisplay | |
pushdisplay(REPL.REPLDisplay(active_repl)) | |
end | |
end | |
if repl | |
if !isa(STDIN,TTY) | |
# note: currently IOStream is used for file STDIN | |
if isa(STDIN,File) || isa(STDIN,IOStream) | |
# reading from a file, behave like include | |
eval(Main,parse_input_line(readstring(STDIN))) | |
else | |
# otherwise behave repl-like | |
while !eof(STDIN) | |
eval_user_input(parse_input_line(STDIN), true) | |
end | |
end | |
else | |
_atreplinit(active_repl) | |
REPL.run_repl(active_repl, backend->(global active_repl_backend = backend)) | |
end | |
end | |
catch err | |
display_error(err,catch_backtrace()) | |
exit(1) | |
end | |
if is_interactive && have_color | |
print(color_normal) | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
import .Serializer: known_object_data, object_number, serialize_cycle, deserialize_cycle, writetag, | |
__deserialized_types__, serialize_typename, deserialize_typename, | |
TYPENAME_TAG, object_numbers | |
type ClusterSerializer{I<:IO} <: AbstractSerializer | |
io::I | |
counter::Int | |
table::ObjectIdDict | |
sent_objects::Set{UInt64} # used by serialize (track objects sent) | |
ClusterSerializer(io::I) = new(io, 0, ObjectIdDict(), Set{UInt64}()) | |
end | |
ClusterSerializer(io::IO) = ClusterSerializer{typeof(io)}(io) | |
function deserialize(s::ClusterSerializer, ::Type{TypeName}) | |
full_body_sent = deserialize(s) | |
number = read(s.io, UInt64) | |
if !full_body_sent | |
tn = get(known_object_data, number, nothing)::TypeName | |
if !haskey(object_numbers, tn) | |
# set up reverse mapping for serialize | |
object_numbers[tn] = number | |
end | |
deserialize_cycle(s, tn) | |
else | |
tn = deserialize_typename(s, number) | |
end | |
return tn | |
end | |
function serialize(s::ClusterSerializer, t::TypeName) | |
serialize_cycle(s, t) && return | |
writetag(s.io, TYPENAME_TAG) | |
identifier = object_number(t) | |
send_whole = !(identifier in s.sent_objects) | |
serialize(s, send_whole) | |
write(s.io, identifier) | |
if send_whole | |
serialize_typename(s, t) | |
push!(s.sent_objects, identifier) | |
end | |
# println(t.module, ":", t.name, ", id:", identifier, send_whole ? " sent" : " NOT sent") | |
nothing | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Collections | |
import Base: setindex!, done, get, hash, haskey, isempty, length, next, getindex, start, copymutable | |
import ..Order: Forward, Ordering, lt | |
export | |
PriorityQueue, | |
dequeue!, | |
enqueue!, | |
heapify!, | |
heapify, | |
heappop!, | |
heappush!, | |
isheap, | |
peek | |
# Some algorithms that can be defined only after infrastructure is in place | |
Base.append!(a::Vector, iter) = _append!(a, Base.iteratorsize(iter), iter) | |
function _append!(a, ::Base.HasLength, iter) | |
n = length(a) | |
resize!(a, n+length(iter)) | |
@inbounds for (i,item) in zip(n+1:length(a), iter) | |
a[i] = item | |
end | |
a | |
end | |
function _append!(a, ::Base.IteratorSize, iter) | |
for item in iter | |
push!(a, item) | |
end | |
a | |
end | |
# Heap operations on flat arrays | |
# ------------------------------ | |
# Binary heap indexing | |
heapleft(i::Integer) = 2i | |
heapright(i::Integer) = 2i + 1 | |
heapparent(i::Integer) = div(i, 2) | |
# Binary min-heap percolate down. | |
function percolate_down!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward, len::Integer=length(xs)) | |
@inbounds while (l = heapleft(i)) <= len | |
r = heapright(i) | |
j = r > len || lt(o, xs[l], xs[r]) ? l : r | |
if lt(o, xs[j], x) | |
xs[i] = xs[j] | |
i = j | |
else | |
break | |
end | |
end | |
xs[i] = x | |
end | |
percolate_down!(xs::AbstractArray, i::Integer, o::Ordering, len::Integer=length(xs)) = percolate_down!(xs, i, xs[i], o, len) | |
# Binary min-heap percolate up. | |
function percolate_up!(xs::AbstractArray, i::Integer, x=xs[i], o::Ordering=Forward) | |
@inbounds while (j = heapparent(i)) >= 1 | |
if lt(o, x, xs[j]) | |
xs[i] = xs[j] | |
i = j | |
else | |
break | |
end | |
end | |
xs[i] = x | |
end | |
percolate_up!{T}(xs::AbstractArray{T}, i::Integer, o::Ordering) = percolate_up!(xs, i, xs[i], o) | |
""" | |
heappop!(v, [ord]) | |
Given a binary heap-ordered array, remove and return the lowest ordered element. | |
For efficiency, this function does not check that the array is indeed heap-ordered. | |
""" | |
function heappop!(xs::AbstractArray, o::Ordering=Forward) | |
x = xs[1] | |
y = pop!(xs) | |
if !isempty(xs) | |
percolate_down!(xs, 1, y, o) | |
end | |
x | |
end | |
""" | |
heappush!(v, x, [ord]) | |
Given a binary heap-ordered array, push a new element `x`, preserving the heap property. | |
For efficiency, this function does not check that the array is indeed heap-ordered. | |
""" | |
function heappush!(xs::AbstractArray, x, o::Ordering=Forward) | |
push!(xs, x) | |
percolate_up!(xs, length(xs), x, o) | |
xs | |
end | |
# Turn an arbitrary array into a binary min-heap in linear time. | |
""" | |
heapify!(v, ord::Ordering=Forward) | |
In-place [`heapify`](:func:`heapify`). | |
""" | |
function heapify!(xs::AbstractArray, o::Ordering=Forward) | |
for i in heapparent(length(xs)):-1:1 | |
percolate_down!(xs, i, o) | |
end | |
xs | |
end | |
""" | |
heapify(v, ord::Ordering=Forward) | |
Returns a new vector in binary heap order, optionally using the given ordering. | |
```jldoctest | |
julia> a = [1,3,4,5,2]; | |
julia> Base.Collections.heapify(a) | |
5-element Array{Int64,1}: | |
1 | |
2 | |
4 | |
5 | |
3 | |
julia> Base.Collections.heapify(a, Base.Order.Reverse) | |
5-element Array{Int64,1}: | |
5 | |
3 | |
4 | |
1 | |
2 | |
``` | |
""" | |
heapify(xs::AbstractArray, o::Ordering=Forward) = heapify!(copymutable(xs), o) | |
""" | |
isheap(v, ord::Ordering=Forward) | |
Return `true` if an array is heap-ordered according to the given order. | |
```jldoctest | |
julia> a = [1,2,3] | |
3-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
julia> Base.Collections.isheap(a,Base.Order.Forward) | |
true | |
julia> Base.Collections.isheap(a,Base.Order.Reverse) | |
false | |
``` | |
""" | |
function isheap(xs::AbstractArray, o::Ordering=Forward) | |
for i in 1:div(length(xs), 2) | |
if lt(o, xs[heapleft(i)], xs[i]) || | |
(heapright(i) <= length(xs) && lt(o, xs[heapright(i)], xs[i])) | |
return false | |
end | |
end | |
true | |
end | |
# PriorityQueue | |
# ------------- | |
""" | |
PriorityQueue(K, V, [ord]) | |
Construct a new [`PriorityQueue`](:obj:`PriorityQueue`), with keys of type | |
`K` and values/priorites of type `V`. | |
If an order is not given, the priority queue is min-ordered using | |
the default comparison for `V`. | |
A `PriorityQueue` acts like a `Dict`, mapping values to their | |
priorities, with the addition of a `dequeue!` function to remove the | |
lowest priority element. | |
```jldoctest | |
julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) | |
Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: | |
"c" => 1 | |
"b" => 3 | |
"a" => 2 | |
``` | |
""" | |
type PriorityQueue{K,V,O<:Ordering} <: Associative{K,V} | |
# Binary heap of (element, priority) pairs. | |
xs::Array{Pair{K,V}, 1} | |
o::O | |
# Map elements to their index in xs | |
index::Dict{K, Int} | |
function PriorityQueue(o::O) | |
new(Array{Pair{K,V}}(0), o, Dict{K, Int}()) | |
end | |
PriorityQueue() = PriorityQueue{K,V,O}(Forward) | |
function PriorityQueue(ks::AbstractArray{K}, vs::AbstractArray{V}, | |
o::O) | |
# TODO: maybe deprecate | |
if length(ks) != length(vs) | |
throw(ArgumentError("key and value arrays must have equal lengths")) | |
end | |
PriorityQueue{K,V,O}(zip(ks, vs), o) | |
end | |
function PriorityQueue(itr, o::O) | |
xs = Array{Pair{K,V}}(length(itr)) | |
index = Dict{K, Int}() | |
for (i, (k, v)) in enumerate(itr) | |
xs[i] = Pair{K,V}(k, v) | |
if haskey(index, k) | |
throw(ArgumentError("PriorityQueue keys must be unique")) | |
end | |
index[k] = i | |
end | |
pq = new(xs, o, index) | |
# heapify | |
for i in heapparent(length(pq.xs)):-1:1 | |
percolate_down!(pq, i) | |
end | |
pq | |
end | |
end | |
PriorityQueue(o::Ordering=Forward) = PriorityQueue{Any,Any,typeof(o)}(o) | |
PriorityQueue{K,V}(::Type{K}, ::Type{V}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(o) | |
# TODO: maybe deprecate | |
PriorityQueue{K,V}(ks::AbstractArray{K}, vs::AbstractArray{V}, | |
o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(ks, vs, o) | |
PriorityQueue{K,V}(kvs::Associative{K,V}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(kvs, o) | |
PriorityQueue{K,V}(a::AbstractArray{Tuple{K,V}}, o::Ordering=Forward) = PriorityQueue{K,V,typeof(o)}(a, o) | |
length(pq::PriorityQueue) = length(pq.xs) | |
isempty(pq::PriorityQueue) = isempty(pq.xs) | |
haskey(pq::PriorityQueue, key) = haskey(pq.index, key) | |
""" | |
peek(pq) | |
Return the lowest priority key from a priority queue without removing that | |
key from the queue. | |
""" | |
peek(pq::PriorityQueue) = pq.xs[1] | |
function percolate_down!(pq::PriorityQueue, i::Integer) | |
x = pq.xs[i] | |
@inbounds while (l = heapleft(i)) <= length(pq) | |
r = heapright(i) | |
j = r > length(pq) || lt(pq.o, pq.xs[l].second, pq.xs[r].second) ? l : r | |
if lt(pq.o, pq.xs[j].second, x.second) | |
pq.index[pq.xs[j].first] = i | |
pq.xs[i] = pq.xs[j] | |
i = j | |
else | |
break | |
end | |
end | |
pq.index[x.first] = i | |
pq.xs[i] = x | |
end | |
function percolate_up!(pq::PriorityQueue, i::Integer) | |
x = pq.xs[i] | |
@inbounds while i > 1 | |
j = heapparent(i) | |
if lt(pq.o, x.second, pq.xs[j].second) | |
pq.index[pq.xs[j].first] = i | |
pq.xs[i] = pq.xs[j] | |
i = j | |
else | |
break | |
end | |
end | |
pq.index[x.first] = i | |
pq.xs[i] = x | |
end | |
# Equivalent to percolate_up! with an element having lower priority than any other | |
function force_up!(pq::PriorityQueue, i::Integer) | |
x = pq.xs[i] | |
@inbounds while i > 1 | |
j = heapparent(i) | |
pq.index[pq.xs[j].first] = i | |
pq.xs[i] = pq.xs[j] | |
i = j | |
end | |
pq.index[x.first] = i | |
pq.xs[i] = x | |
end | |
function getindex{K,V}(pq::PriorityQueue{K,V}, key) | |
pq.xs[pq.index[key]].second | |
end | |
function get{K,V}(pq::PriorityQueue{K,V}, key, deflt) | |
i = get(pq.index, key, 0) | |
i == 0 ? deflt : pq.xs[i].second | |
end | |
# Change the priority of an existing element, or equeue it if it isn't present. | |
function setindex!{K,V}(pq::PriorityQueue{K, V}, value, key) | |
if haskey(pq, key) | |
i = pq.index[key] | |
oldvalue = pq.xs[i].second | |
pq.xs[i] = Pair{K,V}(key, value) | |
if lt(pq.o, oldvalue, value) | |
percolate_down!(pq, i) | |
else | |
percolate_up!(pq, i) | |
end | |
else | |
enqueue!(pq, key, value) | |
end | |
value | |
end | |
""" | |
enqueue!(pq, k, v) | |
Insert the a key `k` into a priority queue `pq` with priority `v`. | |
```jldoctest | |
julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) | |
Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: | |
"c" => 1 | |
"b" => 3 | |
"a" => 2 | |
julia> Base.Collections.enqueue!(a, "d", 4) | |
Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 4 entries: | |
"c" => 1 | |
"b" => 3 | |
"a" => 2 | |
"d" => 4 | |
``` | |
""" | |
function enqueue!{K,V}(pq::PriorityQueue{K,V}, key, value) | |
if haskey(pq, key) | |
throw(ArgumentError("PriorityQueue keys must be unique")) | |
end | |
push!(pq.xs, Pair{K,V}(key, value)) | |
pq.index[key] = length(pq) | |
percolate_up!(pq, length(pq)) | |
pq | |
end | |
""" | |
dequeue!(pq) | |
Remove and return the lowest priority key from a priority queue. | |
```jldoctest | |
julia> a = Base.Collections.PriorityQueue(["a","b","c"],[2,3,1],Base.Order.Forward) | |
Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 3 entries: | |
"c" => 1 | |
"b" => 3 | |
"a" => 2 | |
julia> Base.Collections.dequeue!(a) | |
"c" | |
julia> a | |
Base.Collections.PriorityQueue{String,Int64,Base.Order.ForwardOrdering} with 2 entries: | |
"b" => 3 | |
"a" => 2 | |
``` | |
""" | |
function dequeue!(pq::PriorityQueue) | |
x = pq.xs[1] | |
y = pop!(pq.xs) | |
if !isempty(pq) | |
pq.xs[1] = y | |
pq.index[y.first] = 1 | |
percolate_down!(pq, 1) | |
end | |
delete!(pq.index, x.first) | |
x.first | |
end | |
function dequeue!(pq::PriorityQueue, key) | |
idx = pq.index[key] | |
force_up!(pq, idx) | |
dequeue!(pq) | |
key | |
end | |
# Unordered iteration through key value pairs in a PriorityQueue | |
start(pq::PriorityQueue) = start(pq.index) | |
done(pq::PriorityQueue, i) = done(pq.index, i) | |
function next{K,V}(pq::PriorityQueue{K,V}, i) | |
(k, idx), i = next(pq.index, i) | |
return (pq.xs[idx], i) | |
end | |
end # module Collections | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Factorials | |
const _fact_table64 = | |
Int64[1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800, | |
87178291200,1307674368000,20922789888000,355687428096000,6402373705728000, | |
121645100408832000,2432902008176640000] | |
const _fact_table128 = | |
UInt128[0x00000000000000000000000000000001, 0x00000000000000000000000000000002, | |
0x00000000000000000000000000000006, 0x00000000000000000000000000000018, | |
0x00000000000000000000000000000078, 0x000000000000000000000000000002d0, | |
0x000000000000000000000000000013b0, 0x00000000000000000000000000009d80, | |
0x00000000000000000000000000058980, 0x00000000000000000000000000375f00, | |
0x00000000000000000000000002611500, 0x0000000000000000000000001c8cfc00, | |
0x0000000000000000000000017328cc00, 0x0000000000000000000000144c3b2800, | |
0x00000000000000000000013077775800, 0x00000000000000000000130777758000, | |
0x00000000000000000001437eeecd8000, 0x00000000000000000016beecca730000, | |
0x000000000000000001b02b9306890000, 0x000000000000000021c3677c82b40000, | |
0x0000000000000002c5077d36b8c40000, 0x000000000000003ceea4c2b3e0d80000, | |
0x000000000000057970cd7e2933680000, 0x00000000000083629343d3dcd1c00000, | |
0x00000000000cd4a0619fb0907bc00000, 0x00000000014d9849ea37eeac91800000, | |
0x00000000232f0fcbb3e62c3358800000, 0x00000003d925ba47ad2cd59dae000000, | |
0x0000006f99461a1e9e1432dcb6000000, 0x00000d13f6370f96865df5dd54000000, | |
0x0001956ad0aae33a4560c5cd2c000000, 0x0032ad5a155c6748ac18b9a580000000, | |
0x0688589cc0e9505e2f2fee5580000000, 0xde1bc4d19efcac82445da75b00000000] | |
function factorial_lookup(n::Integer, table, lim) | |
n < 0 && throw(DomainError()) | |
n > lim && throw(OverflowError()) | |
n == 0 && return one(n) | |
@inbounds f = table[n] | |
return oftype(n, f) | |
end | |
factorial(n::Int128) = factorial_lookup(n, _fact_table128, 33) | |
factorial(n::UInt128) = factorial_lookup(n, _fact_table128, 34) | |
factorial(n::Union{Int64,UInt64}) = factorial_lookup(n, _fact_table64, 20) | |
if Int === Int32 | |
factorial(n::Union{Int8,UInt8,Int16,UInt16}) = factorial(Int32(n)) | |
factorial(n::Union{Int32,UInt32}) = factorial_lookup(n, _fact_table64, 12) | |
else | |
factorial(n::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32}) = factorial(Int64(n)) | |
end | |
function gamma(n::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64}) | |
n < 0 && throw(DomainError()) | |
n == 0 && return Inf | |
n <= 2 && return 1.0 | |
n > 20 && return gamma(Float64(n)) | |
@inbounds return Float64(_fact_table64[n-1]) | |
end | |
# Basic functions for working with permutations | |
""" | |
isperm(v) -> Bool | |
Returns `true` if `v` is a valid permutation. | |
```jldoctest | |
julia> isperm([1; 2]) | |
true | |
julia> isperm([1; 3]) | |
false | |
``` | |
""" | |
function isperm(A) | |
n = length(A) | |
used = falses(n) | |
for a in A | |
(0 < a <= n) && (used[a] $= true) || return false | |
end | |
true | |
end | |
isperm(p::Tuple{}) = true | |
isperm(p::Tuple{Int}) = p[1] == 1 | |
isperm(p::Tuple{Int,Int}) = ((p[1] == 1) & (p[2] == 2)) | ((p[1] == 2) & (p[2] == 1)) | |
function permute!!{T<:Integer}(a, p::AbstractVector{T}) | |
count = 0 | |
start = 0 | |
while count < length(a) | |
ptr = start = findnext(p, start+1) | |
temp = a[start] | |
next = p[start] | |
count += 1 | |
while next != start | |
a[ptr] = a[next] | |
p[ptr] = 0 | |
ptr = next | |
next = p[next] | |
count += 1 | |
end | |
a[ptr] = temp | |
p[ptr] = 0 | |
end | |
a | |
end | |
""" | |
permute!(v, p) | |
Permute vector `v` in-place, according to permutation `p`. No checking is done | |
to verify that `p` is a permutation. | |
To return a new permutation, use `v[p]`. Note that this is generally faster than | |
`permute!(v,p)` for large vectors. | |
""" | |
permute!(a, p::AbstractVector) = permute!!(a, copymutable(p)) | |
function ipermute!!{T<:Integer}(a, p::AbstractVector{T}) | |
count = 0 | |
start = 0 | |
while count < length(a) | |
start = findnext(p, start+1) | |
temp = a[start] | |
next = p[start] | |
count += 1 | |
while next != start | |
temp_next = a[next] | |
a[next] = temp | |
temp = temp_next | |
ptr = p[next] | |
p[next] = 0 | |
next = ptr | |
count += 1 | |
end | |
a[next] = temp | |
p[next] = 0 | |
end | |
a | |
end | |
""" | |
ipermute!(v, p) | |
Like `permute!`, but the inverse of the given permutation is applied. | |
""" | |
ipermute!(a, p::AbstractVector) = ipermute!!(a, copymutable(p)) | |
""" | |
invperm(v) | |
Return the inverse permutation of `v`. | |
If `B = A[v]`, then `A == B[invperm(v)]`. | |
```jldoctest | |
julia> v = [2; 4; 3; 1]; | |
julia> invperm(v) | |
4-element Array{Int64,1}: | |
4 | |
1 | |
3 | |
2 | |
julia> A = ['a','b','c','d']; | |
julia> B = A[v] | |
4-element Array{Char,1}: | |
'b' | |
'd' | |
'c' | |
'a' | |
julia> B[invperm(v)] | |
4-element Array{Char,1}: | |
'a' | |
'b' | |
'c' | |
'd' | |
``` | |
""" | |
function invperm(a::AbstractVector) | |
b = zero(a) # similar vector of zeros | |
n = length(a) | |
@inbounds for (i, j) in enumerate(a) | |
((1 <= j <= n) && b[j] == 0) || | |
throw(ArgumentError("argument is not a permutation")) | |
b[j] = i | |
end | |
b | |
end | |
function invperm(p::Union{Tuple{},Tuple{Int},Tuple{Int,Int}}) | |
isperm(p) || throw(ArgumentError("argument is not a permutation")) | |
p # in dimensions 0-2, every permutation is its own inverse | |
end | |
invperm(a::Tuple) = (invperm([a...])...,) | |
#XXX This function should be moved to Combinatorics.jl but is currently used by Base.DSP. | |
""" | |
nextprod([k_1,k_2,...], n) | |
Next integer not less than `n` that can be written as ``\\prod k_i^{p_i}`` for integers | |
``p_1``, ``p_2``, etc. | |
""" | |
function nextprod(a::Vector{Int}, x) | |
if x > typemax(Int) | |
throw(ArgumentError("unsafe for x > typemax(Int), got $x")) | |
end | |
k = length(a) | |
v = ones(Int, k) # current value of each counter | |
mx = [nextpow(ai,x) for ai in a] # maximum value of each counter | |
v[1] = mx[1] # start at first case that is >= x | |
p::widen(Int) = mx[1] # initial value of product in this case | |
best = p | |
icarry = 1 | |
while v[end] < mx[end] | |
if p >= x | |
best = p < best ? p : best # keep the best found yet | |
carrytest = true | |
while carrytest | |
p = div(p, v[icarry]) | |
v[icarry] = 1 | |
icarry += 1 | |
p *= a[icarry] | |
v[icarry] *= a[icarry] | |
carrytest = v[icarry] > mx[icarry] && icarry < k | |
end | |
if p < x | |
icarry = 1 | |
end | |
else | |
while p < x | |
p *= a[1] | |
v[1] *= a[1] | |
end | |
end | |
end | |
# might overflow, but want predictable return type | |
return mx[end] < best ? Int(mx[end]) : Int(best) | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
immutable Complex{T<:Real} <: Number | |
re::T | |
im::T | |
end | |
Complex(x::Real, y::Real) = Complex(promote(x,y)...) | |
Complex(x::Real) = Complex(x, zero(x)) | |
""" | |
im | |
The imaginary unit. | |
""" | |
const im = Complex(false,true) | |
typealias Complex128 Complex{Float64} | |
typealias Complex64 Complex{Float32} | |
typealias Complex32 Complex{Float16} | |
convert{T<:Real}(::Type{Complex{T}}, x::Real) = Complex{T}(x,0) | |
convert{T<:Real}(::Type{Complex{T}}, z::Complex) = Complex{T}(real(z),imag(z)) | |
convert{T<:Real}(::Type{T}, z::Complex) = | |
isreal(z) ? convert(T,real(z)) : throw(InexactError()) | |
convert(::Type{Complex}, z::Complex) = z | |
convert(::Type{Complex}, x::Real) = Complex(x) | |
promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{S}) = | |
Complex{promote_type(T,S)} | |
promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{Complex{S}}) = | |
Complex{promote_type(T,S)} | |
widen{T}(::Type{Complex{T}}) = Complex{widen(T)} | |
""" | |
real(z) | |
Return the real part of the complex number `z`. | |
""" | |
real(z::Complex) = z.re | |
""" | |
imag(z) | |
Return the imaginary part of the complex number `z`. | |
""" | |
imag(z::Complex) = z.im | |
real(x::Real) = x | |
imag(x::Real) = zero(x) | |
""" | |
reim(z) | |
Return both the real and imaginary parts of the complex number `z`. | |
""" | |
reim(z) = (real(z), imag(z)) | |
real{T<:Real}(::Type{T}) = T | |
real{T<:Real}(::Type{Complex{T}}) = T | |
complex{T<:Real}(::Type{T}) = Complex{T} | |
complex{T<:Real}(::Type{Complex{T}}) = Complex{T} | |
isreal(x::Real) = true | |
isreal(z::Complex) = imag(z) == 0 | |
""" | |
isimag(z) -> Bool | |
Test whether `z` is purely imaginary, i.e. has a real part equal to 0. | |
""" | |
isimag(z::Number) = real(z) == 0 | |
isinteger(z::Complex) = isreal(z) & isinteger(real(z)) | |
isfinite(z::Complex) = isfinite(real(z)) & isfinite(imag(z)) | |
isnan(z::Complex) = isnan(real(z)) | isnan(imag(z)) | |
isinf(z::Complex) = isinf(real(z)) | isinf(imag(z)) | |
complex(x::Real, y::Real) = Complex(x, y) | |
complex(x::Real) = Complex(x) | |
complex(z::Complex) = z | |
flipsign(x::Complex, y::Real) = ifelse(signbit(y), -x, x) | |
function show(io::IO, z::Complex) | |
r, i = reim(z) | |
compact = get(io, :compact, false) | |
show(io, r) | |
if signbit(i) && !isnan(i) | |
i = -i | |
print(io, compact ? "-" : " - ") | |
else | |
print(io, compact ? "+" : " + ") | |
end | |
show(io, i) | |
if !(isa(i,Integer) && !isa(i,Bool) || isa(i,AbstractFloat) && isfinite(i)) | |
print(io, "*") | |
end | |
print(io, "im") | |
end | |
show(io::IO, z::Complex{Bool}) = | |
print(io, z == im ? "im" : "Complex($(z.re),$(z.im))") | |
function read{T<:Real}(s::IO, ::Type{Complex{T}}) | |
r = read(s,T) | |
i = read(s,T) | |
Complex{T}(r,i) | |
end | |
function write(s::IO, z::Complex) | |
write(s,real(z),imag(z)) | |
end | |
## equality and hashing of complex numbers ## | |
==(z::Complex, w::Complex) = (real(z) == real(w)) & (imag(z) == imag(w)) | |
==(z::Complex, x::Real) = isreal(z) && real(z) == x | |
==(x::Real, z::Complex) = isreal(z) && real(z) == x | |
isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w)) | |
if UInt === UInt64 | |
const h_imag = 0x32a7a07f3e7cd1f9 | |
else | |
const h_imag = 0x3e7cd1f9 | |
end | |
const hash_0_imag = hash(0, h_imag) | |
function hash(z::Complex, h::UInt) | |
# TODO: with default argument specialization, this would be better: | |
# hash(real(z), h $ hash(imag(z), h $ h_imag) $ hash(0, h $ h_imag)) | |
hash(real(z), h $ hash(imag(z), h_imag) $ hash_0_imag) | |
end | |
## generic functions of complex numbers ## | |
""" | |
conj(z) | |
Compute the complex conjugate of a complex number `z`. | |
""" | |
conj(z::Complex) = Complex(real(z),-imag(z)) | |
abs(z::Complex) = hypot(real(z), imag(z)) | |
abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z) | |
inv(z::Complex) = conj(z)/abs2(z) | |
inv{T<:Integer}(z::Complex{T}) = inv(float(z)) | |
-(z::Complex) = Complex(-real(z), -imag(z)) | |
+(z::Complex, w::Complex) = Complex(real(z) + real(w), imag(z) + imag(w)) | |
-(z::Complex, w::Complex) = Complex(real(z) - real(w), imag(z) - imag(w)) | |
*(z::Complex, w::Complex) = Complex(real(z) * real(w) - imag(z) * imag(w), | |
real(z) * imag(w) + imag(z) * real(w)) | |
muladd(z::Complex, w::Complex, x::Complex) = | |
Complex(muladd(real(z), real(w), real(x)) - imag(z)*imag(w), # TODO: use mulsub given #15985 | |
muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x)))) | |
# handle Bool and Complex{Bool} | |
# avoid type signature ambiguity warnings | |
+(x::Bool, z::Complex{Bool}) = Complex(x + real(z), imag(z)) | |
+(z::Complex{Bool}, x::Bool) = Complex(real(z) + x, imag(z)) | |
-(x::Bool, z::Complex{Bool}) = Complex(x - real(z), - imag(z)) | |
-(z::Complex{Bool}, x::Bool) = Complex(real(z) - x, imag(z)) | |
*(x::Bool, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) | |
*(z::Complex{Bool}, x::Bool) = Complex(real(z) * x, imag(z) * x) | |
+(x::Bool, z::Complex) = Complex(x + real(z), imag(z)) | |
+(z::Complex, x::Bool) = Complex(real(z) + x, imag(z)) | |
-(x::Bool, z::Complex) = Complex(x - real(z), - imag(z)) | |
-(z::Complex, x::Bool) = Complex(real(z) - x, imag(z)) | |
*(x::Bool, z::Complex) = Complex(x * real(z), x * imag(z)) | |
*(z::Complex, x::Bool) = Complex(real(z) * x, imag(z) * x) | |
+(x::Real, z::Complex{Bool}) = Complex(x + real(z), imag(z)) | |
+(z::Complex{Bool}, x::Real) = Complex(real(z) + x, imag(z)) | |
function -(x::Real, z::Complex{Bool}) | |
# we don't want the default type for -(Bool) | |
re = x-real(z) | |
Complex(re, - oftype(re, imag(z))) | |
end | |
-(z::Complex{Bool}, x::Real) = Complex(real(z) - x, imag(z)) | |
*(x::Real, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) | |
*(z::Complex{Bool}, x::Real) = Complex(real(z) * x, imag(z) * x) | |
# adding or multiplying real & complex is common | |
+(x::Real, z::Complex) = Complex(x + real(z), imag(z)) | |
+(z::Complex, x::Real) = Complex(x + real(z), imag(z)) | |
function -(x::Real, z::Complex) | |
# we don't want the default type for -(Bool) | |
re = x - real(z) | |
Complex(re, - oftype(re, imag(z))) | |
end | |
-(z::Complex, x::Real) = Complex(real(z) - x, imag(z)) | |
*(x::Real, z::Complex) = Complex(x * real(z), x * imag(z)) | |
*(z::Complex, x::Real) = Complex(x * real(z), x * imag(z)) | |
muladd(x::Real, z::Complex, y::Number) = muladd(z, x, y) | |
muladd(z::Complex, x::Real, y::Real) = Complex(muladd(real(z),x,y), imag(z)*x) | |
muladd(z::Complex, x::Real, w::Complex) = | |
Complex(muladd(real(z),x,real(w)), muladd(imag(z),x,imag(w))) | |
muladd(x::Real, y::Real, z::Complex) = Complex(muladd(x,y,real(z)), imag(z)) | |
muladd(z::Complex, w::Complex, x::Real) = | |
Complex(muladd(real(z), real(w), x) - imag(z)*imag(w), # TODO: use mulsub given #15985 | |
muladd(real(z), imag(w), imag(z) * real(w))) | |
/(a::Real, z::Complex) = a*inv(z) | |
/(z::Complex, x::Real) = Complex(real(z)/x, imag(z)/x) | |
function /{T<:Real}(a::Complex{T}, b::Complex{T}) | |
are = real(a); aim = imag(a); bre = real(b); bim = imag(b) | |
if abs(bre) <= abs(bim) | |
if isinf(bre) && isinf(bim) | |
r = sign(bre)/sign(bim) | |
else | |
r = bre / bim | |
end | |
den = bim + r*bre | |
Complex((are*r + aim)/den, (aim*r - are)/den) | |
else | |
if isinf(bre) && isinf(bim) | |
r = sign(bim)/sign(bre) | |
else | |
r = bim / bre | |
end | |
den = bre + r*bim | |
Complex((are + aim*r)/den, (aim - are*r)/den) | |
end | |
end | |
inv{T<:Union{Float16,Float32}}(z::Complex{T}) = | |
oftype(z, conj(widen(z))/abs2(widen(z))) | |
/{T<:Union{Float16,Float32}}(z::Complex{T}, w::Complex{T}) = | |
oftype(z, widen(z)*inv(widen(w))) | |
# robust complex division for double precision | |
# the first step is to scale variables if appropriate ,then do calculations | |
# in a way that avoids over/underflow (subfuncs 1 and 2), then undo the scaling. | |
# scaling variable s and other techniques | |
# based on arxiv.1210.4539 | |
# a + i*b | |
# p + i*q = --------- | |
# c + i*d | |
function /(z::Complex128, w::Complex128) | |
a, b = reim(z); c, d = reim(w) | |
half = 0.5 | |
two = 2.0 | |
ab = max(abs(a), abs(b)) | |
cd = max(abs(c), abs(d)) | |
ov = realmax(a) | |
un = realmin(a) | |
ϵ = eps(Float64) | |
bs = two/(ϵ*ϵ) | |
s = 1.0 | |
ab >= half*ov && (a=half*a; b=half*b; s=two*s ) # scale down a,b | |
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d | |
ab <= un*two/ϵ && (a=a*bs; b=b*bs; s=s/bs ) # scale up a,b | |
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d | |
abs(d)<=abs(c) ? ((p,q)=robust_cdiv1(a,b,c,d) ) : ((p,q)=robust_cdiv1(b,a,d,c); q=-q) | |
return Complex128(p*s,q*s) # undo scaling | |
end | |
function robust_cdiv1(a::Float64, b::Float64, c::Float64, d::Float64) | |
r = d/c | |
t = 1.0/(c+d*r) | |
p = robust_cdiv2(a,b,c,d,r,t) | |
q = robust_cdiv2(b,-a,c,d,r,t) | |
return p,q | |
end | |
function robust_cdiv2(a::Float64, b::Float64, c::Float64, d::Float64, r::Float64, t::Float64) | |
if r != 0 | |
br = b*r | |
return (br != 0 ? (a+br)*t : a*t + (b*t)*r) | |
else | |
return (a + d*(b/c)) * t | |
end | |
end | |
function inv(w::Complex128) | |
c, d = reim(w) | |
half = 0.5 | |
two = 2.0 | |
cd = max(abs(c), abs(d)) | |
ov = realmax(c) | |
un = realmin(c) | |
ϵ = eps(Float64) | |
bs = two/(ϵ*ϵ) | |
s = 1.0 | |
cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d | |
cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d | |
if abs(d)<=abs(c) | |
r = d/c | |
t = 1.0/(c+d*r) | |
p = t | |
q = -r * t | |
else | |
c, d = d, c | |
r = d/c | |
t = 1.0/(c+d*r) | |
p = r * t | |
q = -t | |
end | |
return Complex128(p*s,q*s) # undo scaling | |
end | |
function ssqs{T<:AbstractFloat}(x::T, y::T) | |
k::Int = 0 | |
ρ = x*x + y*y | |
if !isfinite(ρ) && (isinf(x) || isinf(y)) | |
ρ = convert(T, Inf) | |
elseif isinf(ρ) || (ρ==0 && (x!=0 || y!=0)) || ρ<nextfloat(zero(T))/(2*eps(T)^2) | |
m::T = max(abs(x), abs(y)) | |
k = m==0 ? m : exponent(m) | |
xk, yk = ldexp(x,-k), ldexp(y,-k) | |
ρ = xk*xk + yk*yk | |
end | |
ρ, k | |
end | |
function sqrt{T<:AbstractFloat}(z::Complex{T}) | |
x, y = reim(z) | |
if x==y==0 | |
return Complex(zero(x),y) | |
end | |
ρ, k::Int = ssqs(x, y) | |
if isfinite(x) ρ=ldexp(abs(x),-k)+sqrt(ρ) end | |
if isodd(k) | |
k = div(k-1,2) | |
else | |
k = div(k,2)-1 | |
ρ += ρ | |
end | |
ρ = ldexp(sqrt(ρ),k) #sqrt((abs(z)+abs(x))/2) without over/underflow | |
ξ = ρ | |
η = y | |
if ρ != 0 | |
if isfinite(η) η=(η/ρ)/2 end | |
if x<0 | |
ξ = abs(η) | |
η = copysign(ρ,y) | |
end | |
end | |
Complex(ξ,η) | |
end | |
sqrt(z::Complex) = sqrt(float(z)) | |
# function sqrt(z::Complex) | |
# rz = float(real(z)) | |
# iz = float(imag(z)) | |
# r = sqrt((hypot(rz,iz)+abs(rz))/2) | |
# if r == 0 | |
# return Complex(zero(iz), iz) | |
# end | |
# if rz >= 0 | |
# return Complex(r, iz/r/2) | |
# end | |
# return Complex(abs(iz)/r/2, copysign(r,iz)) | |
# end | |
# compute exp(im*theta) | |
cis(theta::Real) = Complex(cos(theta),sin(theta)) | |
""" | |
cis(z) | |
Return ``\\exp(iz)``. | |
""" | |
function cis(z::Complex) | |
v = exp(-imag(z)) | |
Complex(v*cos(real(z)), v*sin(real(z))) | |
end | |
""" | |
angle(z) | |
Compute the phase angle in radians of a complex number `z`. | |
""" | |
angle(z::Complex) = atan2(imag(z), real(z)) | |
function log{T<:AbstractFloat}(z::Complex{T}) | |
const T1::T = 1.25 | |
const T2::T = 3 | |
const ln2::T = log(convert(T,2)) #0.6931471805599453 | |
x, y = reim(z) | |
ρ, k = ssqs(x,y) | |
ax = abs(x) | |
ay = abs(y) | |
if ax < ay | |
θ, β = ax, ay | |
else | |
θ, β = ay, ax | |
end | |
if k==0 && (0.5 < β*β) && (β <= T1 || ρ < T2) | |
ρρ = log1p((β-1)*(β+1)+θ*θ)/2 | |
else | |
ρρ = log(ρ)/2 + k*ln2 | |
end | |
Complex(ρρ, angle(z)) | |
end | |
log(z::Complex) = log(float(z)) | |
# function log(z::Complex) | |
# ar = abs(real(z)) | |
# ai = abs(imag(z)) | |
# if ar < ai | |
# r = ar/ai | |
# re = log(ai) + log1p(r*r)/2 | |
# else | |
# if ar == 0 | |
# re = isnan(ai) ? ai : -inv(ar) | |
# elseif isinf(ai) | |
# re = oftype(ar,Inf) | |
# else | |
# r = ai/ar | |
# re = log(ar) + log1p(r*r)/2 | |
# end | |
# end | |
# Complex(re, angle(z)) | |
# end | |
function log10(z::Complex) | |
a = log(z) | |
a/log(oftype(real(a),10)) | |
end | |
function log2(z::Complex) | |
a = log(z) | |
a/log(oftype(real(a),2)) | |
end | |
function exp(z::Complex) | |
zr, zi = reim(z) | |
if isnan(zr) | |
Complex(zr, zi==0 ? zi : zr) | |
elseif !isfinite(zi) | |
if zr == Inf | |
Complex(-zr, oftype(zr,NaN)) | |
elseif zr == -Inf | |
Complex(-zero(zr), copysign(zero(zi), zi)) | |
else | |
Complex(oftype(zr,NaN), oftype(zi,NaN)) | |
end | |
else | |
er = exp(zr) | |
if zi == zero(zi) | |
Complex(er, zi) | |
else | |
Complex(er*cos(zi), er*sin(zi)) | |
end | |
end | |
end | |
function expm1(z::Complex) | |
zr,zi = reim(z) | |
if isnan(zr) | |
Complex(zr, zi==0 ? zi : zr) | |
elseif !isfinite(zi) | |
if zr == Inf | |
Complex(-zr, oftype(zr,NaN)) | |
elseif zr == -Inf | |
Complex(-one(zr), copysign(zero(zi), zi)) | |
else | |
Complex(oftype(zr,NaN), oftype(zi,NaN)) | |
end | |
else | |
erm1 = expm1(zr) | |
if zi == 0 | |
Complex(erm1, zi) | |
else | |
er = erm1+one(erm1) | |
wr = isfinite(er) ? erm1 - 2.0*er*(sin(0.5*zi))^2 : er*cos(zi) | |
Complex(wr, er*sin(zi)) | |
end | |
end | |
end | |
function log1p{T}(z::Complex{T}) | |
zr,zi = reim(z) | |
if isfinite(zr) | |
isinf(zi) && return log(z) | |
# This is based on a well-known trick for log1p of real z, | |
# allegedly due to Kahan, only modified to handle real(u) <= 0 | |
# differently to avoid inaccuracy near z==-2 and for correct branch cut | |
u = float(one(T)) + z | |
u == 1 ? convert(typeof(u), z) : real(u) <= 0 ? log(u) : log(u)*z/(u-1) | |
elseif isnan(zr) | |
Complex(zr, zr) | |
elseif isfinite(zi) | |
Complex(T(Inf), copysign(zr > 0 ? zero(T) : convert(T, pi), zi)) | |
else | |
Complex(T(Inf), T(NaN)) | |
end | |
end | |
function ^{T<:AbstractFloat}(z::Complex{T}, p::Complex{T})::Complex{T} | |
if p == 2 #square | |
zr, zi = reim(z) | |
x = (zr-zi)*(zr+zi) | |
y = 2*zr*zi | |
if isnan(x) | |
if isinf(y) | |
x = copysign(zero(T),zr) | |
elseif isinf(zi) | |
x = convert(T,-Inf) | |
elseif isinf(zr) | |
x = convert(T,Inf) | |
end | |
elseif isnan(y) && isinf(x) | |
y = copysign(zero(T), y) | |
end | |
Complex(x,y) | |
elseif z!=0 | |
if p!=0 && isinteger(p) | |
rp = real(p) | |
if rp < 0 | |
return power_by_squaring(inv(z), convert(Integer, -rp)) | |
else | |
return power_by_squaring(z, convert(Integer, rp)) | |
end | |
end | |
exp(p*log(z)) | |
elseif p!=0 #0^p | |
zero(z) #CHECK SIGNS | |
else #0^0 | |
zer = copysign(zero(T),real(p))*copysign(zero(T),imag(z)) | |
Complex(one(T), zer) | |
end | |
end | |
function exp2{T}(z::Complex{T}) | |
er = exp2(real(z)) | |
theta = imag(z) * log(convert(T, 2)) | |
Complex(er*cos(theta), er*sin(theta)) | |
end | |
function exp10{T}(z::Complex{T}) | |
er = exp10(real(z)) | |
theta = imag(z) * log(convert(T, 10)) | |
Complex(er*cos(theta), er*sin(theta)) | |
end | |
function ^{T<:Complex}(z::T, p::T) | |
if isinteger(p) | |
rp = real(p) | |
if rp < 0 | |
return power_by_squaring(inv(float(z)), convert(Integer, -rp)) | |
else | |
return power_by_squaring(float(z), convert(Integer, rp)) | |
end | |
end | |
pr, pim = reim(p) | |
zr, zi = reim(z) | |
r = abs(z) | |
rp = r^pr | |
theta = atan2(zi, zr) | |
ntheta = pr*theta | |
if pim != 0 && r != 0 | |
rp = rp*exp(-pim*theta) | |
ntheta = ntheta + pim*log(r) | |
end | |
cosntheta = cos(ntheta) | |
sinntheta = sin(ntheta) | |
re, im = rp*cosntheta, rp*sinntheta | |
if isinf(rp) | |
if isnan(re) | |
re = copysign(zero(re), cosntheta) | |
end | |
if isnan(im) | |
im = copysign(zero(im), sinntheta) | |
end | |
end | |
# apply some corrections to force known zeros | |
if pim == 0 | |
if isinteger(pr) | |
if zi == 0 | |
im = copysign(zero(im), im) | |
elseif zr == 0 | |
if isinteger(0.5*pr) # pr is even | |
im = copysign(zero(im), im) | |
else | |
re = copysign(zero(re), re) | |
end | |
end | |
else | |
dr = pr*2 | |
if isinteger(dr) && zi == 0 | |
if zr < 0 | |
re = copysign(zero(re), re) | |
else | |
im = copysign(zero(im), im) | |
end | |
end | |
end | |
end | |
Complex(re, im) | |
end | |
^(z::Complex, n::Bool) = n ? z : one(z) | |
^(z::Complex, n::Integer) = z^Complex(n) | |
^{T<:AbstractFloat}(z::Complex{T}, n::Bool) = n ? z : one(z) # to resolve ambiguity | |
^{T<:Integer}(z::Complex{T}, n::Bool) = n ? z : one(z) # to resolve ambiguity | |
^{T<:AbstractFloat}(z::Complex{T}, n::Integer) = | |
n>=0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n) | |
^{T<:Integer}(z::Complex{T}, n::Integer) = power_by_squaring(z,n) # DomainError for n<0 | |
function sin{T}(z::Complex{T}) | |
F = float(T) | |
zr, zi = reim(z) | |
if zr == 0 | |
Complex(F(zr), sinh(zi)) | |
elseif !isfinite(zr) | |
if zi == 0 || isinf(zi) | |
Complex(F(NaN), F(zi)) | |
else | |
Complex(F(NaN), F(NaN)) | |
end | |
else | |
Complex(sin(zr)*cosh(zi), cos(zr)*sinh(zi)) | |
end | |
end | |
function cos{T}(z::Complex{T}) | |
F = float(T) | |
zr, zi = reim(z) | |
if zr == 0 | |
Complex(cosh(zi), isnan(zi) ? F(zr) : -flipsign(F(zr),zi)) | |
elseif !isfinite(zr) | |
if zi == 0 | |
Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) | |
elseif isinf(zi) | |
Complex(F(Inf), F(NaN)) | |
else | |
Complex(F(NaN), F(NaN)) | |
end | |
else | |
Complex(cos(zr)*cosh(zi), -sin(zr)*sinh(zi)) | |
end | |
end | |
function tan(z::Complex) | |
zr, zi = reim(z) | |
w = tanh(Complex(-zi, zr)) | |
Complex(imag(w), -real(w)) | |
end | |
function asin(z::Complex) | |
zr, zi = reim(z) | |
if isinf(zr) && isinf(zi) | |
return Complex(copysign(oftype(zr,pi)/4, zr),zi) | |
elseif isnan(zi) && isinf(zr) | |
return Complex(zi, oftype(zr, Inf)) | |
end | |
ξ = zr == 0 ? zr : | |
!isfinite(zr) ? oftype(zr,pi)/2 * sign(zr) : | |
atan2(zr, real(sqrt(1-z)*sqrt(1+z))) | |
η = asinh(copysign(imag(sqrt(conj(1-z))*sqrt(1+z)), imag(z))) | |
Complex(ξ,η) | |
end | |
function acos{T<:AbstractFloat}(z::Complex{T}) | |
zr, zi = reim(z) | |
if isnan(zr) | |
if isinf(zi) return Complex(zr, -zi) | |
else return Complex(zr, zr) end | |
elseif isnan(zi) | |
if isinf(zr) return Complex(zi, abs(zr)) | |
elseif zr==0 return Complex(oftype(zr,pi)/2, zi) | |
else return Complex(zi, zi) end | |
elseif zr==zi==0 | |
return Complex(oftype(zr,pi)/2, -zi) | |
elseif zr==Inf && zi===0.0 | |
return Complex(zi, -zr) | |
elseif zr==-Inf && zi===-0.0 | |
return Complex(oftype(zi,pi), -zr) | |
end | |
ξ = 2*atan2(real(sqrt(1-z)), real(sqrt(1+z))) | |
η = asinh(imag(sqrt(conj(1+z))*sqrt(1-z))) | |
if isinf(zr) && isinf(zi) ξ -= oftype(η,pi)/4 * sign(zr) end | |
Complex(ξ,η) | |
end | |
acos(z::Complex) = acos(float(z)) | |
function atan(z::Complex) | |
w = atanh(Complex(-imag(z),real(z))) | |
Complex(imag(w),-real(w)) | |
end | |
function sinh(z::Complex) | |
zr, zi = reim(z) | |
w = sin(Complex(zi, zr)) | |
Complex(imag(w),real(w)) | |
end | |
function cosh(z::Complex) | |
zr, zi = reim(z) | |
cos(Complex(zi,-zr)) | |
end | |
function tanh{T<:AbstractFloat}(z::Complex{T}) | |
const Ω = prevfloat(typemax(T)) | |
ξ, η = reim(z) | |
if isnan(ξ) && η==0 return Complex(ξ, η) end | |
if 4*abs(ξ) > asinh(Ω) #Overflow? | |
Complex(copysign(one(T),ξ), | |
copysign(zero(T),η*(isfinite(η) ? sin(2*abs(η)) : one(η)))) | |
else | |
t = tan(η) | |
β = 1+t*t #sec(η)^2 | |
s = sinh(ξ) | |
ρ = sqrt(1 + s*s) #cosh(ξ) | |
if isinf(t) | |
Complex(ρ/s,1/t) | |
else | |
Complex(β*ρ*s,t)/(1+β*s*s) | |
end | |
end | |
end | |
tanh(z::Complex) = tanh(float(z)) | |
function asinh(z::Complex) | |
w = asin(Complex(-imag(z),real(z))) | |
Complex(imag(w),-real(w)) | |
end | |
function acosh(z::Complex) | |
zr, zi = reim(z) | |
if isnan(zr) || isnan(zi) | |
if isinf(zr) || isinf(zi) | |
return Complex(oftype(zr, Inf), oftype(zi, NaN)) | |
else | |
return Complex(oftype(zr, NaN), oftype(zi, NaN)) | |
end | |
elseif zr==-Inf && zi===-0.0 #Edge case is wrong - WHY? | |
return Complex(oftype(zr,Inf), oftype(zi, -pi)) | |
end | |
ξ = asinh(real(sqrt(conj(z-1))*sqrt(z+1))) | |
η = 2atan2(imag(sqrt(z-1)),real(sqrt(z+1))) | |
if isinf(zr) && isinf(zi) | |
η -= oftype(η,pi)/4 * sign(zi) * sign(zr) | |
end | |
Complex(ξ, η) | |
end | |
function atanh{T<:AbstractFloat}(z::Complex{T}) | |
const Ω = prevfloat(typemax(T)) | |
const θ = sqrt(Ω)/4 | |
const ρ = 1/θ | |
x, y = reim(z) | |
ax = abs(x) | |
ay = abs(y) | |
if ax > θ || ay > θ #Prevent overflow | |
if isnan(y) | |
if isinf(x) | |
return Complex(copysign(zero(x),x), y) | |
else | |
return Complex(real(1/z), y) | |
end | |
end | |
if isinf(y) | |
return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y)) | |
end | |
return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) | |
elseif ax==1 | |
if y == 0 | |
ξ = copysign(oftype(x,Inf),x) | |
η = zero(y) | |
else | |
ym = ay+ρ | |
ξ = log(sqrt(sqrt(4+y*y))/sqrt(ym)) | |
η = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2 | |
end | |
else #Normal case | |
ysq = (ay+ρ)^2 | |
if x == 0 | |
ξ = x | |
else | |
ξ = log1p(4x/((1-x)^2 + ysq))/4 | |
end | |
η = angle(Complex((1-x)*(1+x)-ysq, 2y))/2 | |
end | |
Complex(ξ, η) | |
end | |
atanh(z::Complex) = atanh(float(z)) | |
function lexcmp(a::Complex, b::Complex) | |
c = cmp(real(a), real(b)) | |
c == 0 || return c | |
cmp(imag(a), imag(b)) | |
end | |
#Rounding complex numbers | |
#Requires two different RoundingModes for the real and imaginary components | |
""" | |
round(z, RoundingModeReal, RoundingModeImaginary) | |
Returns the nearest integral value of the same type as the complex-valued `z` to `z`, | |
breaking ties using the specified [`RoundingMode`](:obj:`RoundingMode`)s. The first | |
[`RoundingMode`](:obj:`RoundingMode`) is used for rounding the real components while the | |
second is used for rounding the imaginary components. | |
""" | |
function round{T<:AbstractFloat, MR, MI}(z::Complex{T}, ::RoundingMode{MR}, ::RoundingMode{MI}) | |
Complex(round(real(z), RoundingMode{MR}()), | |
round(imag(z), RoundingMode{MI}())) | |
end | |
round(z::Complex) = Complex(round(real(z)), round(imag(z))) | |
function round(z::Complex, digits::Integer, base::Integer=10) | |
Complex(round(real(z), digits, base), | |
round(imag(z), digits, base)) | |
end | |
float{T<:AbstractFloat}(z::Complex{T}) = z | |
float(z::Complex) = Complex(float(real(z)), float(imag(z))) | |
big{T<:AbstractFloat}(z::Complex{T}) = Complex{BigFloat}(z) | |
big{T<:Integer}(z::Complex{T}) = Complex{BigInt}(z) | |
## Array operations on complex numbers ## | |
complex{T<:Complex}(A::AbstractArray{T}) = A | |
function complex{T}(A::AbstractArray{T}) | |
if !isleaftype(T) | |
error("`complex` not defined on abstractly-typed arrays; please convert to a more specific type") | |
end | |
convert(AbstractArray{typeof(complex(zero(T)))}, A) | |
end | |
big{T<:Integer,N}(A::AbstractArray{Complex{T},N}) = convert(AbstractArray{Complex{BigInt},N}, A) | |
big{T<:AbstractFloat,N}(A::AbstractArray{Complex{T},N}) = convert(AbstractArray{Complex{BigFloat},N}, A) | |
## promotion to complex ## | |
_default_type(T::Type{Complex}) = Complex{Int} | |
promote_array_type{S<:Union{Complex, Real}, T<:AbstractFloat}(F, ::Type{S}, ::Type{Complex{T}}, ::Type) = Complex{T} | |
function complex{S<:Real,T<:Real}(A::AbstractArray{S}, B::AbstractArray{T}) | |
if size(A) != size(B); throw(DimensionMismatch()); end | |
F = similar(A, typeof(complex(zero(S),zero(T)))) | |
RF, RA, RB = eachindex(F), eachindex(A), eachindex(B) | |
if RF == RA == RB | |
for i in RA | |
@inbounds F[i] = complex(A[i], B[i]) | |
end | |
else | |
for (iF, iA, iB) in zip(RF, RA, RB) | |
@inbounds F[iF] = complex(A[iA], B[iB]) | |
end | |
end | |
return F | |
end | |
function complex{T<:Real}(A::Real, B::AbstractArray{T}) | |
F = similar(B, typeof(complex(A,zero(T)))) | |
RF, RB = eachindex(F), eachindex(B) | |
if RF == RB | |
for i in RB | |
@inbounds F[i] = complex(A, B[i]) | |
end | |
else | |
for (iF, iB) in zip(RF, RB) | |
@inbounds F[iF] = complex(A, B[iB]) | |
end | |
end | |
return F | |
end | |
function complex{T<:Real}(A::AbstractArray{T}, B::Real) | |
F = similar(A, typeof(complex(zero(T),B))) | |
RF, RA = eachindex(F), eachindex(A) | |
if RF == RA | |
for i in RA | |
@inbounds F[i] = complex(A[i], B) | |
end | |
else | |
for (iF, iA) in zip(RF, RA) | |
@inbounds F[iF] = complex(A[iA], B) | |
end | |
end | |
return F | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
Main.Core.eval(Main.Core, :(baremodule Inference | |
using Core.Intrinsics | |
import Core: print, println, show, write, unsafe_write, STDOUT, STDERR | |
ccall(:jl_set_istopmod, Void, (Bool,), false) | |
eval(x) = Core.eval(Inference,x) | |
eval(m,x) = Core.eval(m,x) | |
include = Core.include | |
## Load essential files and libraries | |
include("ctypes.jl") | |
include("essentials.jl") | |
include("generator.jl") | |
include("reflection.jl") | |
include("options.jl") | |
# core operations & types | |
include("promotion.jl") | |
include("tuple.jl") | |
include("range.jl") | |
include("expr.jl") | |
include("error.jl") | |
# core numeric operations & types | |
include("bool.jl") | |
include("number.jl") | |
include("int.jl") | |
include("operators.jl") | |
include("pointer.jl") | |
const checked_add = + | |
const checked_sub = - | |
if !isdefined(Main, :Base) | |
# conditional to allow redefining Core.Inference after base exists | |
(::Type{T}){T}(arg) = convert(T, arg)::T | |
end | |
# Symbol constructors | |
Symbol(s::String) = Symbol(s.data) | |
Symbol(a::Array{UInt8,1}) = | |
ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int32), a, length(a)) | |
# core array operations | |
include("array.jl") | |
include("abstractarray.jl") | |
include("hashing.jl") | |
include("nofloat_hashing.jl") | |
# map-reduce operators | |
macro simd(forloop) | |
esc(forloop) | |
end | |
include("reduce.jl") | |
## core structures | |
include("intset.jl") | |
include("associative.jl") | |
# core docsystem | |
include("docs/core.jl") | |
# compiler | |
include("inference.jl") | |
ccall(:jl_set_typeinf_func, Void, (Any,), typeinf_ext) | |
end # baremodule Inference | |
)) | |
# This file is a part of Julia. License is MIT: http://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') | |
immutable DevNullStream <: IO end | |
const DevNull = DevNullStream() | |
isreadable(::DevNullStream) = false | |
iswritable(::DevNullStream) = true | |
isopen(::DevNullStream) = true | |
read(::DevNullStream, ::Type{UInt8}) = throw(EOFError()) | |
write(::DevNullStream, ::UInt8) = 1 | |
close(::DevNullStream) = nothing | |
flush(::DevNullStream) = nothing | |
wait_connected(::DevNullStream) = nothing | |
wait_readnb(::DevNullStream) = wait() | |
wait_readbyte(::DevNullStream) = wait() | |
wait_close(::DevNullStream) = wait() | |
eof(::DevNullStream) = true | |
let CoreIO = Union{Core.CoreSTDOUT, Core.CoreSTDERR} | |
global write, unsafe_write | |
write(io::CoreIO, x::UInt8) = Core.write(io, x) | |
unsafe_write(io::CoreIO, x::Ptr{UInt8}, nb::UInt) = Core.unsafe_write(io, x, nb) | |
end | |
STDIN = DevNull | |
STDOUT = Core.STDOUT | |
STDERR = Core.STDERR | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# essential type definitions for interacting with C code | |
# (platform- or OS-dependent types are defined in c.jl) | |
""" | |
Cuchar | |
Equivalent to the native `unsigned char` c-type (`UInt8`). | |
""" | |
typealias Cuchar UInt8 | |
""" | |
Cshort | |
Equivalent to the native `signed short` c-type (`Int16`). | |
""" | |
typealias Cshort Int16 | |
""" | |
Cushort | |
Equivalent to the native `unsigned short` c-type (`UInt16`). | |
""" | |
typealias Cushort UInt16 | |
""" | |
Cint | |
Equivalent to the native `signed int` c-type (`Int32`). | |
""" | |
typealias Cint Int32 | |
""" | |
Cuint | |
Equivalent to the native `unsigned int` c-type (`UInt32`). | |
""" | |
typealias Cuint UInt32 | |
""" | |
Cptrdiff_t | |
Equivalent to the native `ptrdiff_t` c-type (`Int`). | |
""" | |
typealias Cptrdiff_t Int | |
""" | |
Csize_t | |
Equivalent to the native `size_t` c-type (`UInt`). | |
""" | |
typealias Csize_t UInt | |
""" | |
Cssize_t | |
Equivalent to the native `ssize_t` c-type. | |
""" | |
typealias Cssize_t Int | |
""" | |
Cintmax_t | |
Equivalent to the native `intmax_t` c-type (`Int64`). | |
""" | |
typealias Cintmax_t Int64 | |
""" | |
Cuintmax_t | |
Equivalent to the native `uintmax_t` c-type (`UInt64`). | |
""" | |
typealias Cuintmax_t UInt64 | |
""" | |
Clonglong | |
Equivalent to the native `signed long long` c-type (`Int64`). | |
""" | |
typealias Clonglong Int64 | |
""" | |
Culonglong | |
Equivalent to the native `unsigned long long` c-type (`UInt64`). | |
""" | |
typealias Culonglong UInt64 | |
""" | |
Cfloat | |
Equivalent to the native `float` c-type (`Float32`). | |
""" | |
typealias Cfloat Float32 | |
""" | |
Cdouble | |
Equivalent to the native `double` c-type (`Float64`). | |
""" | |
typealias Cdouble Float64 | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module dSFMT | |
import Base: copy, copy!, == | |
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" | |
type DSFMT_state | |
val::Vector{Int32} | |
DSFMT_state(val::Vector{Int32} = zeros(Int32, JN32)) = | |
new(length(val) == JN32 ? val : throw(DomainError())) | |
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 | |
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) | |
index = s.val[end-1] | |
work = zeros(UInt64, JN32>>1) | |
dsfmt = reinterpret(UInt64, copy(s.val)) | |
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] = (t0 << SL1) $ (L1 >> 32) $ (L1 << 32) $ mts[b] | |
mts[u+1] = (t1 << SL1) $ (L0 >> 32) $ (L0 << 32) $ mts[b+1] | |
mts[a] = (mts[u] >> SR) $ (mts[u] & MSK1) $ t0 | |
mts[a+1] = (mts[u+1] >> SR) $ (mts[u+1] & MSK2) $ t1 | |
mts[end] = (mts[end] + 2) % (N*2) | |
return mts | |
end | |
## Windows entropy | |
if is_windows() | |
function win32_SystemFunction036!{T}(a::Array{T}) | |
ccall((:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Void}, UInt32), a, sizeof(a)) | |
end | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## file formats ## | |
module DataFmt | |
import Base: _default_delims, tryparse_internal, show | |
export countlines, readdlm, readcsv, writedlm, writecsv | |
invalid_dlm(::Type{Char}) = reinterpret(Char, 0xfffffffe) | |
invalid_dlm(::Type{UInt8}) = 0xfe | |
invalid_dlm(::Type{UInt16}) = 0xfffe | |
invalid_dlm(::Type{UInt32}) = 0xfffffffe | |
const offs_chunk_size = 5000 | |
countlines(f::AbstractString, eol::Char='\n') = open(io->countlines(io,eol), f)::Int | |
function countlines(io::IO, eol::Char='\n') | |
isascii(eol) || throw(ArgumentError("only ASCII line terminators are supported")) | |
aeol = UInt8(eol) | |
a = Array{UInt8}(8192) | |
nl = 0 | |
while !eof(io) | |
nb = readbytes!(io, a) | |
@simd for i=1:nb | |
@inbounds nl += a[i] == aeol | |
end | |
end | |
nl | |
end | |
""" | |
readdlm(source, T::Type; options...) | |
The columns are assumed to be separated by one or more whitespaces. The end of line | |
delimiter is taken as `\\n`. | |
""" | |
readdlm(input, T::Type; opts...) = readdlm(input, invalid_dlm(Char), T, '\n'; opts...) | |
""" | |
readdlm(source, delim::Char, T::Type; options...) | |
The end of line delimiter is taken as `\\n`. | |
""" | |
readdlm(input, dlm::Char, T::Type; opts...) = readdlm(input, dlm, T, '\n'; opts...) | |
""" | |
readdlm(source; options...) | |
The columns are assumed to be separated by one or more whitespaces. The end of line | |
delimiter is taken as `\\n`. If all data is numeric, the result will be a numeric array. If | |
some elements cannot be parsed as numbers, a heterogeneous array of numbers and strings | |
is returned. | |
""" | |
readdlm(input; opts...) = readdlm(input, invalid_dlm(Char), '\n'; opts...) | |
""" | |
readdlm(source, delim::Char; options...) | |
The end of line delimiter is taken as `\\n`. If all data is numeric, the result will be a | |
numeric array. If some elements cannot be parsed as numbers, a heterogeneous array of | |
numbers and strings is returned. | |
""" | |
readdlm(input, dlm::Char; opts...) = readdlm(input, dlm, '\n'; opts...) | |
""" | |
readdlm(source, delim::Char, eol::Char; options...) | |
If all data is numeric, the result will be a numeric array. If some elements cannot be | |
parsed as numbers, a heterogeneous array of numbers and strings is returned. | |
""" | |
readdlm(input, dlm::Char, eol::Char; opts...) = | |
readdlm_auto(input, dlm, Float64, eol, true; opts...) | |
""" | |
readdlm(source, delim::Char, T::Type, eol::Char; header=false, skipstart=0, skipblanks=true, use_mmap, quotes=true, dims, comments=true, comment_char='#') | |
Read a matrix from the source where each line (separated by `eol`) gives one row, with | |
elements separated by the given delimiter. The source can be a text file, stream or byte | |
array. Memory mapped files can be used by passing the byte array representation of the | |
mapped segment as source. | |
If `T` is a numeric type, the result is an array of that type, with any non-numeric elements | |
as `NaN` for floating-point types, or zero. Other useful values of `T` include | |
`String`, `AbstractString`, and `Any`. | |
If `header` is `true`, the first row of data will be read as header and the tuple | |
`(data_cells, header_cells)` is returned instead of only `data_cells`. | |
Specifying `skipstart` will ignore the corresponding number of initial lines from the input. | |
If `skipblanks` is `true`, blank lines in the input will be ignored. | |
If `use_mmap` is `true`, the file specified by `source` is memory mapped for potential | |
speedups. Default is `true` except on Windows. On Windows, you may want to specify `true` if | |
the file is large, and is only read once and not written to. | |
If `quotes` is `true`, columns enclosed within double-quote (\") characters are allowed to | |
contain new lines and column delimiters. Double-quote characters within a quoted field must | |
be escaped with another double-quote. Specifying `dims` as a tuple of the expected rows and | |
columns (including header, if any) may speed up reading of large files. If `comments` is | |
`true`, lines beginning with `comment_char` and text following `comment_char` in any line | |
are ignored. | |
""" | |
readdlm(input, dlm::Char, T::Type, eol::Char; opts...) = | |
readdlm_auto(input, dlm, T, eol, false; opts...) | |
readdlm_auto(input::Vector{UInt8}, dlm::Char, T::Type, eol::Char, auto::Bool; opts...) = | |
readdlm_string(String(input), dlm, T, eol, auto, val_opts(opts)) | |
readdlm_auto(input::IO, dlm::Char, T::Type, eol::Char, auto::Bool; opts...) = | |
readdlm_string(readstring(input), dlm, T, eol, auto, val_opts(opts)) | |
function readdlm_auto(input::AbstractString, dlm::Char, T::Type, eol::Char, auto::Bool; opts...) | |
optsd = val_opts(opts) | |
use_mmap = get(optsd, :use_mmap, is_windows() ? false : true) | |
fsz = filesize(input) | |
if use_mmap && fsz > 0 && fsz < typemax(Int) | |
a = Mmap.mmap(input, Vector{UInt8}, (Int(fsz),)) | |
# TODO: It would be nicer to use String(a) without making a copy, | |
# but because the mmap'ed array is not NUL-terminated this causes | |
# jl_try_substrtod to segfault below. | |
return readdlm_string(String(copy(a)), dlm, T, eol, auto, optsd) | |
else | |
return readdlm_string(readstring(input), dlm, T, eol, auto, optsd) | |
end | |
end | |
# | |
# Handlers act on events generated by the parser. | |
# Parser calls store_cell on the handler to pass events. | |
# | |
# DLMOffsets: Keep offsets (when result dimensions are not known) | |
# DLMStore: Store values directly into a result store (when result dimensions are known) | |
abstract DLMHandler | |
type DLMOffsets <: DLMHandler | |
oarr::Vector{Vector{Int}} | |
offidx::Int | |
thresh::Int | |
bufflen::Int | |
function DLMOffsets(sbuff::String) | |
offsets = Array{Array{Int,1}}(1) | |
offsets[1] = Array{Int}(offs_chunk_size) | |
thresh = ceil(min(typemax(UInt), Base.Sys.total_memory()) / sizeof(Int) / 5) | |
new(offsets, 1, thresh, length(sbuff.data)) | |
end | |
end | |
function store_cell(dlmoffsets::DLMOffsets, row::Int, col::Int, | |
quoted::Bool, startpos::Int, endpos::Int) | |
offidx = dlmoffsets.offidx | |
(offidx == 0) && return # offset collection stopped to avoid choking on memory | |
oarr = dlmoffsets.oarr | |
offsets = oarr[end] | |
if length(offsets) < offidx | |
offlen = offs_chunk_size * length(oarr) | |
if (offlen + offs_chunk_size) > dlmoffsets.thresh | |
est_tot = round(Int, offlen * dlmoffsets.bufflen / endpos) | |
if (est_tot - offlen) > offs_chunk_size # allow another chunk | |
# abandon offset collection | |
dlmoffsets.oarr = Vector{Int}[] | |
dlmoffsets.offidx = 0 | |
return | |
end | |
end | |
offsets = Array{Int}(offs_chunk_size) | |
push!(oarr, offsets) | |
offidx = 1 | |
end | |
offsets[offidx] = row | |
offsets[offidx+1] = col | |
offsets[offidx+2] = Int(quoted) | |
offsets[offidx+3] = startpos | |
offsets[offidx+4] = endpos | |
dlmoffsets.offidx = offidx + 5 | |
nothing | |
end | |
function result(dlmoffsets::DLMOffsets) | |
trimsz = (dlmoffsets.offidx-1) % offs_chunk_size | |
((trimsz > 0) || (dlmoffsets.offidx == 1)) && resize!(dlmoffsets.oarr[end], trimsz) | |
dlmoffsets.oarr | |
end | |
type DLMStore{T} <: DLMHandler | |
hdr::Array{AbstractString, 2} | |
data::Array{T, 2} | |
nrows::Int | |
ncols::Int | |
lastrow::Int | |
lastcol::Int | |
hdr_offset::Int | |
sbuff::String | |
auto::Bool | |
eol::Char | |
end | |
function DLMStore{T}(::Type{T}, dims::NTuple{2,Integer}, | |
has_header::Bool, sbuff::String, auto::Bool, eol::Char) | |
(nrows,ncols) = dims | |
nrows <= 0 && throw(ArgumentError("number of rows in dims must be > 0, got $nrows")) | |
ncols <= 0 && throw(ArgumentError("number of columns in dims must be > 0, got $ncols")) | |
hdr_offset = has_header ? 1 : 0 | |
DLMStore{T}(fill(SubString(sbuff,1,0), 1, ncols), Array{T}(nrows-hdr_offset, ncols), | |
nrows, ncols, 0, 0, hdr_offset, sbuff, auto, eol) | |
end | |
_chrinstr(sbuff::String, chr::UInt8, startpos::Int, endpos::Int) = | |
(endpos >= startpos) && (C_NULL != ccall(:memchr, Ptr{UInt8}, | |
(Ptr{UInt8}, Int32, Csize_t), pointer(sbuff.data)+startpos-1, chr, endpos-startpos+1)) | |
function store_cell{T}(dlmstore::DLMStore{T}, row::Int, col::Int, | |
quoted::Bool, startpos::Int, endpos::Int) | |
drow = row - dlmstore.hdr_offset | |
ncols = dlmstore.ncols | |
lastcol = dlmstore.lastcol | |
lastrow = dlmstore.lastrow | |
cells::Array{T,2} = dlmstore.data | |
sbuff = dlmstore.sbuff | |
endpos = prevind(sbuff, nextind(sbuff,endpos)) | |
if (endpos > 0) && ('\n' == dlmstore.eol) && ('\r' == Char(sbuff[endpos])) | |
endpos = prevind(sbuff, endpos) | |
end | |
if quoted | |
startpos += 1 | |
endpos -= 1 | |
end | |
if drow > 0 | |
# fill missing elements | |
while ((drow - lastrow) > 1) || ((drow > lastrow > 0) && (lastcol < ncols)) | |
if (lastcol == ncols) || (lastrow == 0) | |
lastcol = 0 | |
lastrow += 1 | |
end | |
for cidx in (lastcol+1):ncols | |
if (T <: AbstractString) || (T == Any) | |
cells[lastrow, cidx] = SubString(sbuff, 1, 0) | |
elseif ((T <: Number) || (T <: Char)) && dlmstore.auto | |
throw(TypeError(:store_cell, "", Any, T)) | |
else | |
error("missing value at row $lastrow column $cidx") | |
end | |
end | |
lastcol = ncols | |
end | |
# fill data | |
if quoted && _chrinstr(sbuff, UInt8('"'), startpos, endpos) | |
unescaped = replace(SubString(sbuff, startpos, endpos), r"\"\"", "\"") | |
fail = colval(unescaped, 1, length(unescaped), cells, drow, col) | |
else | |
fail = colval(sbuff, startpos, endpos, cells, drow, col) | |
end | |
if fail | |
sval = SubString(sbuff, startpos, endpos) | |
if (T <: Number) && dlmstore.auto | |
throw(TypeError(:store_cell, "", Any, T)) | |
else | |
error("file entry \"$(sval)\" cannot be converted to $T") | |
end | |
end | |
dlmstore.lastrow = drow | |
dlmstore.lastcol = col | |
else | |
# fill header | |
if quoted && _chrinstr(sbuff, UInt8('"'), startpos, endpos) | |
unescaped = replace(SubString(sbuff, startpos, endpos), r"\"\"", "\"") | |
colval(unescaped, 1, length(unescaped), dlmstore.hdr, 1, col) | |
else | |
colval(sbuff, startpos, endpos, dlmstore.hdr, 1, col) | |
end | |
end | |
nothing | |
end | |
function result{T}(dlmstore::DLMStore{T}) | |
nrows = dlmstore.nrows - dlmstore.hdr_offset | |
ncols = dlmstore.ncols | |
lastcol = dlmstore.lastcol | |
lastrow = dlmstore.lastrow | |
cells = dlmstore.data | |
sbuff = dlmstore.sbuff | |
if (nrows > 0) && ((lastcol < ncols) || (lastrow < nrows)) | |
while lastrow <= nrows | |
(lastcol == ncols) && (lastcol = 0; lastrow += 1) | |
for cidx in (lastcol+1):ncols | |
if (T <: AbstractString) || (T == Any) | |
cells[lastrow, cidx] = SubString(sbuff, 1, 0) | |
elseif ((T <: Number) || (T <: Char)) && dlmstore.auto | |
throw(TypeError(:store_cell, "", Any, T)) | |
else | |
error("missing value at row $lastrow column $cidx") | |
end | |
end | |
lastcol = ncols | |
(lastrow == nrows) && break | |
end | |
dlmstore.lastrow = lastrow | |
dlmstore.lastcol = ncols | |
end | |
(dlmstore.hdr_offset > 0) ? (dlmstore.data, dlmstore.hdr) : dlmstore.data | |
end | |
function readdlm_string(sbuff::String, dlm::Char, T::Type, eol::Char, auto::Bool, optsd::Dict) | |
ign_empty = (dlm == invalid_dlm(Char)) | |
quotes = get(optsd, :quotes, true) | |
comments = get(optsd, :comments, true) | |
comment_char = get(optsd, :comment_char, '#') | |
dims = get(optsd, :dims, nothing) | |
has_header = get(optsd, :header, get(optsd, :has_header, false)) | |
haskey(optsd, :has_header) && (optsd[:has_header] != has_header) && throw(ArgumentError("conflicting values for header and has_header")) | |
skipstart = get(optsd, :skipstart, 0) | |
(skipstart >= 0) || throw(ArgumentError("skipstart must be ≥ 0, got $skipstart")) | |
skipblanks = get(optsd, :skipblanks, true) | |
offset_handler = (dims === nothing) ? DLMOffsets(sbuff) : DLMStore(T, dims, has_header, sbuff, auto, eol) | |
for retry in 1:2 | |
try | |
dims = dlm_parse(sbuff, eol, dlm, '"', comment_char, ign_empty, quotes, comments, skipstart, skipblanks, offset_handler) | |
break | |
catch ex | |
if isa(ex, TypeError) && (ex.func == :store_cell) | |
T = ex.expected | |
else | |
rethrow(ex) | |
end | |
offset_handler = (dims === nothing) ? DLMOffsets(sbuff) : DLMStore(T, dims, has_header, sbuff, auto, eol) | |
end | |
end | |
isa(offset_handler, DLMStore) && (return result(offset_handler)) | |
offsets = result(offset_handler) | |
!isempty(offsets) && (return dlm_fill(T, offsets, dims, has_header, sbuff, auto, eol)) | |
optsd[:dims] = dims | |
return readdlm_string(sbuff, dlm, T, eol, auto, optsd) | |
end | |
const valid_opts = [:header, :has_header, :use_mmap, :quotes, :comments, :dims, :comment_char, :skipstart, :skipblanks] | |
const valid_opt_types = [Bool, Bool, Bool, Bool, Bool, NTuple{2,Integer}, Char, Integer, Bool] | |
const deprecated_opts = Dict(:has_header => :header) | |
function val_opts(opts) | |
d = Dict{Symbol,Union{Bool,NTuple{2,Integer},Char,Integer}}() | |
for (opt_name, opt_val) in opts | |
if opt_name == :ignore_invalid_chars | |
Base.depwarn("the ignore_invalid_chars option is no longer supported and will be ignored", :val_opts) | |
continue | |
end | |
in(opt_name, valid_opts) || | |
throw(ArgumentError("unknown option $opt_name")) | |
opt_typ = valid_opt_types[findfirst(valid_opts, opt_name)] | |
isa(opt_val, opt_typ) || | |
throw(ArgumentError("$opt_name should be of type $opt_typ, got $(typeof(opt_val))")) | |
d[opt_name] = opt_val | |
haskey(deprecated_opts, opt_name) && | |
Base.depwarn("$opt_name is deprecated, use $(deprecated_opts[opt_name]) instead", :val_opts) | |
end | |
return d | |
end | |
function dlm_fill(T::DataType, offarr::Vector{Vector{Int}}, dims::NTuple{2,Integer}, has_header::Bool, sbuff::String, auto::Bool, eol::Char) | |
idx = 1 | |
offidx = 1 | |
offsets = offarr[1] | |
row = 0 | |
col = 0 | |
try | |
dh = DLMStore(T, dims, has_header, sbuff, auto, eol) | |
while idx <= length(offsets) | |
row = offsets[idx] | |
col = offsets[idx+1] | |
quoted = offsets[idx+2] != 0 | |
startpos = offsets[idx+3] | |
endpos = offsets[idx+4] | |
((idx += 5) > offs_chunk_size) && (offidx < length(offarr)) && (idx = 1; offsets = offarr[offidx += 1]) | |
store_cell(dh, row, col, quoted, startpos, endpos) | |
end | |
return result(dh) | |
catch ex | |
isa(ex, TypeError) && (ex.func == :store_cell) && (return dlm_fill(ex.expected, offarr, dims, has_header, sbuff, auto, eol)) | |
error("at row $row, column $col : $ex") | |
end | |
end | |
function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Bool,2}, row::Int, col::Int) | |
n = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) | |
isnull(n) || (cells[row, col] = get(n)) | |
isnull(n) | |
end | |
function colval{T<:Integer}(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) | |
n = tryparse_internal(T, sbuff, startpos, endpos, 0, false) | |
isnull(n) || (cells[row, col] = get(n)) | |
isnull(n) | |
end | |
function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Float64,2}, row::Int, col::Int) | |
n = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), sbuff, startpos-1, endpos-startpos+1) | |
isnull(n) || (cells[row, col] = get(n)) | |
isnull(n) | |
end | |
function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Float32,2}, row::Int, col::Int) | |
n = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) | |
isnull(n) || (cells[row, col] = get(n)) | |
isnull(n) | |
end | |
function colval{T<:AbstractString}(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) | |
cells[row, col] = SubString(sbuff, startpos, endpos) | |
return false | |
end | |
function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Any,2}, row::Int, col::Int) | |
# if array is of Any type, attempt parsing only the most common types: Int, Bool, Float64 and fallback to SubString | |
len = endpos-startpos+1 | |
if len > 0 | |
# check Inteter | |
ni64 = tryparse_internal(Int, sbuff, startpos, endpos, 0, false) | |
isnull(ni64) || (cells[row, col] = get(ni64); return false) | |
# check Bool | |
nb = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) | |
isnull(nb) || (cells[row, col] = get(nb); return false) | |
# check float64 | |
nf64 = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) | |
isnull(nf64) || (cells[row, col] = get(nf64); return false) | |
end | |
cells[row, col] = SubString(sbuff, startpos, endpos) | |
false | |
end | |
function colval{T<:Char}(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) | |
if startpos == endpos | |
cells[row, col] = next(sbuff, startpos)[1] | |
return false | |
else | |
return true | |
end | |
end | |
colval(sbuff::String, startpos::Int, endpos::Int, cells::Array, row::Int, col::Int) = true | |
function dlm_parse{T,D}(dbuff::T, eol::D, dlm::D, qchar::D, cchar::D, | |
ign_adj_dlm::Bool, allow_quote::Bool, allow_comments::Bool, | |
skipstart::Int, skipblanks::Bool, dh::DLMHandler) | |
all_ascii = (D <: UInt8) || (isascii(eol) && | |
isascii(dlm) && | |
(!allow_quote || isascii(qchar)) && | |
(!allow_comments || isascii(cchar))) | |
if T === String && all_ascii | |
return dlm_parse(dbuff.data, eol % UInt8, dlm % UInt8, qchar % UInt8, cchar % UInt8, | |
ign_adj_dlm, allow_quote, allow_comments, skipstart, skipblanks, dh) | |
end | |
ncols = nrows = col = 0 | |
is_default_dlm = (dlm == invalid_dlm(D)) | |
error_str = "" | |
# 0: begin field, 1: quoted field, 2: unquoted field, | |
# 3: second quote (could either be end of field or escape character), | |
# 4: comment, 5: skipstart | |
state = (skipstart > 0) ? 5 : 0 | |
is_eol = is_dlm = is_cr = is_quote = is_comment = expct_col = false | |
idx = 1 | |
try | |
slen = sizeof(dbuff) | |
col_start_idx = 1 | |
was_cr = false | |
while idx <= slen | |
val,idx = next(dbuff, idx) | |
if (is_eol = (Char(val) == Char(eol))) | |
is_dlm = is_comment = is_cr = is_quote = false | |
elseif (is_dlm = (is_default_dlm ? in(Char(val), _default_delims) : (Char(val) == Char(dlm)))) | |
is_comment = is_cr = is_quote = false | |
elseif (is_quote = (Char(val) == Char(qchar))) | |
is_comment = is_cr = false | |
elseif (is_comment = (Char(val) == Char(cchar))) | |
is_cr = false | |
else | |
is_cr = (Char(eol) == '\n') && (Char(val) == '\r') | |
end | |
if 2 == state # unquoted field | |
if is_dlm | |
state = 0 | |
col += 1 | |
store_cell(dh, nrows+1, col, false, col_start_idx, idx-2) | |
col_start_idx = idx | |
!ign_adj_dlm && (expct_col = true) | |
elseif is_eol | |
nrows += 1 | |
col += 1 | |
store_cell(dh, nrows, col, false, col_start_idx, idx - (was_cr ? 3 : 2)) | |
col_start_idx = idx | |
ncols = max(ncols, col) | |
col = 0 | |
state = 0 | |
elseif (is_comment && allow_comments) | |
nrows += 1 | |
col += 1 | |
store_cell(dh, nrows, col, false, col_start_idx, idx - 2) | |
ncols = max(ncols, col) | |
col = 0 | |
state = 4 | |
end | |
elseif 1 == state # quoted field | |
is_quote && (state = 3) | |
elseif 4 == state # comment line | |
if is_eol | |
col_start_idx = idx | |
state = 0 | |
end | |
elseif 0 == state # begin field | |
if is_quote | |
state = (allow_quote && !was_cr) ? 1 : 2 | |
expct_col = false | |
elseif is_dlm | |
if !ign_adj_dlm | |
expct_col = true | |
col += 1 | |
store_cell(dh, nrows+1, col, false, col_start_idx, idx-2) | |
end | |
col_start_idx = idx | |
elseif is_eol | |
if (col > 0) || !skipblanks | |
nrows += 1 | |
if expct_col | |
col += 1 | |
store_cell(dh, nrows, col, false, col_start_idx, idx - (was_cr ? 3 : 2)) | |
end | |
ncols = max(ncols, col) | |
col = 0 | |
end | |
col_start_idx = idx | |
expct_col = false | |
elseif is_comment && allow_comments | |
if col > 0 | |
nrows += 1 | |
if expct_col | |
col += 1 | |
store_cell(dh, nrows, col, false, col_start_idx, idx - 2) | |
end | |
ncols = max(ncols, col) | |
col = 0 | |
end | |
expct_col = false | |
state = 4 | |
elseif !is_cr | |
state = 2 | |
expct_col = false | |
end | |
elseif 3 == state # second quote | |
if is_quote && !was_cr | |
state = 1 | |
elseif is_dlm && !was_cr | |
state = 0 | |
col += 1 | |
store_cell(dh, nrows+1, col, true, col_start_idx, idx-2) | |
col_start_idx = idx | |
!ign_adj_dlm && (expct_col = true) | |
elseif is_eol | |
nrows += 1 | |
col += 1 | |
store_cell(dh, nrows, col, true, col_start_idx, idx - (was_cr ? 3 : 2)) | |
col_start_idx = idx | |
ncols = max(ncols, col) | |
col = 0 | |
state = 0 | |
elseif is_comment && allow_comments && !was_cr | |
nrows += 1 | |
col += 1 | |
store_cell(dh, nrows, col, true, col_start_idx, idx - 2) | |
ncols = max(ncols, col) | |
col = 0 | |
state = 4 | |
elseif (is_cr && was_cr) || !is_cr | |
error_str = escape_string("unexpected character '$(Char(val))' after quoted field at row $(nrows+1) column $(col+1)") | |
break | |
end | |
elseif 5 == state # skip start | |
if is_eol | |
col_start_idx = idx | |
skipstart -= 1 | |
(0 == skipstart) && (state = 0) | |
end | |
end | |
was_cr = is_cr | |
end | |
if isempty(error_str) | |
if 1 == state # quoted field | |
error_str = "truncated column at row $(nrows+1) column $(col+1)" | |
elseif (2 == state) || (3 == state) || ((0 == state) && is_dlm) # unquoted field, second quote, or begin field with last character as delimiter | |
col += 1 | |
nrows += 1 | |
store_cell(dh, nrows, col, (3 == state), col_start_idx, idx-1) | |
ncols = max(ncols, col) | |
end | |
end | |
catch ex | |
if isa(ex, TypeError) && (ex.func == :store_cell) | |
rethrow(ex) | |
else | |
error("at row $(nrows+1), column $col : $ex)") | |
end | |
end | |
!isempty(error_str) && error(error_str) | |
return (nrows, ncols) | |
end | |
readcsv(io; opts...) = readdlm(io, ','; opts...) | |
readcsv(io, T::Type; opts...) = readdlm(io, ',', T; opts...) | |
# todo: keyword argument for # of digits to print | |
writedlm_cell(io::IO, elt::AbstractFloat, dlm, quotes) = print_shortest(io, elt) | |
function writedlm_cell{T}(io::IO, elt::AbstractString, dlm::T, quotes::Bool) | |
if quotes && !isempty(elt) && (('"' in elt) || ('\n' in elt) || ((T <: Char) ? (dlm in elt) : contains(elt, dlm))) | |
print(io, '"', replace(elt, r"\"", "\"\""), '"') | |
else | |
print(io, elt) | |
end | |
end | |
writedlm_cell(io::IO, elt, dlm, quotes) = print(io, elt) | |
function writedlm(io::IO, a::AbstractMatrix, dlm; opts...) | |
optsd = val_opts(opts) | |
quotes = get(optsd, :quotes, true) | |
pb = PipeBuffer() | |
lastc = last(indices(a, 2)) | |
for i = indices(a, 1) | |
for j = indices(a, 2) | |
writedlm_cell(pb, a[i, j], dlm, quotes) | |
j == lastc ? write(pb,'\n') : print(pb,dlm) | |
end | |
(nb_available(pb) > (16*1024)) && write(io, takebuf_array(pb)) | |
end | |
write(io, takebuf_array(pb)) | |
nothing | |
end | |
writedlm{T}(io::IO, a::AbstractArray{T,0}, dlm; opts...) = writedlm(io, reshape(a,1), dlm; opts...) | |
# write an iterable row as dlm-separated items | |
function writedlm_row(io::IO, row, dlm, quotes) | |
state = start(row) | |
while !done(row, state) | |
(x, state) = next(row, state) | |
writedlm_cell(io, x, dlm, quotes) | |
done(row, state) ? write(io,'\n') : print(io,dlm) | |
end | |
end | |
# If the row is a single string, write it as a string rather than | |
# iterating over characters. Also, include the common case of | |
# a Number (handled correctly by the generic writedlm_row above) | |
# purely as an optimization. | |
function writedlm_row(io::IO, row::Union{Number,AbstractString}, dlm, quotes) | |
writedlm_cell(io, row, dlm, quotes) | |
write(io, '\n') | |
end | |
# write an iterable collection of iterable rows | |
function writedlm(io::IO, itr, dlm; opts...) | |
optsd = val_opts(opts) | |
quotes = get(optsd, :quotes, true) | |
pb = PipeBuffer() | |
for row in itr | |
writedlm_row(pb, row, dlm, quotes) | |
(nb_available(pb) > (16*1024)) && write(io, takebuf_array(pb)) | |
end | |
write(io, takebuf_array(pb)) | |
nothing | |
end | |
function writedlm(fname::AbstractString, a, dlm; opts...) | |
open(fname, "w") do io | |
writedlm(io, a, dlm; opts...) | |
end | |
end | |
""" | |
writedlm(f, A, delim='\\t'; opts) | |
Write `A` (a vector, matrix, or an iterable collection of iterable rows) as text to `f` | |
(either a filename string or an [`IO`](:class:`IO`) stream) using the given delimiter | |
`delim` (which defaults to tab, but can be any printable Julia object, typically a `Char` or | |
`AbstractString`). | |
For example, two vectors `x` and `y` of the same length can be written as two columns of | |
tab-delimited text to `f` by either `writedlm(f, [x y])` or by `writedlm(f, zip(x, y))`. | |
""" | |
writedlm(io, a; opts...) = writedlm(io, a, '\t'; opts...) | |
""" | |
writecsv(filename, A; opts) | |
Equivalent to [`writedlm`](:func:`writedlm`) with `delim` set to comma. | |
""" | |
writecsv(io, a; opts...) = writedlm(io, a, ','; opts...) | |
show(io::IO, ::MIME"text/csv", a) = writedlm(io, a, ',') | |
show(io::IO, ::MIME"text/tab-separated-values", a) = writedlm(io, a, '\t') | |
end # module DataFmt | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Convert # of Rata Die days to proleptic Gregorian calendar y,m,d,w | |
# Reference: http://mysite.verizon.net/aesir_research/date/date0.htm | |
function yearmonthday(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
d = c - div(153m-457,5); return m > 12 ? (y+1,m-12,d) : (y,m,d) | |
end | |
function year(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
return m > 12 ? y+1 : y | |
end | |
function yearmonth(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
return m > 12 ? (y+1,m-12) : (y,m) | |
end | |
function month(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
return m > 12 ? m-12 : m | |
end | |
function monthday(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
d = c - div(153m-457,5); return m > 12 ? (m-12,d) : (m,d) | |
end | |
function day(days) | |
z = days + 306; h = 100z - 25; a = fld(h,3652425); b = a - fld(a,4) | |
y = fld(100b+h,36525); c = b + z - 365y - fld(y,4); m = div(5c+456,153) | |
return c - div(153m-457,5) | |
end | |
# https://en.wikipedia.org/wiki/Talk:ISO_week_date#Algorithms | |
function week(days) | |
w = div(abs(days-1),7) % 20871 | |
c,w = divrem((w + (w >= 10435)),5218) | |
w = (w*28+[15,23,3,11][c+1]) % 1461 | |
return div(w,28) + 1 | |
end | |
# Accessor functions | |
value(dt::TimeType) = dt.instant.periods.value | |
days(dt::Date) = value(dt) | |
days(dt::DateTime) = fld(value(dt),86400000) | |
year(dt::TimeType) = year(days(dt)) | |
month(dt::TimeType) = month(days(dt)) | |
week(dt::TimeType) = week(days(dt)) | |
day(dt::TimeType) = day(days(dt)) | |
hour(dt::DateTime) = mod(fld(value(dt),3600000),24) | |
minute(dt::DateTime) = mod(fld(value(dt),60000),60) | |
second(dt::DateTime) = mod(fld(value(dt),1000),60) | |
millisecond(dt::DateTime) = mod(value(dt),1000) | |
dayofmonth(dt::TimeType) = day(dt) | |
yearmonth(dt::TimeType) = yearmonth(days(dt)) | |
monthday(dt::TimeType) = monthday(days(dt)) | |
yearmonthday(dt::TimeType) = yearmonthday(days(dt)) | |
# Documentation for exported accessors | |
for func in (:year, :month) | |
name = string(func) | |
@eval begin | |
@doc """ | |
$($name)(dt::TimeType) -> Int64 | |
The $($name) of a `Date` or `DateTime` as an `Int64`. | |
""" $func(dt::TimeType) | |
end | |
end | |
""" | |
week(dt::TimeType) -> Int64 | |
Return the [ISO week date](https://en.wikipedia.org/wiki/ISO_week_date) of a `Date` or | |
`DateTime` as an `Int64`. Note that the first week of a year is the week that contains the | |
first Thursday of the year which can result in dates prior to January 4th being in the last | |
week of the previous year. For example `week(Date(2005,1,1))` is the 53rd week of 2004. | |
""" | |
week(dt::TimeType) | |
for func in (:day, :dayofmonth) | |
name = string(func) | |
@eval begin | |
@doc """ | |
$($name)(dt::TimeType) -> Int64 | |
The day of month of a `Date` or `DateTime` as an `Int64`. | |
""" $func(dt::TimeType) | |
end | |
end | |
""" | |
hour(dt::DateTime) -> Int64 | |
The hour of day of a `DateTime` as an `Int64`. | |
""" | |
hour(dt::DateTime) | |
for func in (:minute, :second, :millisecond) | |
name = string(func) | |
@eval begin | |
@doc """ | |
$($name)(dt::DateTime) -> Int64 | |
The $($name) of a `DateTime` as an `Int64`. | |
""" $func(dt::DateTime) | |
end | |
end | |
for parts in (["year", "month"], ["month", "day"], ["year", "month", "day"]) | |
name = join(parts) | |
func = Symbol(name) | |
@eval begin | |
@doc """ | |
$($name)(dt::TimeType) -> ($(join(repeated(Int64, length($parts)), ", "))) | |
Simultaneously return the $(join($parts, ", ", " and ")) parts of a `Date` or | |
`DateTime`. | |
""" $func(dt::TimeType) | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
### truncation | |
Base.trunc(dt::Date, p::Type{Year}) = Date(UTD(totaldays(year(dt), 1, 1))) | |
Base.trunc(dt::Date, p::Type{Month}) = firstdayofmonth(dt) | |
Base.trunc(dt::Date, p::Type{Day}) = dt | |
Base.trunc(dt::DateTime, p::Type{Year}) = DateTime(trunc(Date(dt), Year)) | |
Base.trunc(dt::DateTime, p::Type{Month}) = DateTime(trunc(Date(dt), Month)) | |
Base.trunc(dt::DateTime, p::Type{Day}) = DateTime(Date(dt)) | |
Base.trunc(dt::DateTime, p::Type{Hour}) = dt - Minute(dt) - Second(dt) - Millisecond(dt) | |
Base.trunc(dt::DateTime, p::Type{Minute}) = dt - Second(dt) - Millisecond(dt) | |
Base.trunc(dt::DateTime, p::Type{Second}) = dt - Millisecond(dt) | |
Base.trunc(dt::DateTime, p::Type{Millisecond}) = dt | |
""" | |
trunc(dt::TimeType, ::Type{Period}) -> TimeType | |
Truncates the value of `dt` according to the provided `Period` type. E.g. if `dt` is | |
`1996-01-01T12:30:00`, then `trunc(dt,Day) == 1996-01-01T00:00:00`. | |
""" | |
Dates.trunc(::Dates.TimeType, ::Type{Dates.Period}) | |
# Adjusters | |
""" | |
firstdayofweek(dt::TimeType) -> TimeType | |
Adjusts `dt` to the Monday of its week. | |
""" | |
function firstdayofweek end | |
firstdayofweek(dt::Date) = Date(UTD(value(dt) - dayofweek(dt) + 1)) | |
firstdayofweek(dt::DateTime) = DateTime(firstdayofweek(Date(dt))) | |
""" | |
lastdayofweek(dt::TimeType) -> TimeType | |
Adjusts `dt` to the Sunday of its week. | |
""" | |
function lastdayofweek end | |
lastdayofweek(dt::Date) = Date(UTD(value(dt) + (7 - dayofweek(dt)))) | |
lastdayofweek(dt::DateTime) = DateTime(lastdayofweek(Date(dt))) | |
""" | |
firstdayofmonth(dt::TimeType) -> TimeType | |
Adjusts `dt` to the first day of its month. | |
""" | |
function firstdayofmonth end | |
firstdayofmonth(dt::Date) = Date(UTD(value(dt) - day(dt) + 1)) | |
firstdayofmonth(dt::DateTime) = DateTime(firstdayofmonth(Date(dt))) | |
""" | |
lastdayofmonth(dt::TimeType) -> TimeType | |
Adjusts `dt` to the last day of its month. | |
""" | |
function lastdayofmonth end | |
function lastdayofmonth(dt::Date) | |
y, m, d = yearmonthday(dt) | |
return Date(UTD(value(dt) + daysinmonth(y, m) - d)) | |
end | |
lastdayofmonth(dt::DateTime) = DateTime(lastdayofmonth(Date(dt))) | |
""" | |
firstdayofyear(dt::TimeType) -> TimeType | |
Adjusts `dt` to the first day of its year. | |
""" | |
function firstdayofyear end | |
firstdayofyear(dt::Date) = Date(UTD(value(dt) - dayofyear(dt) + 1)) | |
firstdayofyear(dt::DateTime) = DateTime(firstdayofyear(Date(dt))) | |
""" | |
lastdayofyear(dt::TimeType) -> TimeType | |
Adjusts `dt` to the last day of its year. | |
""" | |
function lastdayofyear end | |
function lastdayofyear(dt::Date) | |
y, m, d = yearmonthday(dt) | |
return Date(UTD(value(dt) + daysinyear(y) - dayofyear(y, m, d))) | |
end | |
lastdayofyear(dt::DateTime) = DateTime(lastdayofyear(Date(dt))) | |
""" | |
firstdayofquarter(dt::TimeType) -> TimeType | |
Adjusts `dt` to the first day of its quarter. | |
""" | |
function firstdayofquarter end | |
function firstdayofquarter(dt::Date) | |
y,m = yearmonth(dt) | |
mm = m < 4 ? 1 : m < 7 ? 4 : m < 10 ? 7 : 10 | |
return Date(y, mm, 1) | |
end | |
firstdayofquarter(dt::DateTime) = DateTime(firstdayofquarter(Date(dt))) | |
""" | |
lastdayofquarter(dt::TimeType) -> TimeType | |
Adjusts `dt` to the last day of its quarter. | |
""" | |
function lastdayofquarter end | |
function lastdayofquarter(dt::Date) | |
y,m = yearmonth(dt) | |
mm, d = m < 4 ? (3, 31) : m < 7 ? (6, 30) : m < 10 ? (9, 30) : (12, 31) | |
return Date(y, mm, d) | |
end | |
lastdayofquarter(dt::DateTime) = DateTime(lastdayofquarter(Date(dt))) | |
# Temporal Adjusters | |
immutable DateFunction | |
f::Function | |
# validate boolean, single-arg inner constructor | |
function DateFunction(f::ANY, negate::Bool, dt::TimeType) | |
isa(f(dt), Bool) || throw(ArgumentError("Provided function must take a single TimeType argument and return true or false")) | |
return new(negate ? x -> !f(x)::Bool : f) | |
end | |
end | |
Base.show(io::IO, df::DateFunction) = println(io, df.f) | |
# Core adjuster | |
function adjust(df::DateFunction, start, step, limit) | |
for i = 1:limit | |
df.f(start) && return start | |
start += step | |
end | |
throw(ArgumentError("Adjustment limit reached: $limit iterations")) | |
end | |
function adjust(func::Function, start; step::Period=Day(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, start), start, step, limit) | |
end | |
# Constructors using DateFunctions | |
""" | |
Date(f::Function, y[, m, d]; step=Day(1), negate=false, limit=10000) -> Date | |
Create a `Date` through the adjuster API. The starting point will be constructed from the | |
provided `y, m, d` arguments, and will be adjusted until `f::Function` returns `true`. The | |
step size in adjusting can be provided manually through the `step` keyword. If | |
`negate=true`, then the adjusting will stop when `f::Function` returns `false` instead of | |
`true`. `limit` provides a limit to the max number of iterations the adjustment API will | |
pursue before throwing an error (given that `f::Function` is never satisfied). | |
""" | |
function Date(func::Function, y, m=1, d=1;step::Period=Day(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, Date(y, m, d)), Date(y, m, d), step, limit) | |
end | |
""" | |
DateTime(f::Function, y[, m, d, h, mi, s]; step=Day(1), negate=false, limit=10000) -> DateTime | |
Create a `DateTime` through the adjuster API. The starting point will be constructed from | |
the provided `y, m, d...` arguments, and will be adjusted until `f::Function` returns | |
`true`. The step size in adjusting can be provided manually through the `step` keyword. If | |
`negate=true`, then the adjusting will stop when `f::Function` returns `false` instead of | |
`true`. `limit` provides a limit to the max number of iterations the adjustment API will | |
pursue before throwing an error (in the case that `f::Function` is never satisfied). | |
""" | |
DateTime(::Function, args...) | |
function DateTime(func::Function, y, m=1; step::Period=Day(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, DateTime(y, m)), DateTime(y, m), step, limit) | |
end | |
function DateTime(func::Function, y, m, d; step::Period=Hour(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, DateTime(y)), DateTime(y, m, d), step, limit) | |
end | |
function DateTime(func::Function, y, m, d, h; step::Period=Minute(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, DateTime(y)), DateTime(y, m, d, h), step, limit) | |
end | |
function DateTime(func::Function, y, m, d, h, mi; step::Period=Second(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, DateTime(y)), DateTime(y, m, d, h, mi), step, limit) | |
end | |
function DateTime(func::Function, y, m, d, h, mi, s; step::Period=Millisecond(1), negate::Bool=false, limit::Int=10000) | |
return adjust(DateFunction(func, negate, DateTime(y)), DateTime(y, m, d, h, mi, s), step, limit) | |
end | |
# Return the next TimeType that falls on dow | |
ISDAYOFWEEK = Dict(Mon => DateFunction(ismonday, false, Date(0)), | |
Tue => DateFunction(istuesday, false, Date(0)), | |
Wed => DateFunction(iswednesday, false, Date(0)), | |
Thu => DateFunction(isthursday, false, Date(0)), | |
Fri => DateFunction(isfriday, false, Date(0)), | |
Sat => DateFunction(issaturday, false, Date(0)), | |
Sun => DateFunction(issunday, false, Date(0))) | |
# "same" indicates whether the current date can be considered or not | |
""" | |
tonext(dt::TimeType,dow::Int;same::Bool=false) -> TimeType | |
Adjusts `dt` to the next day of week corresponding to `dow` with `1 = Monday, 2 = Tuesday, | |
etc`. Setting `same=true` allows the current `dt` to be considered as the next `dow`, | |
allowing for no adjustment to occur. | |
""" | |
tonext(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt+Day(1), Day(1), 7) | |
# Return the next TimeType where func evals true using step in incrementing | |
""" | |
tonext(func::Function,dt::TimeType;step=Day(1),negate=false,limit=10000,same=false) -> TimeType | |
Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func` | |
returns `true`. `func` must take a single `TimeType` argument and return a `Bool`. `same` | |
allows `dt` to be considered in satisfying `func`. `negate` will make the adjustment process | |
terminate when `func` returns `false` instead of `true`. | |
""" | |
function tonext(func::Function, dt::TimeType;step::Period=Day(1), negate::Bool=false, limit::Int=10000, same::Bool=false) | |
return adjust(DateFunction(func, negate, dt), same ? dt : dt+step, step, limit) | |
end | |
""" | |
toprev(dt::TimeType,dow::Int;same::Bool=false) -> TimeType | |
Adjusts `dt` to the previous day of week corresponding to `dow` with `1 = Monday, 2 = | |
Tuesday, etc`. Setting `same=true` allows the current `dt` to be considered as the previous | |
`dow`, allowing for no adjustment to occur. | |
""" | |
toprev(dt::TimeType, dow::Int; same::Bool=false) = adjust(ISDAYOFWEEK[dow], same ? dt : dt+Day(-1), Day(-1), 7) | |
""" | |
toprev(func::Function,dt::TimeType;step=Day(-1),negate=false,limit=10000,same=false) -> TimeType | |
Adjusts `dt` by iterating at most `limit` iterations by `step` increments until `func` | |
returns `true`. `func` must take a single `TimeType` argument and return a `Bool`. `same` | |
allows `dt` to be considered in satisfying `func`. `negate` will make the adjustment process | |
terminate when `func` returns `false` instead of `true`. | |
""" | |
function toprev(func::Function, dt::TimeType; step::Period=Day(-1), negate::Bool=false, limit::Int=10000, same::Bool=false) | |
return adjust(DateFunction(func, negate, dt), same ? dt : dt+step, step, limit) | |
end | |
# Return the first TimeType that falls on dow in the Month or Year | |
""" | |
tofirst(dt::TimeType,dow::Int;of=Month) -> TimeType | |
Adjusts `dt` to the first `dow` of its month. Alternatively, `of=Year` will adjust to the | |
first `dow` of the year. | |
""" | |
function tofirst(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month) | |
dt = of <: Month ? firstdayofmonth(dt) : firstdayofyear(dt) | |
return adjust(ISDAYOFWEEK[dow], dt, Day(1), 366) | |
end | |
# Return the last TimeType that falls on dow in the Month or Year | |
""" | |
tolast(dt::TimeType,dow::Int;of=Month) -> TimeType | |
Adjusts `dt` to the last `dow` of its month. Alternatively, `of=Year` will adjust to the | |
last `dow` of the year. | |
""" | |
function tolast(dt::TimeType, dow::Int; of::Union{Type{Year}, Type{Month}}=Month) | |
dt = of <: Month ? lastdayofmonth(dt) : lastdayofyear(dt) | |
return adjust(ISDAYOFWEEK[dow], dt, Day(-1), 366) | |
end | |
function recur{T<:TimeType}(fun::Function, start::T, stop::T; step::Period=Day(1), negate::Bool=false, limit::Int=10000) | |
((start != stop) & ((step > zero(step)) != (stop > start))) && return T[] | |
a = T[] | |
check = start <= stop ? 1 : -1 | |
df = Dates.DateFunction(fun, negate, start) | |
while true | |
next = Dates.adjust(df, start, step, limit) | |
cmp(next, stop) == check && break | |
push!(a, next) | |
start = next + step | |
end | |
return a | |
end | |
""" | |
recur{T<:TimeType}(func::Function,dr::StepRange{T};negate=false,limit=10000) -> Vector{T} | |
`func` takes a single TimeType argument and returns a `Bool` indicating whether the input | |
should be "included" in the final set. `recur` applies `func` over each element in the range | |
of `dr`, including those elements for which `func` returns `true` in the resulting Array, | |
unless `negate=true`, then only elements where `func` returns `false` are included. | |
""" | |
function recur{T<:TimeType}(fun::Function, dr::StepRange{T};negate::Bool=false, limit::Int=10000) | |
return recur(fun, first(dr), last(dr); step=step(dr), negate=negate, limit=limit) | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Instant arithmetic | |
(+)(x::Instant) = x | |
(-){T<:Instant}(x::T,y::T) = x.periods - y.periods | |
# TimeType arithmetic | |
(+)(x::TimeType) = x | |
(-){T<:TimeType}(x::T,y::T) = x.instant - y.instant | |
# TimeType-Year arithmetic | |
function (+)(dt::DateTime,y::Year) | |
oy,m,d = yearmonthday(dt); ny = oy+value(y); ld = daysinmonth(ny,m) | |
return DateTime(ny,m,d <= ld ? d : ld,hour(dt),minute(dt),second(dt),millisecond(dt)) | |
end | |
function (+)(dt::Date,y::Year) | |
oy,m,d = yearmonthday(dt); ny = oy+value(y); ld = daysinmonth(ny,m) | |
return Date(ny,m,d <= ld ? d : ld) | |
end | |
function (-)(dt::DateTime,y::Year) | |
oy,m,d = yearmonthday(dt); ny = oy-value(y); ld = daysinmonth(ny,m) | |
return DateTime(ny,m,d <= ld ? d : ld,hour(dt),minute(dt),second(dt),millisecond(dt)) | |
end | |
function (-)(dt::Date,y::Year) | |
oy,m,d = yearmonthday(dt); ny = oy-value(y); ld = daysinmonth(ny,m) | |
return Date(ny,m,d <= ld ? d : ld) | |
end | |
# TimeType-Month arithmetic | |
# monthwrap adds two months with wraparound behavior (i.e. 12 + 1 == 1) | |
monthwrap(m1,m2) = (v = mod1(m1+m2,12); return v < 0 ? 12 + v : v) | |
# yearwrap takes a starting year/month and a month to add and returns | |
# the resulting year with wraparound behavior (i.e. 2000-12 + 1 == 2001) | |
yearwrap(y,m1,m2) = y + fld(m1 + m2 - 1,12) | |
function (+)(dt::DateTime,z::Month) | |
y,m,d = yearmonthday(dt) | |
ny = yearwrap(y,m,value(z)) | |
mm = monthwrap(m,value(z)); ld = daysinmonth(ny,mm) | |
return DateTime(ny,mm,d <= ld ? d : ld,hour(dt),minute(dt),second(dt),millisecond(dt)) | |
end | |
function (+)(dt::Date,z::Month) | |
y,m,d = yearmonthday(dt) | |
ny = yearwrap(y,m,value(z)) | |
mm = monthwrap(m,value(z)); ld = daysinmonth(ny,mm) | |
return Date(ny,mm,d <= ld ? d : ld) | |
end | |
function (-)(dt::DateTime,z::Month) | |
y,m,d = yearmonthday(dt) | |
ny = yearwrap(y,m,-value(z)) | |
mm = monthwrap(m,-value(z)); ld = daysinmonth(ny,mm) | |
return DateTime(ny,mm,d <= ld ? d : ld,hour(dt),minute(dt),second(dt),millisecond(dt)) | |
end | |
function (-)(dt::Date,z::Month) | |
y,m,d = yearmonthday(dt) | |
ny = yearwrap(y,m,-value(z)) | |
mm = monthwrap(m,-value(z)); ld = daysinmonth(ny,mm) | |
return Date(ny,mm,d <= ld ? d : ld) | |
end | |
(+)(x::Date,y::Week) = return Date(UTD(value(x) + 7*value(y))) | |
(-)(x::Date,y::Week) = return Date(UTD(value(x) - 7*value(y))) | |
(+)(x::Date,y::Day) = return Date(UTD(value(x) + value(y))) | |
(-)(x::Date,y::Day) = return Date(UTD(value(x) - value(y))) | |
(+)(x::DateTime,y::Period) = return DateTime(UTM(value(x)+toms(y))) | |
(-)(x::DateTime,y::Period) = return DateTime(UTM(value(x)-toms(y))) | |
(+)(y::Period,x::TimeType) = x + y | |
(-)(y::Period,x::TimeType) = x - y | |
for op in (:.+, :.-) | |
op_ = Symbol(string(op)[2:end]) | |
@eval begin | |
# GeneralPeriod, AbstractArray{TimeType} | |
($op){T<:TimeType}(x::AbstractArray{T}, y::GeneralPeriod) = | |
reshape(T[($op_)(i,y) for i in x], size(x)) | |
($op){T<:TimeType}(y::GeneralPeriod, x::AbstractArray{T}) = ($op)(x,y) | |
($op_){T<:TimeType}(x::AbstractArray{T}, y::GeneralPeriod) = ($op)(x,y) | |
($op_){T<:TimeType}(y::GeneralPeriod, x::AbstractArray{T}) = ($op)(x,y) | |
# TimeType, StridedArray{GeneralPeriod} | |
($op){T<:TimeType,P<:GeneralPeriod}(x::StridedArray{P}, y::T) = | |
reshape(T[($op_)(i,y) for i in x], size(x)) | |
($op){P<:GeneralPeriod}(y::TimeType, x::StridedArray{P}) = ($op)(x,y) | |
($op_){T<:TimeType,P<:GeneralPeriod}(x::StridedArray{P}, y::T) = ($op)(x,y) | |
($op_){P<:GeneralPeriod}(y::TimeType, x::StridedArray{P}) = ($op)(x,y) | |
end | |
end | |
# TimeType, AbstractArray{TimeType} | |
(.-){T<:TimeType}(x::AbstractArray{T}, y::T) = reshape(Period[i - y for i in x], size(x)) | |
(.-){T<:TimeType}(y::T, x::AbstractArray{T}) = -(x .- y) | |
(-){T<:TimeType}(x::AbstractArray{T}, y::T) = x .- y | |
(-){T<:TimeType}(y::T, x::AbstractArray{T}) = -(x .- y) | |
# AbstractArray{TimeType}, AbstractArray{TimeType} | |
(-){T<:TimeType}(x::OrdinalRange{T}, y::OrdinalRange{T}) = collect(x) - collect(y) | |
(-){T<:TimeType}(x::Range{T}, y::Range{T}) = collect(x) - collect(y) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Conversion/Promotion | |
""" | |
Date(dt::DateTime) -> Date | |
Converts a `DateTime` to a `Date`. The hour, minute, second, and millisecond parts of | |
the `DateTime` are truncated, so only the year, month and day parts are used in | |
construction. | |
""" | |
Date(dt::TimeType) = convert(Date,dt) | |
""" | |
DateTime(dt::Date) -> DateTime | |
Converts a `Date` to a `DateTime`. The hour, minute, second, and millisecond parts of | |
the new `DateTime` are assumed to be zero. | |
""" | |
DateTime(dt::TimeType) = convert(DateTime,dt) | |
Base.convert(::Type{DateTime},dt::Date) = DateTime(UTM(value(dt)*86400000)) | |
Base.convert(::Type{Date},dt::DateTime) = Date(UTD(days(dt))) | |
""" | |
convert{T<:Real}(::Type{T}, dt::DateTime) -> T | |
Converts a DateTime value `dt` to a number of type `T`. The returned value corresponds to the number of Rata Die milliseconds since epoch. | |
See `convert(DateTime, x::Real)` for inverse. | |
""" | |
Base.convert{R<:Real}(::Type{R},x::DateTime) = convert(R,value(x)) | |
""" | |
convert{T<:Real}(::Type{T}, dt::Date) -> T | |
Converts a Date value `dt` to a number of type `T`. The returned value corresponds to the number of Rata Die days since epoch. | |
See `convert(Date, x::Real)` for inverse. | |
""" | |
Base.convert{R<:Real}(::Type{R},x::Date) = convert(R,value(x)) | |
""" | |
convert{T<:Real}(::Type{DateTime}, x::T) -> DateTime | |
Converts a number of type `T` to a DateTime. `x` should be the number of Rata Die milliseconds since epoch. | |
See `convert(Int64,dt::DateTime)` for inverse. | |
""" | |
Base.convert{R<:Real}(::Type{DateTime}, x::R) = DateTime(UTM(x)) | |
""" | |
convert{T<:Real}(::Type{Date}, x::T) -> Date | |
Converts a number of type `T` to a Date. `x` should be the number of Rata Die days since epoch. | |
See `convert(Int64,dt::Date)` for inverse. | |
""" | |
Base.convert{R<:Real}(::Type{Date}, x::R) = Date(UTD(x)) | |
### External Conversions | |
const UNIXEPOCH = value(DateTime(1970)) #Rata Die milliseconds for 1970-01-01T00:00:00 | |
""" | |
unix2datetime(x) -> DateTime | |
Takes the number of seconds since unix epoch `1970-01-01T00:00:00` and converts to the | |
corresponding `DateTime`. | |
""" | |
function unix2datetime(x) | |
rata = UNIXEPOCH + round(Int64, Int64(1000) * x) | |
return DateTime(UTM(rata)) | |
end | |
""" | |
datetime2unix(dt::DateTime) -> Float64 | |
Takes the given `DateTime` and returns the number of seconds | |
since the unix epoch `1970-01-01T00:00:00` as a `Float64`. | |
""" | |
datetime2unix(dt::DateTime) = (value(dt) - UNIXEPOCH)/1000.0 | |
""" | |
now() -> DateTime | |
Returns a `DateTime` corresponding to the user's system time including the system timezone | |
locale. | |
""" | |
function now() | |
tv = Libc.TimeVal() | |
tm = Libc.TmStruct(tv.sec) | |
return DateTime(tm.year+1900,tm.month+1,tm.mday,tm.hour,tm.min,tm.sec,div(tv.usec,1000)) | |
end | |
""" | |
today() -> Date | |
Returns the date portion of `now()`. | |
""" | |
today() = Date(now()) | |
""" | |
now(::Type{UTC}) -> DateTime | |
Returns a `DateTime` corresponding to the user's system time as UTC/GMT. | |
""" | |
now(::Type{UTC}) = unix2datetime(time()) | |
""" | |
rata2datetime(days) -> DateTime | |
Takes the number of Rata Die days since epoch `0000-12-31T00:00:00` and returns the | |
corresponding `DateTime`. | |
""" | |
rata2datetime(days) = DateTime(yearmonthday(days)...) | |
""" | |
datetime2rata(dt::TimeType) -> Int64 | |
Returns the number of Rata Die days since epoch from the given `Date` or `DateTime`. | |
""" | |
datetime2rata(dt::TimeType) = days(dt) | |
# Julian conversions | |
const JULIANEPOCH = value(DateTime(-4713,11,24,12)) | |
""" | |
julian2datetime(julian_days) -> DateTime | |
Takes the number of Julian calendar days since epoch `-4713-11-24T12:00:00` and returns the | |
corresponding `DateTime`. | |
""" | |
function julian2datetime(f) | |
rata = JULIANEPOCH + round(Int64, Int64(86400000) * f) | |
return DateTime(UTM(rata)) | |
end | |
""" | |
datetime2julian(dt::DateTime) -> Float64 | |
Takes the given `DateTime` and returns the number of Julian calendar days since the julian | |
epoch `-4713-11-24T12:00:00` as a `Float64`. | |
""" | |
datetime2julian(dt::DateTime) = (value(dt) - JULIANEPOCH)/86400000.0 | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module Dates | |
importall ..Base.Operators | |
using Base.Iterators | |
include("types.jl") | |
include("periods.jl") | |
include("accessors.jl") | |
include("query.jl") | |
include("arithmetic.jl") | |
include("conversions.jl") | |
include("ranges.jl") | |
include("adjusters.jl") | |
include("rounding.jl") | |
include("io.jl") | |
export Period, DatePeriod, TimePeriod, | |
Year, Month, Week, Day, Hour, Minute, Second, Millisecond, | |
TimeZone, UTC, TimeType, DateTime, Date, | |
# accessors.jl | |
yearmonthday, yearmonth, monthday, year, month, week, day, | |
hour, minute, second, millisecond, dayofmonth, | |
# query.jl | |
dayofweek, isleapyear, daysinmonth, daysinyear, dayofyear, dayname, dayabbr, | |
dayofweekofmonth, daysofweekinmonth, monthname, monthabbr, | |
quarterofyear, dayofquarter, | |
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, | |
Mon, Tue, Wed, Thu, Fri, Sat, Sun, | |
January, February, March, April, May, June, | |
July, August, September, October, November, December, | |
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec, | |
# conversions.jl | |
unix2datetime, datetime2unix, now, today, | |
rata2datetime, datetime2rata, julian2datetime, datetime2julian, | |
# adjusters.jl | |
firstdayofweek, lastdayofweek, | |
firstdayofmonth, lastdayofmonth, | |
firstdayofyear, lastdayofyear, | |
firstdayofquarter, lastdayofquarter, | |
adjust, tonext, toprev, tofirst, tolast, recur, | |
# io.jl | |
ISODateTimeFormat, ISODateFormat, DateFormat, RFC1123Format | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# TODO: optimize this | |
function Base.string(dt::DateTime) | |
y,m,d = yearmonthday(days(dt)) | |
h,mi,s = hour(dt),minute(dt),second(dt) | |
yy = y < 0 ? @sprintf("%05i",y) : lpad(y,4,"0") | |
mm = lpad(m,2,"0") | |
dd = lpad(d,2,"0") | |
hh = lpad(h,2,"0") | |
mii = lpad(mi,2,"0") | |
ss = lpad(s,2,"0") | |
ms = millisecond(dt) == 0 ? "" : string(millisecond(dt)/1000.0)[2:end] | |
return "$yy-$mm-$(dd)T$hh:$mii:$ss$(ms)" | |
end | |
Base.show(io::IO,x::DateTime) = print(io,string(x)) | |
function Base.string(dt::Date) | |
y,m,d = yearmonthday(value(dt)) | |
yy = y < 0 ? @sprintf("%05i",y) : lpad(y,4,"0") | |
mm = lpad(m,2,"0") | |
dd = lpad(d,2,"0") | |
return "$yy-$mm-$dd" | |
end | |
Base.show(io::IO,x::Date) = print(io,string(x)) | |
### Parsing | |
const english = Dict{String,Int}("january"=>1,"february"=>2,"march"=>3,"april"=>4, | |
"may"=>5,"june"=>6,"july"=>7,"august"=>8,"september"=>9, | |
"october"=>10,"november"=>11,"december"=>12) | |
const abbrenglish = Dict{String,Int}("jan"=>1,"feb"=>2,"mar"=>3,"apr"=>4, | |
"may"=>5,"jun"=>6,"jul"=>7,"aug"=>8,"sep"=>9, | |
"oct"=>10,"nov"=>11,"dec"=>12) | |
const MONTHTOVALUE = Dict{String,Dict{String,Int}}("english"=>english) | |
const MONTHTOVALUEABBR = Dict{String,Dict{String,Int}}("english"=>abbrenglish) | |
# Date/DateTime Parsing | |
abstract Slot{T<:Any} | |
immutable DelimitedSlot{T<:Any} <: Slot{T} | |
parser::Type{T} | |
letter::Char | |
width::Int | |
transition::Union{Regex,AbstractString} | |
end | |
immutable FixedWidthSlot{T<:Any} <: Slot{T} | |
parser::Type{T} | |
letter::Char | |
width::Int | |
end | |
immutable DateFormat | |
slots::Array{Slot,1} | |
prefix::AbstractString # optional transition from the start of a string to the 1st slot | |
locale::AbstractString | |
end | |
abstract DayOfWeekSlot | |
# Slot rules translate letters into types. Note that | |
# list of rules can be extended. | |
immutable SlotRule | |
rules::Array{Type} | |
end | |
const SLOT_RULE = SlotRule(Array{Type}(256)) | |
getindex(collection::SlotRule, key::Char) = collection.rules[Int(key)] | |
setindex!(collection::SlotRule, value::Type, key::Char) = collection.rules[Int(key)] = value | |
keys(c::SlotRule) = map(Char, filter(el -> isassigned(c.rules, el) && c.rules[el] != Void, eachindex(c.rules))) | |
SLOT_RULE['y'] = Year | |
SLOT_RULE['Y'] = Year | |
SLOT_RULE['m'] = Month | |
SLOT_RULE['u'] = Month | |
SLOT_RULE['U'] = Month | |
SLOT_RULE['e'] = DayOfWeekSlot | |
SLOT_RULE['E'] = DayOfWeekSlot | |
SLOT_RULE['d'] = Day | |
SLOT_RULE['H'] = Hour | |
SLOT_RULE['M'] = Minute | |
SLOT_RULE['S'] = Second | |
SLOT_RULE['s'] = Millisecond | |
duplicates(slots) = any(map(x->count(y->x.parser==y.parser,slots),slots) .> 1) | |
""" | |
DateFormat(format::AbstractString, locale::AbstractString="english") -> DateFormat | |
Construct a date formatting object that can be used for parsing date strings or | |
formatting a date object as a string. For details on the syntax for `format` see | |
[`DateTime(::AbstractString, ::AbstractString)`](:ref:`parsing <man-date-parsing>`) and | |
[`format`](:ref:`formatting <man-date-formatting>`). | |
""" | |
function DateFormat(f::AbstractString, locale::AbstractString="english") | |
slots = Slot[] | |
prefix = "" | |
params = () | |
last_offset = 1 | |
letters = join(keys(SLOT_RULE), "") | |
for m in eachmatch(Regex("(?<!\\\\)([\\Q$letters\\E])\\1*"), f) | |
letter = f[m.offset] | |
typ = SLOT_RULE[letter] | |
width = length(m.match) | |
tran = replace(f[last_offset:m.offset-1], r"\\(.)", s"\1") | |
if isempty(params) | |
prefix = tran | |
else | |
slot = tran == "" ? FixedWidthSlot(params...) : DelimitedSlot(params..., tran) | |
push!(slots,slot) | |
end | |
params = (typ,letter,width) | |
last_offset = m.offset + width | |
end | |
if !isempty(params) | |
if last_offset > endof(f) | |
slot = DelimitedSlot(params..., r"(?=\s|$)") | |
else | |
tran = replace(f[last_offset:end], r"\\(.)", s"\1") | |
if tran == "" | |
slot = FixedWidthSlot(params...) | |
else | |
slot = DelimitedSlot(params..., tran) | |
end | |
end | |
push!(slots,slot) | |
end | |
duplicates(slots) && throw(ArgumentError("Two separate periods of the same type detected")) | |
return DateFormat(slots,prefix,locale) | |
end | |
const SLOTERROR = ArgumentError("Non-digit character encountered") | |
slotparse(slot,x,locale) = !ismatch(r"[^0-9\s]",x) ? slot.parser(x) : throw(SLOTERROR) | |
function slotparse(slot::Slot{Month},x,locale) | |
if slot.letter == 'm' | |
ismatch(r"[^0-9\s]",x) ? throw(SLOTERROR) : return Month(x) | |
elseif slot.letter == 'u' | |
return Month(MONTHTOVALUEABBR[locale][lowercase(x)]) | |
else | |
return Month(MONTHTOVALUE[locale][lowercase(x)]) | |
end | |
end | |
slotparse(slot::Slot{Millisecond},x,locale) = !ismatch(r"[^0-9\s]",x) ? slot.parser(Base.parse(Float64,"."*x)*1000.0) : throw(SLOTERROR) | |
slotparse(slot::Slot{DayOfWeekSlot},x,locale) = nothing | |
function getslot(x,slot::DelimitedSlot,locale,cursor) | |
endind = first(search(x,slot.transition,nextind(x,cursor))) | |
if endind == 0 # we didn't find the next delimiter | |
s = x[cursor:end] | |
index = endof(x)+1 | |
else | |
s = x[cursor:(endind-1)] | |
index = nextind(x,endind) | |
end | |
return index, slotparse(slot,s,locale) | |
end | |
getslot(x,slot,locale,cursor) = (cursor+slot.width, slotparse(slot,x[cursor:(cursor+slot.width-1)], locale)) | |
function parse(x::AbstractString,df::DateFormat) | |
x = strip(x) | |
startswith(x, df.prefix) && (x = replace(x, df.prefix, "", 1)) | |
isempty(x) && throw(ArgumentError("Cannot parse empty format string")) | |
if isa(df.slots[1], DelimitedSlot) && first(search(x,df.slots[1].transition)) == 0 | |
throw(ArgumentError("Delimiter mismatch. Couldn't find first delimiter, \"$(df.slots[1].transition)\", in date string")) | |
end | |
periods = Period[] | |
extra = Any[] # Supports custom slot types such as TimeZone | |
cursor = 1 | |
for slot in df.slots | |
cursor, pe = getslot(x,slot,df.locale,cursor) | |
pe !== nothing && (isa(pe,Period) ? push!(periods,pe) : push!(extra,pe)) | |
cursor > endof(x) && break | |
end | |
sort!(periods,rev=true,lt=periodisless) | |
if isempty(extra) | |
return periods | |
else | |
return vcat(periods, extra) | |
end | |
end | |
slotformat(slot,dt,locale) = lpad(string(value(slot.parser(dt))),slot.width,"0") | |
function slotformat(slot::Slot{Year},dt,locale) | |
s = lpad(string(value(slot.parser(dt))),slot.width,"0") | |
if slot.letter == 'y' | |
return s[(end-slot.width+1):end] # Truncate the year | |
else # == 'Y' | |
return s | |
end | |
end | |
function slotformat(slot::Slot{Month},dt,locale) | |
if slot.letter == 'm' | |
return lpad(month(dt),slot.width,"0") | |
elseif slot.letter == 'u' | |
return VALUETOMONTHABBR[locale][month(dt)] | |
else | |
return VALUETOMONTH[locale][month(dt)] | |
end | |
end | |
function slotformat(slot::Slot{DayOfWeekSlot},dt,locale) | |
if slot.letter == 'e' | |
return VALUETODAYOFWEEKABBR[locale][dayofweek(dt)] | |
else # == 'E' | |
return VALUETODAYOFWEEK[locale][dayofweek(dt)] | |
end | |
end | |
slotformat(slot::Slot{Millisecond},dt,locale) = rpad(string(millisecond(dt)/1000.0)[3:end], slot.width, "0") | |
function format(dt::TimeType,df::DateFormat) | |
f = df.prefix | |
for slot in df.slots | |
f *= slotformat(slot,dt,df.locale) | |
if isa(slot, DelimitedSlot) | |
f *= isa(slot.transition, AbstractString) ? slot.transition : "" | |
end | |
end | |
return f | |
end | |
# UI | |
const ISODateTimeFormat = DateFormat("yyyy-mm-dd\\THH:MM:SS.s") | |
const ISODateFormat = DateFormat("yyyy-mm-dd") | |
const RFC1123Format = DateFormat("e, dd u yyyy HH:MM:SS") | |
""" | |
DateTime(dt::AbstractString, format::AbstractString; locale="english") -> DateTime | |
Construct a `DateTime` by parsing the `dt` date string following the pattern given in | |
the `format` string. The following character codes can be used to construct the `format` | |
string: | |
| Code | Matches | Comment | | |
|:-----------|:----------|:-------------------------------------------------------------| | |
| `y` | 1996, 96 | Returns year of 1996, 0096 | | |
| `Y` | 1996, 96 | Returns year of 1996, 0096. Equivalent to `y` | | |
| `m` | 1, 01 | Matches 1 or 2-digit months | | |
| `u` | Jan | Matches abbreviated months according to the `locale` keyword | | |
| `U` | January | Matches full month names according to the `locale` keyword | | |
| `d` | 1, 01 | Matches 1 or 2-digit days | | |
| `H` | 00 | Matches hours | | |
| `M` | 00 | Matches minutes | | |
| `S` | 00 | Matches seconds | | |
| `s` | .500 | Matches milliseconds | | |
| `e` | Mon, Tues | Matches abbreviated days of the week | | |
| `E` | Monday | Matches full name days of the week | | |
| `yyyymmdd` | 19960101 | Matches fixed-width year, month, and day | | |
Characters not listed above are normally treated as delimiters between date and time slots. | |
For example a `dt` string of "1996-01-15T00:00:00.0" would have a `format` string like | |
"y-m-dTH:M:S.s". If you need to use a code character as a delimiter you can escape it using | |
backslash. The date "1995y01m" would have the format "y\\ym\\m". | |
""" | |
DateTime(dt::AbstractString,format::AbstractString;locale::AbstractString="english") = DateTime(dt,DateFormat(format,locale)) | |
""" | |
DateTime(dt::AbstractString, df::DateFormat) -> DateTime | |
Construct a `DateTime` by parsing the `dt` date string following the pattern given in | |
the [`DateFormat`](:func:`Dates.DateFormat`) object. Similar to | |
`DateTime(::AbstractString, ::AbstractString)` but more efficient when repeatedly parsing | |
similarly formatted date strings with a pre-created `DateFormat` object. | |
""" | |
DateTime(dt::AbstractString,df::DateFormat=ISODateTimeFormat) = DateTime(parse(dt,df)...) | |
""" | |
Date(dt::AbstractString, format::AbstractString; locale="english") -> Date | |
Construct a `Date` object by parsing a `dt` date string following the pattern given in the | |
`format` string. Follows the same conventions as | |
`DateTime(::AbstractString, ::AbstractString)`. | |
""" | |
Date(dt::AbstractString,format::AbstractString;locale::AbstractString="english") = Date(dt,DateFormat(format,locale)) | |
""" | |
Date(dt::AbstractString, df::DateFormat) -> Date | |
Parse a date from a date string `dt` using a `DateFormat` object `df`. | |
""" | |
Date(dt::AbstractString,df::DateFormat=ISODateFormat) = Date(parse(dt,df)...) | |
""" | |
format(dt::TimeType, format::AbstractString; locale="english") -> AbstractString | |
Construct a string by using a `TimeType` object and applying the provided `format`. The | |
following character codes can be used to construct the `format` string: | |
| Code | Examples | Comment | | |
|:-----------|:----------|:-------------------------------------------------------------| | |
| `y` | 6 | Numeric year with a fixed width | | |
| `Y` | 1996 | Numeric year with a minimum width | | |
| `m` | 1, 12 | Numeric month with a minimum width | | |
| `u` | Jan | Month name shortened to 3-chars according to the `locale` | | |
| `U` | January | Full month name according to the `locale` keyword | | |
| `d` | 1, 31 | Day of the month with a minimum width | | |
| `H` | 0, 23 | Hour (24-hour clock) with a minimum width | | |
| `M` | 0, 59 | Minute with a minimum width | | |
| `S` | 0, 59 | Second with a minimum width | | |
| `s` | 000, 500 | Millisecond with a minimum width of 3 | | |
| `e` | Mon, Tue | Abbreviated days of the week | | |
| `E` | Monday | Full day of week name | | |
The number of sequential code characters indicate the width of the code. A format of | |
`yyyy-mm` specifies that the code `y` should have a width of four while `m` a width of two. | |
Codes that yield numeric digits have an associated mode: fixed-width or minimum-width. | |
The fixed-width mode left-pads the value with zeros when it is shorter than the specified | |
width and truncates the value when longer. Minimum-width mode works the same as fixed-width | |
except that it does not truncate values longer than the width. | |
When creating a `format` you can use any non-code characters as a separator. For example to | |
generate the string "1996-01-15T00:00:00" you could use `format`: "yyyy-mm-ddTHH:MM:SS". | |
Note that if you need to use a code character as a literal you can use the escape character | |
backslash. The string "1996y01m" can be produced with the format "yyyy\\ymm\\m". | |
""" | |
format(dt::TimeType,f::AbstractString;locale::AbstractString="english") = format(dt,DateFormat(f,locale)) | |
# vectorized | |
DateTime{T<:AbstractString}(Y::AbstractArray{T},format::AbstractString;locale::AbstractString="english") = DateTime(Y,DateFormat(format,locale)) | |
function DateTime{T<:AbstractString}(Y::AbstractArray{T},df::DateFormat=ISODateTimeFormat) | |
return reshape(DateTime[DateTime(parse(y,df)...) for y in Y], size(Y)) | |
end | |
Date{T<:AbstractString}(Y::AbstractArray{T},format::AbstractString;locale::AbstractString="english") = Date(Y,DateFormat(format,locale)) | |
function Date{T<:AbstractString}(Y::AbstractArray{T},df::DateFormat=ISODateFormat) | |
return reshape(Date[Date(parse(y,df)...) for y in Y], size(Y)) | |
end | |
format{T<:TimeType}(Y::AbstractArray{T},format::AbstractString;locale::AbstractString="english") = Dates.format(Y,DateFormat(format,locale)) | |
function format(Y::AbstractArray{Date},df::DateFormat=ISODateFormat) | |
return reshape([Dates.format(y,df) for y in Y], size(Y)) | |
end | |
function format(Y::AbstractArray{DateTime},df::DateFormat=ISODateTimeFormat) | |
return reshape([Dates.format(y,df) for y in Y], size(Y)) | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
#Period types | |
value(x::Period) = x.value | |
# The default constructors for Periods work well in almost all cases | |
# P(x) = new((convert(Int64,x)) | |
# The following definitions are for Period-specific safety | |
for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond) | |
period_str = string(period) | |
accessor_str = lowercase(period_str) | |
# Convenience method for show() | |
@eval _units(x::$period) = " " * $accessor_str * (abs(value(x)) == 1 ? "" : "s") | |
# periodisless | |
@eval periodisless(x::$period,y::$period) = value(x) < value(y) | |
# AbstractString parsing (mainly for IO code) | |
@eval $period(x::AbstractString) = $period(Base.parse(Int64,x)) | |
# Period accessors | |
typ_str = period in (:Hour, :Minute, :Second, :Millisecond) ? "DateTime" : "TimeType" | |
description = typ_str == "TimeType" ? "`Date` or `DateTime`" : "`$typ_str`" | |
reference = period == :Week ? " For details see [`$accessor_str(::$typ_str)`](:func:`$accessor_str`)." : "" | |
@eval begin | |
@doc """ | |
$($period_str)(dt::$($typ_str)) -> $($period_str) | |
The $($accessor_str) part of a $($description) as a `$($period_str)`.$($reference) | |
""" -> | |
$period(dt::$(Symbol(typ_str))) = $period($(Symbol(accessor_str))(dt)) | |
@doc """ | |
$($period_str)(v) | |
Construct a `$($period_str)` object with the given `v` value. Input must be | |
losslessly convertible to an `Int64`. | |
""" $period(v) | |
end | |
end | |
# Now we're safe to define Period-Number conversions | |
# Anything an Int64 can convert to, a Period can convert to | |
Base.convert{T<:Number}(::Type{T},x::Period) = convert(T,value(x)) | |
Base.convert{T<:Period}(::Type{T},x::Real) = T(x) | |
#Print/show/traits | |
Base.string{P<:Period}(x::P) = string(value(x),_units(x)) | |
Base.show(io::IO,x::Period) = print(io,string(x)) | |
Base.zero{P<:Period}(::Union{Type{P},P}) = P(0) | |
Base.one{P<:Period}(::Union{Type{P},P}) = P(1) | |
Base.typemin{P<:Period}(::Type{P}) = P(typemin(Int64)) | |
Base.typemax{P<:Period}(::Type{P}) = P(typemax(Int64)) | |
# Default values (as used by TimeTypes) | |
""" | |
default(p::Period) -> Period | |
Returns a sensible "default" value for the input Period by returning `one(p)` for Year, | |
Month, and Day, and `zero(p)` for Hour, Minute, Second, and Millisecond. | |
""" | |
function default end | |
default{T<:DatePeriod}(p::Union{T,Type{T}}) = one(p) | |
default{T<:TimePeriod}(p::Union{T,Type{T}}) = zero(p) | |
(-){P<:Period}(x::P) = P(-value(x)) | |
Base.isless{P<:Period}(x::P,y::P) = isless(value(x),value(y)) | |
=={P<:Period}(x::P,y::P) = value(x) == value(y) | |
# Period Arithmetic, grouped by dimensionality: | |
import Base: div, fld, mod, rem, gcd, lcm, +, -, *, /, %, .+, .-, .*, .% | |
for op in (:+,:-,:lcm,:gcd) | |
@eval ($op){P<:Period}(x::P,y::P) = P(($op)(value(x),value(y))) | |
end | |
for op in (:/,:div,:fld) | |
@eval begin | |
($op){P<:Period}(x::P,y::P) = ($op)(value(x),value(y)) | |
($op){P<:Period}(x::P,y::Real) = P(($op)(value(x),Int64(y))) | |
end | |
end | |
for op in (:rem,:mod) | |
@eval begin | |
($op){P<:Period}(x::P,y::P) = P(($op)(value(x),value(y))) | |
($op){P<:Period}(x::P,y::Real) = P(($op)(value(x),Int64(y))) | |
end | |
end | |
/{P<:Period}(X::StridedArray{P}, y::P) = X ./ y | |
%{P<:Period}(X::StridedArray{P}, y::P) = X .% y | |
*{P<:Period}(x::P,y::Real) = P(value(x) * Int64(y)) | |
*(y::Real,x::Period) = x * y | |
.*{P<:Period}(y::Real, X::StridedArray{P}) = X .* y | |
for (op,Ty,Tz) in ((:.*,Real,:P), | |
(:./,:P,Float64), (:./,Real,:P), | |
(:div,:P,Int64), (:div,Integer,:P), | |
(:.%,:P,:P), | |
(:mod,:P,:P)) | |
sop = string(op) | |
op_ = sop[1] == '.' ? Symbol(sop[2:end]) : op | |
@eval begin | |
function ($op){P<:Period}(X::StridedArray{P},y::$Ty) | |
Z = similar(X, $Tz) | |
for (Idst, Isrc) in zip(eachindex(Z), eachindex(X)) | |
@inbounds Z[Idst] = ($op_)(X[Isrc],y) | |
end | |
return Z | |
end | |
end | |
end | |
# intfuncs | |
Base.gcdx{T<:Period}(a::T,b::T) = ((g,x,y)=gcdx(value(a),value(b)); return T(g),x,y) | |
Base.abs{T<:Period}(a::T) = T(abs(value(a))) | |
periodisless(::Period,::Year) = true | |
periodisless(::Period,::Month) = true | |
periodisless(::Year,::Month) = false | |
periodisless(::Period,::Week) = true | |
periodisless(::Year,::Week) = false | |
periodisless(::Month,::Week) = false | |
periodisless(::Period,::Day) = true | |
periodisless(::Year,::Day) = false | |
periodisless(::Month,::Day) = false | |
periodisless(::Week,::Day) = false | |
periodisless(::Period,::Hour) = false | |
periodisless(::Minute,::Hour) = true | |
periodisless(::Second,::Hour) = true | |
periodisless(::Millisecond,::Hour) = true | |
periodisless(::Period,::Minute) = false | |
periodisless(::Second,::Minute) = true | |
periodisless(::Millisecond,::Minute) = true | |
periodisless(::Period,::Second) = false | |
periodisless(::Millisecond,::Second) = true | |
periodisless(::Period,::Millisecond) = false | |
# return (next coarser period, conversion factor): | |
coarserperiod{P<:Period}(::Type{P}) = (P,1) | |
coarserperiod(::Type{Millisecond}) = (Second,1000) | |
coarserperiod(::Type{Second}) = (Minute,60) | |
coarserperiod(::Type{Minute}) = (Hour,60) | |
coarserperiod(::Type{Hour}) = (Day,24) | |
coarserperiod(::Type{Day}) = (Week,7) | |
coarserperiod(::Type{Month}) = (Year,12) | |
# Stores multiple periods in greatest to least order by type, not values, | |
# canonicalized to eliminate zero periods, merge equal period types, | |
# and convert more-precise periods to less-precise periods when possible | |
""" | |
CompoundPeriod | |
A `CompoundPeriod` is useful for expressing time periods that are not a fixed multiple of | |
smaller periods. For example, \"a year and a day\" is not a fixed number of days, but can | |
be expressed using a `CompoundPeriod`. In fact, a `CompoundPeriod` is automatically | |
generated by addition of different period types, e.g. `Year(1) + Day(1)` produces a | |
`CompoundPeriod` result. | |
""" | |
type CompoundPeriod <: AbstractTime | |
periods::Array{Period,1} | |
function CompoundPeriod(p::Vector{Period}) | |
n = length(p) | |
if n > 1 | |
sort!(p, rev=true, lt=periodisless) | |
# canonicalize p by merging equal period types and removing zeros | |
i = j = 1 | |
while j <= n | |
k = j+1 | |
while k <= n | |
if typeof(p[j]) == typeof(p[k]) | |
p[j] += p[k] | |
k += 1 | |
else | |
break | |
end | |
end | |
if p[j] != zero(p[j]) | |
p[i] = p[j] | |
i += 1 | |
end | |
j = k | |
end | |
n = i - 1 # new length | |
elseif n == 1 && value(p[1]) == 0 | |
p = Period[] | |
n = 0 | |
end | |
# canonicalize Periods by pushing "overflow" into a coarser period. | |
if n > 0 | |
pc = sizehint!(Period[], n) | |
P = typeof(p[n]) | |
v = value(p[n]) | |
i = n - 1 | |
while true | |
Pc, f = coarserperiod(P) | |
if i > 0 && typeof(p[i]) == P | |
v += value(p[i]) | |
i -= 1 | |
end | |
v0 = f == 1 ? v : rem(v, f) | |
v0 != 0 && push!(pc, P(v0)) | |
if v != v0 | |
P = Pc | |
v = div(v - v0, f) | |
elseif i > 0 | |
P = typeof(p[i]) | |
v = value(p[i]) | |
i -= 1 | |
else | |
break | |
end | |
end | |
p = reverse!(pc) | |
n = length(p) | |
else | |
return new(resize!(p, n)) | |
end | |
# reduce the amount of mixed positive/negative Periods. | |
if n > 0 | |
pc = sizehint!(Period[], n) | |
i = n | |
while i > 0 | |
j = i | |
# Determine sign of the largest period in this group which | |
# can be converted into via coarserperiod. | |
last = Union{} | |
current = typeof(p[i]) | |
while i > 0 && current != last | |
if typeof(p[i]) == current | |
i -= 1 | |
end | |
last, current = current, coarserperiod(current)[1] | |
end | |
s = sign(value(p[i + 1])) | |
# Adjust all the periods in the group based upon the | |
# largest period sign. | |
P = typeof(p[j]) | |
v = 0 | |
while j > i | |
Pc, f = coarserperiod(P) | |
if j > 0 && typeof(p[j]) == P | |
v += value(p[j]) | |
j -= 1 | |
end | |
v0 = f == 1 ? v : mod(v, f * s) | |
v0 != 0 && push!(pc, P(v0)) | |
if v != v0 | |
P = Pc | |
v = div(v - v0, f) | |
elseif j > 0 | |
P = typeof(p[j]) | |
v = 0 | |
else | |
break | |
end | |
end | |
end | |
p = reverse!(pc) | |
end | |
return new(p) | |
end | |
end | |
""" | |
CompoundPeriod(periods) -> CompoundPeriod | |
Construct a `CompoundPeriod` from a `Vector` of `Period`s. The constructor will | |
automatically simplify the periods into a canonical form according to the following rules: | |
* All `Period`s of the same type will be added together | |
* Any `Period` large enough be partially representable by a coarser `Period` will be broken | |
into multiple `Period`s (eg. `Hour(30)` becomes `Day(1) + Hour(6)`) | |
* `Period`s with opposite signs will be combined when possible | |
(eg. `Hour(1) - Day(1)` becomes `-Hour(23)`) | |
Due to the canonicalization, `CompoundPeriod` is also useful for converting time periods | |
into more human-comprehensible forms. | |
# Examples | |
```julia | |
julia> Dates.CompoundPeriod([Dates.Hour(12), Dates.Hour(13)]) | |
1 day, 1 hour | |
julia> Dates.CompoundPeriod([Dates.Hour(-1), Dates.Minute(1)]) | |
-59 minutes | |
julia> Dates.CompoundPeriod([Dates.Month(1), Dates.Week(-2)]) | |
1 month, -2 weeks | |
julia> Dates.CompoundPeriod(Dates.Minute(50000))) | |
4 weeks, 6 days, 17 hours, 20 minutes | |
``` | |
""" | |
CompoundPeriod{P<:Period}(p::Vector{P}) = CompoundPeriod(Array{Period}(p)) | |
Base.convert(::Type{CompoundPeriod}, x::Period) = CompoundPeriod(Period[x]) | |
function Base.string(x::CompoundPeriod) | |
if isempty(x.periods) | |
return "empty period" | |
else | |
s = "" | |
for p in x.periods | |
s *= ", " * string(p) | |
end | |
return s[3:end] | |
end | |
end | |
Base.show(io::IO,x::CompoundPeriod) = print(io,string(x)) | |
# E.g. Year(1) + Day(1) | |
(+)(x::Period,y::Period) = CompoundPeriod(Period[x,y]) | |
(+)(x::CompoundPeriod,y::Period) = CompoundPeriod(vcat(x.periods,y)) | |
(+)(y::Period,x::CompoundPeriod) = x + y | |
(+)(x::CompoundPeriod,y::CompoundPeriod) = CompoundPeriod(vcat(x.periods,y.periods)) | |
# E.g. Year(1) - Month(1) | |
(-)(x::Period,y::Period) = CompoundPeriod(Period[x,-y]) | |
(-)(x::CompoundPeriod,y::Period) = CompoundPeriod(vcat(x.periods,-y)) | |
(-)(x::CompoundPeriod) = CompoundPeriod(-x.periods) | |
(-)(y::Union{Period,CompoundPeriod},x::CompoundPeriod) = (-x) + y | |
GeneralPeriod = Union{Period,CompoundPeriod} | |
(+)(x::GeneralPeriod) = x | |
(+){P<:GeneralPeriod}(x::StridedArray{P}) = x | |
for op in (:.+, :.-) | |
op_ = Symbol(string(op)[2:end]) | |
@eval begin | |
function ($op){P<:GeneralPeriod}(X::StridedArray{P},y::GeneralPeriod) | |
Z = similar(X, CompoundPeriod) | |
for (Idst, Isrc) in zip(eachindex(Z), eachindex(X)) | |
@inbounds Z[Idst] = ($op_)(X[Isrc],y) | |
end | |
return Z | |
end | |
($op){P<:GeneralPeriod}(x::GeneralPeriod,Y::StridedArray{P}) = ($op)(Y,x) |> ($op_) | |
($op_){P<:GeneralPeriod}(x::GeneralPeriod,Y::StridedArray{P}) = ($op)(Y,x) |> ($op_) | |
($op_){P<:GeneralPeriod}(Y::StridedArray{P},x::GeneralPeriod) = ($op)(Y,x) | |
($op_){P<:GeneralPeriod, Q<:GeneralPeriod}(X::StridedArray{P}, Y::StridedArray{Q}) = | |
reshape(CompoundPeriod[($op_)(x,y) for (x,y) in zip(X, Y)], promote_shape(size(X),size(Y))) | |
end | |
end | |
(==)(x::CompoundPeriod, y::Period) = x == CompoundPeriod(y) | |
(==)(y::Period, x::CompoundPeriod) = x == y | |
(==)(x::CompoundPeriod, y::CompoundPeriod) = x.periods == y.periods | |
# Capture TimeType+-Period methods | |
(+)(a::TimeType,b::Period,c::Period) = (+)(a,b+c) | |
(-)(a::TimeType,b::Period,c::Period) = (-)(a,b-c) | |
(+)(a::TimeType,b::Period,c::Period,d::Period...) = (+)((+)(a,b+c),d...) | |
(-)(a::TimeType,b::Period,c::Period,d::Period...) = (-)((-)(a,b-c),d...) | |
function (+)(x::TimeType,y::CompoundPeriod) | |
for p in y.periods | |
x += p | |
end | |
return x | |
end | |
(+)(x::CompoundPeriod,y::TimeType) = y + x | |
function (-)(x::TimeType,y::CompoundPeriod) | |
for p in y.periods | |
x -= p | |
end | |
return x | |
end | |
(-)(x::CompoundPeriod,y::TimeType) = y - x | |
# Fixed-value Periods (periods corresponding to a well-defined time interval, | |
# as opposed to variable calendar intervals like Year). | |
typealias FixedPeriod Union{Week,Day,Hour,Minute,Second,Millisecond} | |
# like div but throw an error if remainder is nonzero | |
function divexact(x,y) | |
q,r = divrem(x, y) | |
r == 0 || throw(InexactError()) | |
return q | |
end | |
# FixedPeriod conversions and promotion rules | |
const fixedperiod_conversions = [(Week,7),(Day,24),(Hour,60),(Minute,60),(Second,1000),(Millisecond,1)] | |
for i = 1:length(fixedperiod_conversions) | |
(T,n) = fixedperiod_conversions[i] | |
N = 1 | |
for j = i-1:-1:1 # less-precise periods | |
(Tc,nc) = fixedperiod_conversions[j] | |
N *= nc | |
vmax = typemax(Int64) ÷ N | |
vmin = typemin(Int64) ÷ N | |
@eval function Base.convert(::Type{$T}, x::$Tc) | |
$vmin ≤ value(x) ≤ $vmax || throw(InexactError()) | |
return $T(value(x)*$N) | |
end | |
end | |
N = n | |
for j = i+1:length(fixedperiod_conversions) # more-precise periods | |
(Tc,nc) = fixedperiod_conversions[j] | |
@eval Base.convert(::Type{$T}, x::$Tc) = $T(divexact(value(x), $N)) | |
@eval Base.promote_rule(::Type{$T},::Type{$Tc}) = $Tc | |
N *= nc | |
end | |
end | |
# have to declare thusly so that diagonal dispatch above takes precedence: | |
(==){T<:FixedPeriod,S<:FixedPeriod}(x::T,y::S) = (==)(promote(x,y)...) | |
Base.isless{T<:FixedPeriod,S<:FixedPeriod}(x::T,y::S) = isless(promote(x,y)...) | |
# other periods with fixed conversions but which aren't fixed time periods | |
typealias OtherPeriod Union{Month,Year} | |
let vmax = typemax(Int64) ÷ 12, vmin = typemin(Int64) ÷ 12 | |
@eval function Base.convert(::Type{Month}, x::Year) | |
$vmin ≤ value(x) ≤ $vmax || throw(InexactError()) | |
Month(value(x)*12) | |
end | |
end | |
Base.convert(::Type{Year}, x::Month) = Year(divexact(value(x),12)) | |
Base.promote_rule(::Type{Year}, ::Type{Month}) = Month | |
(==){T<:OtherPeriod,S<:OtherPeriod}(x::T,y::S) = (==)(promote(x,y)...) | |
Base.isless{T<:OtherPeriod,S<:OtherPeriod}(x::T,y::S) = isless(promote(x,y)...) | |
# truncating conversions to milliseconds and days: | |
toms(c::Millisecond) = value(c) | |
toms(c::Second) = 1000*value(c) | |
toms(c::Minute) = 60000*value(c) | |
toms(c::Hour) = 3600000*value(c) | |
toms(c::Day) = 86400000*value(c) | |
toms(c::Week) = 604800000*value(c) | |
toms(c::Month) = 86400000.0*30.436875*value(c) | |
toms(c::Year) = 86400000.0*365.2425*value(c) | |
toms(c::CompoundPeriod) = isempty(c.periods)?0.0 : Float64(sum(toms,c.periods)) | |
days(c::Millisecond) = div(value(c),86400000) | |
days(c::Second) = div(value(c),86400) | |
days(c::Minute) = div(value(c),1440) | |
days(c::Hour) = div(value(c),24) | |
days(c::Day) = value(c) | |
days(c::Week) = 7*value(c) | |
days(c::Year) = 365.2425*value(c) | |
days(c::Month) = 30.436875*value(c) | |
days(c::CompoundPeriod) = isempty(c.periods)?0.0 : Float64(sum(days,c.periods)) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Date functions | |
### Core query functions | |
# Monday = 1....Sunday = 7 | |
dayofweek(days) = mod1(days,7) | |
# Number of days in year | |
""" | |
daysinyear(dt::TimeType) -> Int | |
Returns 366 if the year of `dt` is a leap year, otherwise returns 365. | |
""" | |
daysinyear(y) = 365 + isleapyear(y) | |
# Day of the year | |
const MONTHDAYS = [0,31,59,90,120,151,181,212,243,273,304,334] | |
dayofyear(y,m,d) = MONTHDAYS[m] + d + (m > 2 && isleapyear(y)) | |
### Days of the Week | |
""" | |
dayofweek(dt::TimeType) -> Int64 | |
Returns the day of the week as an `Int64` with `1 = Monday, 2 = Tuesday, etc.`. | |
""" | |
dayofweek(dt::TimeType) = dayofweek(days(dt)) | |
const Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday = 1,2,3,4,5,6,7 | |
const Mon,Tue,Wed,Thu,Fri,Sat,Sun = 1,2,3,4,5,6,7 | |
const english_daysofweek = Dict(1=>"Monday",2=>"Tuesday",3=>"Wednesday", | |
4=>"Thursday",5=>"Friday",6=>"Saturday",7=>"Sunday") | |
const VALUETODAYOFWEEK = Dict{String,Dict{Int,String}}("english"=>english_daysofweek) | |
const english_daysofweekabbr = Dict(1=>"Mon",2=>"Tue",3=>"Wed", | |
4=>"Thu",5=>"Fri",6=>"Sat",7=>"Sun") | |
const VALUETODAYOFWEEKABBR = Dict{String,Dict{Int,String}}("english"=>english_daysofweekabbr) | |
dayname(dt::Integer;locale::AbstractString="english") = VALUETODAYOFWEEK[locale][dt] | |
""" | |
dayabbr(dt::TimeType; locale="english") -> AbstractString | |
Return the abbreviated name corresponding to the day of the week of the `Date` or `DateTime` | |
in the given `locale`. | |
""" | |
dayabbr(dt::Integer;locale::AbstractString="english") = VALUETODAYOFWEEKABBR[locale][dt] | |
""" | |
dayname(dt::TimeType; locale="english") -> AbstractString | |
Return the full day name corresponding to the day of the week of the `Date` or `DateTime` in | |
the given `locale`. | |
""" | |
dayname(dt::TimeType;locale::AbstractString="english") = VALUETODAYOFWEEK[locale][dayofweek(dt)] | |
dayabbr(dt::TimeType;locale::AbstractString="english") = VALUETODAYOFWEEKABBR[locale][dayofweek(dt)] | |
# Convenience methods for each day | |
ismonday(dt::TimeType) = dayofweek(dt) == Mon | |
istuesday(dt::TimeType) = dayofweek(dt) == Tue | |
iswednesday(dt::TimeType) = dayofweek(dt) == Wed | |
isthursday(dt::TimeType) = dayofweek(dt) == Thu | |
isfriday(dt::TimeType) = dayofweek(dt) == Fri | |
issaturday(dt::TimeType) = dayofweek(dt) == Sat | |
issunday(dt::TimeType) = dayofweek(dt) == Sun | |
# i.e. 1st Monday? 2nd Monday? 3rd Wednesday? 5th Sunday? | |
""" | |
dayofweekofmonth(dt::TimeType) -> Int | |
For the day of week of `dt`, returns which number it is in `dt`'s month. So if the day of | |
the week of `dt` is Monday, then `1 = First Monday of the month, 2 = Second Monday of the | |
month, etc.` In the range 1:5. | |
""" | |
dayofweekofmonth(dt::TimeType) = (d = day(dt); return d < 8 ? 1 : | |
d < 15 ? 2 : d < 22 ? 3 : d < 29 ? 4 : 5) | |
# Total number of a day of week in the month | |
# e.g. are there 4 or 5 Mondays in this month? | |
const TWENTYNINE = IntSet([1,8,15,22,29]) | |
const THIRTY = IntSet([1,2,8,9,15,16,22,23,29,30]) | |
const THIRTYONE = IntSet([1,2,3,8,9,10,15,16,17,22,23,24,29,30,31]) | |
""" | |
daysofweekinmonth(dt::TimeType) -> Int | |
For the day of week of `dt`, returns the total number of that day of the week in `dt`'s | |
month. Returns 4 or 5. Useful in temporal expressions for specifying the last day of a week | |
in a month by including `dayofweekofmonth(dt) == daysofweekinmonth(dt)` in the adjuster | |
function. | |
""" | |
function daysofweekinmonth(dt::TimeType) | |
y,m,d = yearmonthday(dt) | |
ld = daysinmonth(y,m) | |
return ld == 28 ? 4 : ld == 29 ? ((d in TWENTYNINE) ? 5 : 4) : | |
ld == 30 ? ((d in THIRTY) ? 5 : 4) : | |
(d in THIRTYONE) ? 5 : 4 | |
end | |
### Months | |
const January,February,March,April,May,June = 1,2,3,4,5,6 | |
const July,August,September,October,November,December = 7,8,9,10,11,12 | |
const Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec = 1,2,3,4,5,6,7,8,9,10,11,12 | |
const english_months = Dict(1=>"January",2=>"February",3=>"March",4=>"April", | |
5=>"May",6=>"June",7=>"July",8=>"August",9=>"September", | |
10=>"October",11=>"November",12=>"December") | |
const VALUETOMONTH = Dict{String,Dict{Int,String}}("english"=>english_months) | |
const englishabbr_months = Dict(1=>"Jan",2=>"Feb",3=>"Mar",4=>"Apr", | |
5=>"May",6=>"Jun",7=>"Jul",8=>"Aug",9=>"Sep", | |
10=>"Oct",11=>"Nov",12=>"Dec") | |
const VALUETOMONTHABBR = Dict{String,Dict{Int,String}}("english"=>englishabbr_months) | |
monthname(dt::Integer;locale::AbstractString="english") = VALUETOMONTH[locale][dt] | |
monthabbr(dt::Integer;locale::AbstractString="english") = VALUETOMONTHABBR[locale][dt] | |
""" | |
monthname(dt::TimeType; locale="english") -> AbstractString | |
Return the full name of the month of the `Date` or `DateTime` in the given `locale`. | |
""" | |
monthname(dt::TimeType;locale::AbstractString="english") = VALUETOMONTH[locale][month(dt)] | |
""" | |
monthabbr(dt::TimeType; locale="english") -> AbstractString | |
Return the abbreviated month name of the `Date` or `DateTime` in the given `locale`. | |
""" | |
monthabbr(dt::TimeType;locale::AbstractString="english") = VALUETOMONTHABBR[locale][month(dt)] | |
""" | |
daysinmonth(dt::TimeType) -> Int | |
Returns the number of days in the month of `dt`. Value will be 28, 29, 30, or 31. | |
""" | |
daysinmonth(dt::TimeType) = ((y,m) = yearmonth(dt); return daysinmonth(y,m)) | |
### Years | |
""" | |
isleapyear(dt::TimeType) -> Bool | |
Returns `true` if the year of `dt` is a leap year. | |
""" | |
isleapyear(dt::TimeType) = isleapyear(year(dt)) | |
""" | |
dayofyear(dt::TimeType) -> Int | |
Returns the day of the year for `dt` with January 1st being day 1. | |
""" | |
dayofyear(dt::TimeType) = ((y,m,d) = yearmonthday(dt); return dayofyear(y,m,d)) | |
daysinyear(dt::TimeType) = 365 + isleapyear(dt) | |
### Quarters | |
""" | |
quarterofyear(dt::TimeType) -> Int | |
Returns the quarter that `dt` resides in. Range of value is 1:4. | |
""" | |
function quarterofyear(dt::TimeType) | |
m = month(dt) | |
return m < 4 ? 1 : m < 7 ? 2 : m < 10 ? 3 : 4 | |
end | |
const QUARTERDAYS = [0,90,181,273] | |
""" | |
dayofquarter(dt::TimeType) -> Int | |
Returns the day of the current quarter of `dt`. Range of value is 1:92. | |
""" | |
dayofquarter(dt::TimeType) = dayofyear(dt) - QUARTERDAYS[quarterofyear(dt)] | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Date/DateTime Ranges | |
# Override default step; otherwise it would be Millisecond(1) | |
Base.colon{T<:DateTime}(start::T, stop::T) = StepRange(start, Day(1), stop) | |
# Given a start and end date, how many steps/periods are in between | |
guess(a::DateTime,b::DateTime,c) = floor(Int64,(Int128(b) - Int128(a))/toms(c)) | |
guess(a::Date,b::Date,c) = Int64(div(Int64(b - a),days(c))) | |
function len(a,b,c) | |
lo, hi, st = min(a,b), max(a,b), abs(c) | |
i = guess(a,b,c)-1 | |
while lo+st*i <= hi | |
i += 1 | |
end | |
return i-1 | |
end | |
Base.length{T<:TimeType}(r::StepRange{T}) = isempty(r) ? 0 : len(r.start,r.stop,r.step) + 1 | |
# Period ranges hook into Int64 overflow detection | |
Base.length{P<:Period}(r::StepRange{P}) = length(StepRange(value(r.start),value(r.step),value(r.stop))) | |
# Used to calculate the last valid date in the range given the start, stop, and step | |
# last = stop - steprem(start,stop,step) | |
Base.steprem{T<:TimeType}(a::T,b::T,c) = b - (a + c*len(a,b,c)) | |
import Base.in | |
function in{T<:TimeType}(x::T, r::StepRange{T}) | |
n = len(first(r),x,step(r)) + 1 | |
n >= 1 && n <= length(r) && r[n] == x | |
end | |
Base.start{T<:TimeType}(r::StepRange{T}) = 0 | |
Base.next{T<:TimeType}(r::StepRange{T}, i::Int) = (r.start+r.step*i,i+1) | |
Base.done{T<:TimeType,S<:Period}(r::StepRange{T,S}, i::Integer) = length(r) <= i | |
.+{T<:TimeType}(x::Period, r::Range{T}) = (x+first(r)):step(r):(x+last(r)) | |
.+{T<:TimeType}(r::Range{T},x::Period) = x .+ r | |
+{T<:TimeType}(r::Range{T},x::Period) = x .+ r | |
+{T<:TimeType}(x::Period,r::Range{T}) = x .+ r | |
.-{T<:TimeType}(r::Range{T},x::Period) = (first(r)-x):step(r):(last(r)-x) | |
-{T<:TimeType}(r::Range{T},x::Period) = r .- x | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# The epochs used for date rounding are based ISO 8601's "year zero" notation | |
const DATEEPOCH = value(Date(0)) | |
const DATETIMEEPOCH = value(DateTime(0)) | |
# According to ISO 8601, the first day of the first week of year 0000 is 0000-01-03 | |
const WEEKEPOCH = value(Date(0, 1, 3)) | |
""" | |
epochdays2date(days) -> Date | |
Takes the number of days since the rounding epoch (`0000-01-01T00:00:00`) and returns the | |
corresponding `Date`. | |
""" | |
epochdays2date(i) = Date(UTD(DATEEPOCH + Int64(i))) | |
""" | |
epochms2datetime(milliseconds) -> DateTime | |
Takes the number of milliseconds since the rounding epoch (`0000-01-01T00:00:00`) and | |
returns the corresponding `DateTime`. | |
""" | |
epochms2datetime(i) = DateTime(UTM(DATETIMEEPOCH + Int64(i))) | |
""" | |
date2epochdays(dt::Date) -> Int64 | |
Takes the given `Date` and returns the number of days since the rounding epoch | |
(`0000-01-01T00:00:00`) as an `Int64`. | |
""" | |
date2epochdays(dt::Date) = value(dt) - DATEEPOCH | |
""" | |
datetime2epochms(dt::DateTime) -> Int64 | |
Takes the given `DateTime` and returns the number of milliseconds since the rounding epoch | |
(`0000-01-01T00:00:00`) as an `Int64`. | |
""" | |
datetime2epochms(dt::DateTime) = value(dt) - DATETIMEEPOCH | |
function Base.floor(dt::Date, p::Year) | |
value(p) < 1 && throw(DomainError()) | |
years = year(dt) | |
return Date(years - mod(years, value(p))) | |
end | |
function Base.floor(dt::Date, p::Month) | |
value(p) < 1 && throw(DomainError()) | |
y, m = yearmonth(dt) | |
months_since_epoch = y * 12 + m - 1 | |
month_offset = months_since_epoch - mod(months_since_epoch, value(p)) | |
target_month = mod(month_offset, 12) + 1 | |
target_year = div(month_offset, 12) - (month_offset < 0 && target_month != 1) | |
return Date(target_year, target_month) | |
end | |
function Base.floor(dt::Date, p::Week) | |
value(p) < 1 && throw(DomainError()) | |
days = value(dt) - WEEKEPOCH | |
days = days - mod(days, value(Day(p))) | |
return Date(UTD(WEEKEPOCH + Int64(days))) | |
end | |
function Base.floor(dt::Date, p::Day) | |
value(p) < 1 && throw(DomainError()) | |
days = date2epochdays(dt) | |
return epochdays2date(days - mod(days, value(p))) | |
end | |
Base.floor(dt::DateTime, p::DatePeriod) = DateTime(Base.floor(Date(dt), p)) | |
function Base.floor(dt::DateTime, p::TimePeriod) | |
value(p) < 1 && throw(DomainError()) | |
milliseconds = datetime2epochms(dt) | |
return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p)))) | |
end | |
""" | |
floor(dt::TimeType, p::Period) -> TimeType | |
Returns the nearest `Date` or `DateTime` less than or equal to `dt` at resolution `p`. | |
For convenience, `p` may be a type instead of a value: `floor(dt, Dates.Hour)` is a shortcut | |
for `floor(dt, Dates.Hour(1))`. | |
```jldoctest | |
julia> floor(Date(1985, 8, 16), Dates.Month) | |
1985-08-01 | |
julia> floor(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15)) | |
2013-02-13T00:30:00 | |
julia> floor(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day) | |
2016-08-06T00:00:00 | |
``` | |
""" | |
Base.floor(::Dates.TimeType, ::Dates.Period) | |
""" | |
ceil(dt::TimeType, p::Period) -> TimeType | |
Returns the nearest `Date` or `DateTime` greater than or equal to `dt` at resolution `p`. | |
For convenience, `p` may be a type instead of a value: `ceil(dt, Dates.Hour)` is a shortcut | |
for `ceil(dt, Dates.Hour(1))`. | |
```jldoctest | |
julia> ceil(Date(1985, 8, 16), Dates.Month) | |
1985-09-01 | |
julia> ceil(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15)) | |
2013-02-13T00:45:00 | |
julia> ceil(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day) | |
2016-08-07T00:00:00 | |
``` | |
""" | |
function Base.ceil(dt::TimeType, p::Period) | |
f = floor(dt, p) | |
return (dt == f) ? f : f + p | |
end | |
""" | |
floorceil(dt::TimeType, p::Period) -> (TimeType, TimeType) | |
Simultaneously return the `floor` and `ceil` of a `Date` or `DateTime` at resolution `p`. | |
More efficient than calling both `floor` and `ceil` individually. | |
""" | |
function floorceil(dt::TimeType, p::Period) | |
f = floor(dt, p) | |
return f, (dt == f) ? f : f + p | |
end | |
""" | |
round(dt::TimeType, p::Period, [r::RoundingMode]) -> TimeType | |
Returns the `Date` or `DateTime` nearest to `dt` at resolution `p`. By default | |
(`RoundNearestTiesUp`), ties (e.g., rounding 9:30 to the nearest hour) will be rounded up. | |
For convenience, `p` may be a type instead of a value: `round(dt, Dates.Hour)` is a shortcut | |
for `round(dt, Dates.Hour(1))`. | |
```jldoctest | |
julia> round(Date(1985, 8, 16), Dates.Month) | |
1985-08-01 | |
julia> round(DateTime(2013, 2, 13, 0, 31, 20), Dates.Minute(15)) | |
2013-02-13T00:30:00 | |
julia> round(DateTime(2016, 8, 6, 12, 0, 0), Dates.Day) | |
2016-08-07T00:00:00 | |
``` | |
Valid rounding modes for `round(::TimeType, ::Period, ::RoundingMode)` are | |
`RoundNearestTiesUp` (default), `RoundDown` (`floor`), and `RoundUp` (`ceil`). | |
""" | |
function Base.round(dt::TimeType, p::Period, r::RoundingMode{:NearestTiesUp}) | |
f, c = floorceil(dt, p) | |
return (dt - f) < (c - dt) ? f : c | |
end | |
Base.round(dt::TimeType, p::Period, r::RoundingMode{:Down}) = Base.floor(dt, p) | |
Base.round(dt::TimeType, p::Period, r::RoundingMode{:Up}) = Base.ceil(dt, p) | |
# No implementation of other `RoundingMode`s: rounding to nearest "even" is skipped because | |
# "even" is not defined for Period; rounding toward/away from zero is skipped because ISO | |
# 8601's year 0000 is not really "zero". | |
Base.round(::TimeType, ::Period, ::RoundingMode) = throw(DomainError()) | |
# Default to RoundNearestTiesUp. | |
Base.round(dt::TimeType, p::Period) = Base.round(dt, p, RoundNearestTiesUp) | |
# Make rounding functions callable using Period types in addition to values. | |
Base.floor{T <: Period}(dt::TimeType, p::Type{T}) = Base.floor(dt, p(1)) | |
Base.ceil{T <: Period}(dt::TimeType, p::Type{T}) = Base.ceil(dt, p(1)) | |
function Base.round{T<:Period}(dt::TimeType, p::Type{T}, r::RoundingMode=RoundNearestTiesUp) | |
return Base.round(dt, p(1), r) | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
abstract AbstractTime | |
""" | |
Period | |
Year | |
Month | |
Week | |
Day | |
Hour | |
Minute | |
Second | |
Millisecond | |
`Period` types represent discrete, human representations of time. | |
""" | |
abstract Period <: AbstractTime | |
abstract DatePeriod <: Period | |
abstract TimePeriod <: Period | |
for T in (:Year,:Month,:Week,:Day) | |
@eval immutable $T <: DatePeriod | |
value::Int64 | |
$T(v::Number) = new(v) | |
end | |
end | |
for T in (:Hour,:Minute,:Second,:Millisecond) | |
@eval immutable $T <: TimePeriod | |
value::Int64 | |
$T(v::Number) = new(v) | |
end | |
end | |
""" | |
Year(v) | |
Month(v) | |
Week(v) | |
Day(v) | |
Hour(v) | |
Minute(v) | |
Second(v) | |
Millisecond(v) | |
Construct a `Period` type with the given `v` value. Input must be losslessly convertible | |
to an `Int64`. | |
""" | |
Period(v) | |
""" | |
Instant | |
`Instant` types represent integer-based, machine representations of time as continuous | |
timelines starting from an epoch. | |
""" | |
abstract Instant <: AbstractTime | |
""" | |
UTInstant{T} | |
The `UTInstant` represents a machine timeline based on UT time (1 day = one revolution of | |
the earth). The `T` is a `Period` parameter that indicates the resolution or precision of | |
the instant. | |
""" | |
immutable UTInstant{P<:Period} <: Instant | |
periods::P | |
end | |
# Convenience default constructors | |
UTM(x) = UTInstant(Millisecond(x)) | |
UTD(x) = UTInstant(Day(x)) | |
# Calendar types provide rules for interpretating instant | |
# timelines in human-readable form. | |
abstract Calendar <: AbstractTime | |
# ISOCalendar implements the ISO 8601 standard (en.wikipedia.org/wiki/ISO_8601) | |
# Notably based on the proleptic Gregorian calendar | |
# ISOCalendar provides interpretation rules for UTInstants to civil date and time parts | |
immutable ISOCalendar <: Calendar end | |
abstract TimeZone | |
immutable UTC <: TimeZone end | |
""" | |
TimeType | |
`TimeType` types wrap `Instant` machine instances to provide human representations of the | |
machine instant. Both `DateTime` and `Date` are subtypes of `TimeType`. | |
""" | |
abstract TimeType <: AbstractTime | |
""" | |
DateTime | |
`DateTime` wraps a `UTInstant{Millisecond}` and interprets it according to the proleptic | |
Gregorian calendar. | |
""" | |
immutable DateTime <: TimeType | |
instant::UTInstant{Millisecond} | |
DateTime(instant::UTInstant{Millisecond}) = new(instant) | |
end | |
""" | |
Date | |
`Date` wraps a `UTInstant{Day}` and interprets it according to the proleptic Gregorian calendar. | |
""" | |
immutable Date <: TimeType | |
instant::UTInstant{Day} | |
Date(instant::UTInstant{Day}) = new(instant) | |
end | |
# Fallback constructors | |
_c(x) = convert(Int64,x) | |
DateTime(y,m=1,d=1,h=0,mi=0,s=0,ms=0) = DateTime(_c(y),_c(m),_c(d),_c(h),_c(mi),_c(s),_c(ms)) | |
Date(y,m=1,d=1) = Date(_c(y),_c(m),_c(d)) | |
# Convert y,m,d to # of Rata Die days | |
# Works by shifting the beginning of the year to March 1, | |
# so a leap day is the very last day of the year | |
const SHIFTEDMONTHDAYS = [306,337,0,31,61,92,122,153,184,214,245,275] | |
function totaldays(y,m,d) | |
# If we're in Jan/Feb, shift the given year back one | |
z = m < 3 ? y - 1 : y | |
mdays = SHIFTEDMONTHDAYS[m] | |
# days + month_days + year_days | |
return d + mdays + 365z + fld(z,4) - fld(z,100) + fld(z,400) - 306 | |
end | |
# If the year is divisible by 4, except for every 100 years, except for every 400 years | |
isleapyear(y) = ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0) | |
# Number of days in month | |
const DAYSINMONTH = [31,28,31,30,31,30,31,31,30,31,30,31] | |
daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y)) | |
### CONSTRUCTORS ### | |
# Core constructors | |
""" | |
DateTime(y, [m, d, h, mi, s, ms]) -> DateTime | |
Construct a `DateTime` type by parts. Arguments must be convertible to `Int64`. | |
""" | |
function DateTime(y::Int64,m::Int64=1,d::Int64=1, | |
h::Int64=0,mi::Int64=0,s::Int64=0,ms::Int64=0) | |
0 < m < 13 || throw(ArgumentError("Month: $m out of range (1:12)")) | |
0 < d < daysinmonth(y,m)+1 || throw(ArgumentError("Day: $d out of range (1:$(daysinmonth(y,m)))")) | |
-1 < h < 24 || throw(ArgumentError("Hour: $h out of range (0:23)")) | |
-1 < mi < 60 || throw(ArgumentError("Minute: $mi out of range (0:59)")) | |
-1 < s < 60 || throw(ArgumentError("Second: $s out of range (0:59)")) | |
-1 < ms < 1000 || throw(ArgumentError("Millisecond: $ms out of range (0:999)")) | |
rata = ms + 1000*(s + 60mi + 3600h + 86400*totaldays(y,m,d)) | |
return DateTime(UTM(rata)) | |
end | |
""" | |
Date(y, [m, d]) -> Date | |
Construct a `Date` type by parts. Arguments must be convertible to `Int64`. | |
""" | |
function Date(y::Int64,m::Int64=1,d::Int64=1) | |
0 < m < 13 || throw(ArgumentError("Month: $m out of range (1:12)")) | |
0 < d < daysinmonth(y,m)+1 || throw(ArgumentError("Day: $d out of range (1:$(daysinmonth(y,m)))")) | |
return Date(UTD(totaldays(y,m,d))) | |
end | |
# Convenience constructors from Periods | |
function DateTime(y::Year,m::Month=Month(1),d::Day=Day(1), | |
h::Hour=Hour(0),mi::Minute=Minute(0), | |
s::Second=Second(0),ms::Millisecond=Millisecond(0)) | |
return DateTime(value(y),value(m),value(d), | |
value(h),value(mi),value(s),value(ms)) | |
end | |
Date(y::Year,m::Month=Month(1),d::Day=Day(1)) = Date(value(y),value(m),value(d)) | |
# To allow any order/combination of Periods | |
""" | |
DateTime(periods::Period...) -> DateTime | |
Construct a `DateTime` type by `Period` type parts. Arguments may be in any order. DateTime | |
parts not provided will default to the value of `Dates.default(period)`. | |
""" | |
function DateTime(periods::Period...) | |
y = Year(1); m = Month(1); d = Day(1) | |
h = Hour(0); mi = Minute(0); s = Second(0); ms = Millisecond(0) | |
for p in periods | |
isa(p, Year) && (y = p::Year) | |
isa(p, Month) && (m = p::Month) | |
isa(p, Day) && (d = p::Day) | |
isa(p, Hour) && (h = p::Hour) | |
isa(p, Minute) && (mi = p::Minute) | |
isa(p, Second) && (s = p::Second) | |
isa(p, Millisecond) && (ms = p::Millisecond) | |
end | |
return DateTime(y,m,d,h,mi,s,ms) | |
end | |
""" | |
Date(period::Period...) -> Date | |
Construct a `Date` type by `Period` type parts. Arguments may be in any order. `Date` parts | |
not provided will default to the value of `Dates.default(period)`. | |
""" | |
function Date(periods::Period...) | |
y = Year(1); m = Month(1); d = Day(1) | |
for p in periods | |
isa(p, Year) && (y = p::Year) | |
isa(p, Month) && (m = p::Month) | |
isa(p, Day) && (d = p::Day) | |
end | |
return Date(y,m,d) | |
end | |
# Traits, Equality | |
Base.isfinite{T<:TimeType}(::Union{Type{T},T}) = true | |
calendar(dt::DateTime) = ISOCalendar | |
calendar(dt::Date) = ISOCalendar | |
""" | |
eps(::DateTime) -> Millisecond | |
eps(::Date) -> Day | |
Returns `Millisecond(1)` for `DateTime` values and `Day(1)` for `Date` values. | |
""" | |
Base.eps | |
Base.eps(dt::DateTime) = Millisecond(1) | |
Base.eps(dt::Date) = Day(1) | |
Base.typemax(::Union{DateTime,Type{DateTime}}) = DateTime(146138512,12,31,23,59,59) | |
Base.typemin(::Union{DateTime,Type{DateTime}}) = DateTime(-146138511,1,1,0,0,0) | |
Base.typemax(::Union{Date,Type{Date}}) = Date(252522163911149,12,31) | |
Base.typemin(::Union{Date,Type{Date}}) = Date(-252522163911150,1,1) | |
# Date-DateTime promotion, isless, == | |
Base.promote_rule(::Type{Date},x::Type{DateTime}) = DateTime | |
Base.isless(x::Date,y::Date) = isless(value(x),value(y)) | |
Base.isless(x::DateTime,y::DateTime) = isless(value(x),value(y)) | |
Base.isless(x::TimeType,y::TimeType) = isless(promote(x,y)...) | |
==(x::TimeType,y::TimeType) = ===(promote(x,y)...) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# deep copying | |
# Note: deepcopy_internal(::Any, ::ObjectIdDict) is | |
# only exposed for specialization by libraries | |
deepcopy(x) = deepcopy_internal(x, ObjectIdDict())::typeof(x) | |
deepcopy_internal(x::Union{Symbol,Core.MethodInstance,Method,GlobalRef,DataType,Union,Task}, | |
stackdict::ObjectIdDict) = x | |
deepcopy_internal(x::Tuple, stackdict::ObjectIdDict) = | |
ntuple(i->deepcopy_internal(x[i], stackdict), length(x)) | |
deepcopy_internal(x::Module, stackdict::ObjectIdDict) = error("deepcopy of Modules not supported") | |
function deepcopy_internal(x::SimpleVector, stackdict::ObjectIdDict) | |
if haskey(stackdict, x) | |
return stackdict[x] | |
end | |
y = Core.svec(Any[deepcopy_internal(x[i], stackdict) for i = 1:length(x)]...) | |
stackdict[x] = y | |
return y | |
end | |
function deepcopy_internal(x::ANY, stackdict::ObjectIdDict) | |
T = typeof(x)::DataType | |
nf = nfields(T) | |
(isbits(T) || nf == 0) && return x | |
if haskey(stackdict, x) | |
return stackdict[x] | |
end | |
y = ccall(:jl_new_struct_uninit, Any, (Any,), T) | |
if T.mutable | |
stackdict[x] = y | |
end | |
for i in 1:nf | |
if isdefined(x,i) | |
ccall(:jl_set_nth_field, Void, (Any, Csize_t, Any), y, i-1, | |
deepcopy_internal(getfield(x,i), stackdict)) | |
end | |
end | |
return y::T | |
end | |
function deepcopy_internal(x::Array, stackdict::ObjectIdDict) | |
if haskey(stackdict, x) | |
return stackdict[x] | |
end | |
_deepcopy_array_t(x, eltype(x), stackdict) | |
end | |
function _deepcopy_array_t(x::ANY, T, stackdict::ObjectIdDict) | |
if isbits(T) | |
return (stackdict[x]=copy(x)) | |
end | |
dest = similar(x) | |
stackdict[x] = dest | |
for i = 1:(length(x)::Int) | |
if ccall(:jl_array_isassigned, Cint, (Any, Csize_t), x, i-1) != 0 | |
xi = ccall(:jl_arrayref, Any, (Any, Csize_t), x, i-1) | |
if !isbits(typeof(xi)) | |
xi = deepcopy_internal(xi, stackdict) | |
end | |
ccall(:jl_arrayset, Void, (Any, Any, Csize_t), dest, xi, i-1) | |
end | |
end | |
return dest | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Deprecated functions and objects | |
# | |
# Please add new deprecations at the bottom of the file. | |
# A function deprecated in a release will be removed in the next one. | |
# Please also add a reference to the pull request which introduced the | |
# deprecation. | |
# | |
# For simple cases where a direct replacement is available, use @deprecate: | |
# the first argument is the signature of the deprecated method, the second one | |
# is the call which replaces it. Remove the definition of the deprecated method | |
# and unexport it, as @deprecate takes care of calling the replacement | |
# and of exporting the function. | |
# | |
# For more complex cases, move the body of the deprecated method in this file, | |
# and call depwarn() directly from inside it. The symbol depwarn() expects is | |
# the name of the function, which is used to ensure that the deprecation warning | |
# is only printed the first time for each call place. | |
macro deprecate(old,new) | |
meta = Expr(:meta, :noinline) | |
if isa(old,Symbol) | |
oldname = Expr(:quote,old) | |
newname = Expr(:quote,new) | |
Expr(:toplevel, | |
Expr(:export,esc(old)), | |
:(function $(esc(old))(args...) | |
$meta | |
depwarn(string($oldname," is deprecated, use ",$newname," instead."), | |
$oldname) | |
$(esc(new))(args...) | |
end)) | |
elseif isa(old,Expr) && old.head == :call | |
remove_linenums!(new) | |
oldcall = sprint(io->show_unquoted(io,old)) | |
newcall = sprint(io->show_unquoted(io,new)) | |
oldsym = if isa(old.args[1],Symbol) | |
old.args[1] | |
elseif isa(old.args[1],Expr) && old.args[1].head == :curly | |
old.args[1].args[1] | |
else | |
error("invalid usage of @deprecate") | |
end | |
oldname = Expr(:quote, oldsym) | |
Expr(:toplevel, | |
Expr(:export,esc(oldsym)), | |
:($(esc(old)) = begin | |
$meta | |
depwarn(string($oldcall," is deprecated, use ",$newcall," instead."), | |
$oldname) | |
$(esc(new)) | |
end)) | |
else | |
error("invalid usage of @deprecate") | |
end | |
end | |
function depwarn(msg, funcsym) | |
opts = JLOptions() | |
if opts.depwarn > 0 | |
ln = Int(unsafe_load(cglobal(:jl_lineno, Cint))) | |
fn = unsafe_string(unsafe_load(cglobal(:jl_filename, Ptr{Cchar}))) | |
bt = backtrace() | |
caller = firstcaller(bt, funcsym) | |
if opts.depwarn == 1 # raise a warning | |
warn(msg, once=(caller != C_NULL), key=caller, bt=bt, | |
filename=fn, lineno=ln) | |
elseif opts.depwarn == 2 # raise an error | |
throw(ErrorException(msg)) | |
end | |
end | |
end | |
function firstcaller(bt::Array{Ptr{Void},1}, funcsym::Symbol) | |
# Identify the calling line | |
i = 1 | |
while i <= length(bt) | |
lkups = StackTraces.lookup(bt[i]) | |
i += 1 | |
for lkup in lkups | |
if lkup === StackTraces.UNKNOWN | |
continue | |
end | |
if lkup.func == funcsym | |
@goto found | |
end | |
end | |
end | |
@label found | |
if i <= length(bt) | |
return bt[i] | |
end | |
return C_NULL | |
end | |
deprecate(s::Symbol) = deprecate(current_module(), s) | |
deprecate(m::Module, s::Symbol) = ccall(:jl_deprecate_binding, Void, (Any, Any), m, s) | |
macro deprecate_binding(old, new) | |
Expr(:toplevel, | |
Expr(:export, esc(old)), | |
Expr(:const, Expr(:(=), esc(old), esc(new))), | |
Expr(:call, :deprecate, Expr(:quote, old))) | |
end | |
# 0.5 deprecations | |
for f in (:remotecall, :remotecall_fetch, :remotecall_wait) | |
@eval begin | |
@deprecate ($f)(w::LocalProcess, f::Function, args...) ($f)(f, w::LocalProcess, args...) | |
@deprecate ($f)(w::Worker, f::Function, args...) ($f)(f, w::Worker, args...) | |
@deprecate ($f)(id::Integer, f::Function, args...) ($f)(f, id::Integer, args...) | |
end | |
end | |
# 13232 | |
@deprecate with_bigfloat_precision setprecision | |
@deprecate set_bigfloat_precision(prec) setprecision(prec) | |
@deprecate get_bigfloat_precision() precision(BigFloat) | |
@deprecate set_rounding setrounding | |
@deprecate with_rounding setrounding | |
@deprecate get_rounding rounding | |
#13465 | |
@deprecate cov(x::AbstractVector; corrected=true, mean=Base.mean(x)) Base.covm(x, mean, corrected) | |
@deprecate cov(X::AbstractMatrix; vardim=1, corrected=true, mean=Base.mean(X, vardim)) Base.covm(X, mean, vardim, corrected) | |
@deprecate cov(x::AbstractVector, y::AbstractVector; corrected=true, mean=(Base.mean(x), Base.mean(y))) Base.covm(x, mean[1], y, mean[2], corrected) | |
@deprecate cov(X::AbstractVecOrMat, Y::AbstractVecOrMat; vardim=1, corrected=true, mean=(Base.mean(X, vardim), Base.mean(Y, vardim))) Base.covm(X, mean[1], Y, mean[2], vardim, corrected) | |
@deprecate cor(x::AbstractVector; mean=Base.mean(x)) Base.corm(x, mean) | |
@deprecate cor(X::AbstractMatrix; vardim=1, mean=Base.mean(X, vardim)) Base.corm(X, mean, vardim) | |
@deprecate cor(x::AbstractVector, y::AbstractVector; mean=(Base.mean(x), Base.mean(y))) Base.corm(x, mean[1], y, mean[2]) | |
@deprecate cor(X::AbstractVecOrMat, Y::AbstractVecOrMat; vardim=1, mean=(Base.mean(X, vardim), Base.mean(Y, vardim))) Base.corm(X, mean[1], Y, mean[2], vardim) | |
@deprecate_binding SparseMatrix SparseArrays | |
#13496 | |
@deprecate A_ldiv_B!(A::SparseMatrixCSC, B::StridedVecOrMat) A_ldiv_B!(factorize(A), B) | |
@deprecate chol(A::Number, ::Type{Val{:U}}) chol(A) | |
@deprecate chol(A::AbstractMatrix, ::Type{Val{:U}}) chol(A) | |
@deprecate chol(A::Number, ::Type{Val{:L}}) ctranspose(chol(A)) | |
@deprecate chol(A::AbstractMatrix, ::Type{Val{:L}}) ctranspose(chol(A)) | |
# Number updates | |
# rem1 is inconsistent for x==0: The result should both have the same | |
# sign as x, and should be non-zero. | |
function rem1{T<:Real}(x::T, y::T) | |
depwarn("`rem1(x,y)` is discontinued, as it cannot be defined consistently for `x==0`. Rewrite the expression using `mod1` instead.", :rem1) | |
rem(x-1,y)+1 | |
end | |
rem1(x::Real, y::Real) = rem1(promote(x,y)...) | |
export rem1 | |
# Filesystem module updates | |
@deprecate_binding FS Filesystem | |
isreadable(path...) = isreadable(stat(path...)) | |
iswritable(path...) = iswritable(stat(path...)) | |
isexecutable(path...) = isexecutable(stat(path...)) | |
function isreadable(st::Filesystem.StatStruct) | |
depwarn("isreadable is deprecated as it implied that the file would actually be readable by the user; consider using `isfile` instead. see also the system man page for `access`", :isreadable) | |
return (st.mode & 0o444) > 0 | |
end | |
function iswritable(st::Filesystem.StatStruct) | |
depwarn("iswritable is deprecated as it implied that the file would actually be writable by the user; consider using `isfile` instead. see also the system man page for `access`", :iswritable) | |
return (st.mode & 0o222) > 0 | |
end | |
function isexecutable(st::Filesystem.StatStruct) | |
depwarn("isexecutable is deprecated as it implied that the file would actually be executable by the user; consider using `isfile` instead. see also the system man page for `access`", :isexecutable) | |
return (st.mode & 0o111) > 0 | |
end | |
export isreadable, iswritable, isexecutable | |
@deprecate RemoteRef RemoteChannel | |
function tty_size() | |
depwarn("tty_size is deprecated. use `displaysize(io)` as a replacement", :tty_size) | |
if isdefined(Base, :active_repl) | |
os = REPL.outstream(Base.active_repl) | |
if isa(os, Terminals.TTYTerminal) | |
return displaysize(os) | |
end | |
end | |
if isdefined(Base, :STDOUT) | |
return displaysize(STDOUT) | |
end | |
return displaysize() | |
end | |
# Combinatorics functions that have been moved out of base (#13897) | |
# Note: only the two-argument form of factorial has been moved | |
for deprecatedfunc in [:combinations, :factorial, :prevprod, :levicivita, | |
:nthperm!, :nthperm, :parity, :partitions, :permutations] | |
@eval begin | |
$deprecatedfunc(args...) = error(string($deprecatedfunc, args, | |
" has been moved to the package Combinatorics.jl.\n", | |
"Run Pkg.add(\"Combinatorics\") to install Combinatorics on Julia v0.5-")) | |
export $deprecatedfunc | |
end | |
end | |
# Primes functions that have been moved out of base (#16481) | |
for deprecatedfunc in [:isprime, :primes, :primesmask, :factor] | |
@eval begin | |
$deprecatedfunc(args...) = error(string($deprecatedfunc, args, | |
" has been moved to the package Primes.jl.\n", | |
"Run Pkg.add(\"Primes\") to install Primes on Julia v0.5-")) | |
export $deprecatedfunc | |
end | |
end | |
#14335 | |
@deprecate super(T::DataType) supertype(T) | |
function with_output_limit(thk, lim::Bool=true) # thk is usually show() | |
depwarn("with_output_limit is deprecated. use `io = IOContext(io, :limit => lim)` as a replacement", :with_output_limit) | |
global _limit_output | |
last = _limit_output | |
_limit_output = lim | |
try | |
thk() | |
finally | |
_limit_output = last | |
end | |
end | |
#14555 | |
@deprecate_binding Coff_t Int64 | |
@deprecate_binding FileOffset Int64 | |
#14474 | |
macro boundscheck(yesno,blk) | |
depwarn("The meaning of `@boundscheck` has changed. It now indicates that the provided code block performs bounds checking, and may be elided when inbounds.", Symbol("@boundscheck")) | |
if yesno === true | |
:(@inbounds $(esc(blk))) | |
end | |
end | |
@deprecate parseip(str::AbstractString) parse(IPAddr, str) | |
#https://github.com/JuliaLang/julia/issues/14608 | |
@deprecate readall readstring | |
@deprecate readbytes read | |
@deprecate field_offset(x::DataType, idx) fieldoffset(x, idx+1) | |
@noinline function fieldoffsets(x::DataType) | |
depwarn("fieldoffsets is deprecated. use `map(idx->fieldoffset(x, idx), 1:nfields(x))` instead", :fieldoffsets) | |
nf = nfields(x) | |
offsets = Array{Int}(nf) | |
for i = 1:nf | |
offsets[i] = fieldoffset(x, i) | |
end | |
return offsets | |
end | |
export fieldoffsets | |
# 14766 | |
@deprecate write(io::IO, p::Ptr, nb::Integer) unsafe_write(io, p, nb) | |
@deprecate isgeneric(f) isa(f,Function) | |
# need to do this manually since the front end deprecates method defs of `call` | |
const call = @eval function(f, args...; kw...) | |
$(Expr(:meta, :noinline)) | |
depwarn("call(f,args...) is deprecated, use f(args...) instead.", :call) | |
f(args...; kw...) | |
end | |
export call | |
# Changed issym to issymmetric. #15192 | |
@deprecate issym issymmetric | |
# 15258 | |
@deprecate scale(α::Number, A::AbstractArray) α*A | |
@deprecate scale(A::AbstractArray, α::Number) A*α | |
@deprecate scale(A::AbstractMatrix, x::AbstractVector) A*Diagonal(x) | |
@deprecate scale(x::AbstractVector, A::AbstractMatrix) Diagonal(x)*A | |
# 1933 | |
@deprecate_binding SingleAsyncWork AsyncCondition | |
# #12872 | |
@deprecate istext istextmime | |
#15409 | |
# Deprecated definition of pmap with keyword arguments. | |
# deprecation warnings are in pmap.jl | |
# 15692 | |
typealias Func{N} Function | |
deprecate(:Func) | |
for (Fun, func) in [(:IdFun, :identity), | |
(:AbsFun, :abs), | |
(:Abs2Fun, :abs2), | |
(:ExpFun, :exp), | |
(:LogFun, :log), | |
(:ConjFun, :conj), | |
(:AndFun, :&), | |
(:OrFun, :|), | |
(:XorFun, :$), | |
(:AddFun, :+), | |
(:DotAddFun, :.+), | |
(:SubFun, :-), | |
(:DotSubFun, :.-), | |
(:MulFun, :*), | |
(:DotMulFun, :.*), | |
(:RDivFun, :/), | |
(:DotRDivFun, :./), | |
(:LDivFun, :\), | |
(:IDivFun, :div), | |
(:DotIDivFun, :.÷), | |
(:ModFun, :mod), | |
(:RemFun, :rem), | |
(:DotRemFun, :.%), | |
(:PowFun, :^), | |
(:MaxFun, :scalarmax), | |
(:MinFun, :scalarmin), | |
(:LessFun, :<), | |
(:MoreFun, :>), | |
(:DotLSFun, :.<<), | |
(:DotRSFun, :.>>), | |
(:ElementwiseMaxFun, :max), | |
(:ElementwiseMinFun, :min), | |
(:ComplexFun, :complex), | |
(:DotFun, :dot), | |
] | |
@eval begin | |
@deprecate_binding $(Fun) typeof($(func)) | |
(::Type{typeof($(func))})() = $(func) | |
end | |
end | |
@deprecate_binding CentralizedAbs2Fun typeof(centralizedabs2fun(0)).name.primary | |
(::Type{typeof(centralizedabs2fun(0)).name.primary})(m::Number) = centralizedabs2fun(m) | |
@deprecate specialized_unary(f::Function) f | |
@deprecate specialized_binary(f::Function) f | |
@deprecate specialized_bitwise_unary(f::Function) f | |
@deprecate specialized_bitwise_binary(f::Function) f | |
@deprecate bitunpack(B::BitArray) Array(B) | |
@deprecate bitpack(A::AbstractArray) BitArray(A) | |
# #4163 | |
@deprecate xdump dump | |
@deprecate copy(x::AbstractString) identity(x) | |
@deprecate copy(x::Tuple) identity(x) | |
@deprecate sprandbool(m::Integer, n::Integer, density::AbstractFloat) sprand(Bool, m, n, density) | |
@deprecate sprandbool(r::AbstractRNG, m::Integer, n::Integer, density::AbstractFloat) sprand(r, Bool, m, n, density) | |
@deprecate sprandbool(n::Integer, density::AbstractFloat) sprand(Bool, n, density) | |
@deprecate sprandbool(r::AbstractRNG, n::Integer, density::AbstractFloat) sprand(r, Bool, n, density) | |
@deprecate sprand{T}(n::Integer, density::AbstractFloat, ::Type{T}) sprand(T, n, density) | |
@deprecate sprand{T}(r::AbstractRNG, n::Integer, density::AbstractFloat, ::Type{T}) sprand(r, T, n, density) | |
#15995 | |
@deprecate symbol Symbol | |
#15032: Expressions like Base.(:+) now call broadcast. Since calls | |
# to broadcast(x, ::Symbol) are unheard of, and broadcast(x, ::Integer) | |
# are unlikely, we can treat these as deprecated getfield calls. | |
# (See julia-syntax.scm for the Base.(:+)(...) = ... deprecation.) | |
function broadcast(x::Any, i::Union{Integer,Symbol}) | |
depwarn("x.(i) is deprecated; use getfield(x, i) instead.", :broadcast) | |
getfield(x, i) | |
end | |
# clearer to be more explicit in the warning for the Module case | |
function broadcast(m::Module, s::Symbol) | |
S = repr(s) # 16295 | |
depwarn("$m.($S) is deprecated; use $m.$S or getfield($m, $S) instead.", :broadcast) | |
getfield(m, s) | |
end | |
# expressions like f.(3) should still call broadcast for f::Function, | |
# and in general broadcast should work for scalar arguments, while | |
# getfield is certainly not intended for the case of f::Function. | |
broadcast(f::Function, i::Integer) = f(i) | |
#16167 | |
macro ccallable(def) | |
depwarn("@ccallable requires a return type", Symbol("@ccallable")) | |
if isa(def,Expr) && (def.head === :(=) || def.head === :function) | |
sig = def.args[1] | |
if sig.head === :call | |
name = sig.args[1] | |
at = map(sig.args[2:end]) do a | |
if isa(a,Expr) && a.head === :(::) | |
a.args[2] | |
else | |
:Any | |
end | |
end | |
return quote | |
$(esc(def)) | |
let name = $(esc(name)), tt = $(Expr(:curly, :Tuple, map(esc, at)...)) | |
rt = return_types(name, tt) | |
length(rt) == 1 || error("function not ccallable") | |
ccallable(name, rt[1], tt) | |
end | |
end | |
end | |
end | |
error("expected method definition in @ccallable") | |
end | |
@deprecate_binding ASCIIString String | |
@deprecate_binding UTF8String String | |
@deprecate_binding ByteString String | |
@deprecate utf8(p::Ptr{UInt8}, len::Integer) unsafe_string(p, len) | |
@deprecate utf8(p::Ptr{UInt8}) unsafe_string(p) | |
@deprecate utf8(v::Vector{UInt8}) String(v) | |
@deprecate utf8(s::AbstractString) String(s) | |
@deprecate utf8(x) convert(String, x) | |
@deprecate ascii(p::Ptr{UInt8}, len::Integer) ascii(unsafe_string(p, len)) | |
@deprecate ascii(p::Ptr{UInt8}) ascii(unsafe_string(p)) | |
@deprecate ascii(v::Vector{UInt8}) ascii(String(v)) | |
@deprecate ascii(x) ascii(convert(String, x)) | |
@deprecate bytestring(s::Cstring) unsafe_string(s) | |
@deprecate bytestring(v::Vector{UInt8}) String(copy(v)) | |
@deprecate bytestring(io::Base.AbstractIOBuffer) String(io) | |
@deprecate bytestring(p::Union{Ptr{Int8},Ptr{UInt8}}) unsafe_string(p) | |
@deprecate bytestring(p::Union{Ptr{Int8},Ptr{UInt8}}, len::Integer) unsafe_string(p,len) | |
@deprecate bytestring(s::AbstractString...) string(s...) | |
@deprecate String(s::Cstring) unsafe_string(s) | |
@deprecate String(p::Union{Ptr{Int8},Ptr{UInt8}}) unsafe_string(p) | |
@deprecate String(p::Union{Ptr{Int8},Ptr{UInt8}}, len::Integer) unsafe_string(p,len) | |
@deprecate( | |
convert(::Type{String}, a::Vector{UInt8}, invalids_as::AbstractString), | |
let a = a, invalids_as = invalids_as | |
l = length(a) | |
idx = 1 | |
iscopy = false | |
while idx <= l | |
if !is_valid_continuation(a[idx]) | |
nextidx = idx+1+utf8_trailing[a[idx]+1] | |
(nextidx <= (l+1)) && (idx = nextidx; continue) | |
end | |
!iscopy && (a = copy(a); iscopy = true) | |
endn = idx | |
while endn <= l | |
!is_valid_continuation(a[endn]) && break | |
endn += 1 | |
end | |
(endn > idx) && (endn -= 1) | |
splice!(a, idx:endn, invalids_as.data) | |
l = length(a) | |
end | |
String(a) | |
end | |
) | |
@deprecate ==(x::Char, y::Integer) UInt32(x) == y | |
@deprecate ==(x::Integer, y::Char) x == UInt32(y) | |
# Note: when these deprecations are deleted, the specialized definitions isequal(x::Char, y::Integer) | |
# and isequal(x::Integer, y::Char) in operators.jl can be deleted, too | |
@deprecate isless(x::Char, y::Integer) UInt32(x) < y | |
@deprecate isless(x::Integer, y::Char) x < UInt32(y) | |
#6674 and #4233 | |
macro windows(qm,ex) | |
depwarn("`@windows` is deprecated, use `@static is_windows()` instead", Symbol("@windows")) | |
return @static is_windows() ? esc(ex.args[1]) : esc(ex.args[2]) | |
end | |
macro unix(qm,ex) | |
depwarn("`@unix` is deprecated, use `@static is_unix()` instead", Symbol("@unix")) | |
return @static is_unix() ? esc(ex.args[1]) : esc(ex.args[2]) | |
end | |
macro osx(qm,ex) | |
depwarn("`@osx` is deprecated, use `@static is_apple()` instead", Symbol("@osx")) | |
return @static is_apple() ? esc(ex.args[1]) : esc(ex.args[2]) | |
end | |
macro linux(qm,ex) | |
depwarn("`@linux` is deprecated, use `@static is_linux()` instead", Symbol("@linux")) | |
return @static is_linux() ? esc(ex.args[1]) : esc(ex.args[2]) | |
end | |
macro windows_only(ex) | |
depwarn("`@windows_only` is deprecated, use `@static if is_windows()` instead", Symbol("@windows_only")) | |
return @static if is_windows() esc(ex) end | |
end | |
macro unix_only(ex) | |
depwarn("`@unix_only` is deprecated, use `@static if is_unix()` instead", Symbol("@unix_only")) | |
return @static if is_unix() esc(ex) end | |
end | |
macro osx_only(ex) | |
depwarn("`@osx_only` is deprecated, use `@static if is_apple()` instead", Symbol("@osx_only")) | |
return @static if is_apple() esc(ex) end | |
end | |
macro linux_only(ex) | |
depwarn("`@linux_only` is deprecated, use `@static if is_linux()` instead", Symbol("@linux_only")) | |
return @static if is_linux() esc(ex) end | |
end | |
export | |
@windows, | |
@unix, | |
@osx, | |
@linux, | |
@windows_only, | |
@unix_only, | |
@osx_only, | |
@linux_only | |
export OS_NAME | |
const OS_NAME = | |
if Sys.KERNEL === :NT | |
:Windows | |
else | |
Sys.KERNEL | |
end | |
deprecate(:OS_NAME) # use Sys.KERNEL now | |
export CPU_CORES | |
function _set_CPU_CORES() | |
global const CPU_CORES = Sys.CPU_CORES | |
deprecate(Base, :CPU_CORES) | |
end | |
module Init_CPU_CORES | |
const __init__ = Base._set_CPU_CORES | |
end | |
@deprecate_binding WORD_SIZE Sys.WORD_SIZE | |
@deprecate showcompact_lim show | |
@deprecate_binding writemime show | |
@deprecate blas_set_num_threads BLAS.set_num_threads | |
@deprecate print_escaped escape_string | |
@deprecate print_unescaped unescape_string | |
@deprecate print_joined join | |
@deprecate broadcast!_function(f) (B, As...) -> broadcast!(f, B, As...) | |
@deprecate broadcast_function(f) (As...) -> broadcast(f, As...) | |
##### histogram ##### | |
## nice-valued ranges for histograms | |
export hist, hist!, hist2d, hist2d!, histrange | |
function histrange{T<:AbstractFloat,N}(v::AbstractArray{T,N}, n::Integer) | |
depwarn("histrange(...) is deprecated, use StatsBase.histrange(...) instead",:histrange) | |
nv = length(v) | |
if nv == 0 && n < 0 | |
throw(ArgumentError("number of bins must be ≥ 0 for an empty array, got $n")) | |
elseif nv > 0 && n < 1 | |
throw(ArgumentError("number of bins must be ≥ 1 for a non-empty array, got $n")) | |
end | |
if nv == 0 | |
return 0.0:1.0:0.0 | |
end | |
lo, hi = extrema(v) | |
if hi == lo | |
step = 1.0 | |
else | |
bw = (hi - lo) / n | |
e = 10.0^floor(log10(bw)) | |
r = bw / e | |
if r <= 2 | |
step = 2*e | |
elseif r <= 5 | |
step = 5*e | |
else | |
step = 10*e | |
end | |
end | |
start = step*(ceil(lo/step)-1) | |
nm1 = ceil(Int,(hi - start)/step) | |
start:step:(start + nm1*step) | |
end | |
function histrange{T<:Integer,N}(v::AbstractArray{T,N}, n::Integer) | |
depwarn("histrange(...) is deprecated, use StatsBase.histrange(...) instead",:histrange) | |
nv = length(v) | |
if nv == 0 && n < 0 | |
throw(ArgumentError("number of bins must be ≥ 0 for an empty array, got $n")) | |
elseif nv > 0 && n < 1 | |
throw(ArgumentError("number of bins must be ≥ 1 for a non-empty array, got $n")) | |
end | |
if nv == 0 | |
return 0:1:0 | |
end | |
if n <= 0 | |
throw(ArgumentError("number of bins n=$n must be positive")) | |
end | |
lo, hi = extrema(v) | |
if hi == lo | |
step = 1 | |
else | |
bw = (Float64(hi) - Float64(lo)) / n | |
e = 10.0^max(0,floor(log10(bw))) | |
r = bw / e | |
if r <= 1 | |
step = e | |
elseif r <= 2 | |
step = 2*e | |
elseif r <= 5 | |
step = 5*e | |
else | |
step = 10*e | |
end | |
end | |
start = step*(ceil(lo/step)-1) | |
nm1 = ceil(Int,(hi - start)/step) | |
start:step:(start + nm1*step) | |
end | |
## midpoints of intervals | |
midpoints(r::Range) = r[1:length(r)-1] + 0.5*step(r) | |
midpoints(v::AbstractVector) = [0.5*(v[i] + v[i+1]) for i in 1:length(v)-1] | |
## hist ## | |
function sturges(n) # Sturges' formula | |
depwarn("sturges(n) is deprecated, use StatsBase.sturges(n) instead.",:sturges) | |
n==0 && return one(n) | |
ceil(Int,log2(n))+1 | |
end | |
function hist!{HT}(h::AbstractArray{HT}, v::AbstractVector, edg::AbstractVector; init::Bool=true) | |
depwarn("hist(...) and hist!(...) are deprecated. Use fit(Histogram,...) in StatsBase.jl instead.",:hist!) | |
n = length(edg) - 1 | |
length(h) == n || throw(DimensionMismatch("length(histogram) must equal length(edges) - 1")) | |
if init | |
fill!(h, zero(HT)) | |
end | |
for x in v | |
i = searchsortedfirst(edg, x)-1 | |
if 1 <= i <= n | |
h[i] += 1 | |
end | |
end | |
edg, h | |
end | |
hist(v::AbstractVector, edg::AbstractVector) = hist!(Array{Int,1}(length(edg)-1), v, edg) | |
hist(v::AbstractVector, n::Integer) = hist(v,histrange(v,n)) | |
hist(v::AbstractVector) = hist(v,sturges(length(v))) | |
function hist!{HT}(H::AbstractArray{HT,2}, A::AbstractMatrix, edg::AbstractVector; init::Bool=true) | |
depwarn("hist(...) and hist!(...) are deprecated. Use fit(Histogram,...) in StatsBase.jl instead.",:hist!) | |
m, n = size(A) | |
sH = size(H) | |
sE = (length(edg)-1,n) | |
sH == sE || throw(DimensionMismatch("incorrect size of histogram")) | |
if init | |
fill!(H, zero(HT)) | |
end | |
for j = 1:n | |
hist!(sub(H, :, j), sub(A, :, j), edg) | |
end | |
edg, H | |
end | |
hist(A::AbstractMatrix, edg::AbstractVector) = hist!(Array{Int,2}(length(edg)-1, size(A,2)), A, edg) | |
hist(A::AbstractMatrix, n::Integer) = hist(A,histrange(A,n)) | |
hist(A::AbstractMatrix) = hist(A,sturges(size(A,1))) | |
## hist2d | |
function hist2d!{HT}(H::AbstractArray{HT,2}, v::AbstractMatrix, | |
edg1::AbstractVector, edg2::AbstractVector; init::Bool=true) | |
depwarn("hist2d!(...) and hist2d(...) are deprecated. Use fit(Histogram,...) in StatsBase.jl instead.",:hist2d!) | |
size(v,2) == 2 || throw(DimensionMismatch("hist2d requires an Nx2 matrix")) | |
n = length(edg1) - 1 | |
m = length(edg2) - 1 | |
size(H) == (n, m) || throw(DimensionMismatch("incorrect size of histogram")) | |
if init | |
fill!(H, zero(HT)) | |
end | |
for i = indices(v,1) | |
x = searchsortedfirst(edg1, v[i,1]) - 1 | |
y = searchsortedfirst(edg2, v[i,2]) - 1 | |
if 1 <= x <= n && 1 <= y <= m | |
@inbounds H[x,y] += 1 | |
end | |
end | |
edg1, edg2, H | |
end | |
hist2d(v::AbstractMatrix, edg1::AbstractVector, edg2::AbstractVector) = | |
hist2d!(Array{Int,2}(length(edg1)-1, length(edg2)-1), v, edg1, edg2) | |
hist2d(v::AbstractMatrix, edg::AbstractVector) = hist2d(v, edg, edg) | |
hist2d(v::AbstractMatrix, n1::Integer, n2::Integer) = | |
hist2d(v, histrange(sub(v,:,1),n1), histrange(sub(v,:,2),n2)) | |
hist2d(v::AbstractMatrix, n::Integer) = hist2d(v, n, n) | |
hist2d(v::AbstractMatrix) = hist2d(v, sturges(size(v,1))) | |
@deprecate cell(dims::Integer...) Array{Any}(dims...) | |
@deprecate cell(dims::Tuple{Vararg{Integer}}) Array{Any}(dims) | |
@deprecate(pointer_to_array{T}(p::Ptr{T}, d::Union{Integer, Tuple{Vararg{Integer}}}, own::Bool=false), | |
unsafe_wrap(Array, p, d, own)) | |
@deprecate(pointer_to_string(p::Ptr{UInt8}, len::Integer, own::Bool=false), | |
unsafe_wrap(String, p, len, own)) | |
@deprecate(pointer_to_string(p::Ptr{UInt8}, own::Bool=false), | |
unsafe_wrap(String, p, own)) | |
function checkbounds(::Type{Bool}, sz::Integer, i) | |
depwarn("checkbounds(Bool, size(A, d), i) is deprecated, use checkindex(Bool, indices(A, d), i).", :checkbounds) | |
checkbounds(Bool, 1:sz, i) | |
end | |
immutable FakeArray{T,N} <: AbstractArray{T,N} | |
dims::NTuple{N,Int} | |
end | |
size(A::FakeArray) = A.dims | |
function checkbounds{N,T}(::Type{Bool}, sz::NTuple{N,Integer}, I1::T, I...) | |
depwarn("checkbounds(Bool, size(A), I...) is deprecated, use checkbounds(Bool, A, I...).", :checkbounds) | |
checkbounds(Bool, FakeArray(sz), I1, I...) | |
end | |
function first(::Colon) | |
depwarn("first(:) is deprecated, see http://docs.julialang.org/en/latest/devdocs/offset-arrays/", :first) | |
1 | |
end | |
function _first(i, A, d) | |
depwarn("_first is deprecated, see http://docs.julialang.org/en/latest/devdocs/offset-arrays/", :_first) | |
__first(i, A, d) | |
end | |
__first(::Colon, P, ::Colon) = first(linearindices(P)) | |
__first(i, P, ::Colon) = first(i) | |
__first(::Colon, P, d) = first(indices(P, d)) | |
__first(i, P, d) = first(i) | |
# Not exported, but deprecation may be useful just in case | |
function Broadcast.check_broadcast_shape(sz::Dims, As::Union{AbstractArray,Number}...) | |
depwarn("check_broadcast_shape(size(A), B...) should be replaced with check_broadcast_shape(indices(A), B...)", :check_broadcast_shape) | |
Broadcast.check_broadcast_shape(map(OneTo, sz), As...) | |
end | |
@deprecate trailingsize{n}(A::AbstractArray, ::Type{Val{n}}) trailingsize(A, n) | |
@deprecate slice view | |
@deprecate sub view | |
# Point users to SuiteSparse | |
function ereach{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, k::Integer, parent::Vector{Ti}) | |
error(string("ereach(A, k, parent) now lives in package SuiteSparse.jl. Run", | |
"Pkg.add(\"SuiteSparse\") to install SuiteSparse on Julia v0.5.")) | |
end | |
export etree | |
function etree{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, postorder::Bool) | |
error(string("etree(A[, post]) now lives in package SuiteSparse.jl. Run", | |
"Pkg.add(\"SuiteSparse\") to install SuiteSparse on Julia v0.5.")) | |
end | |
etree(A::SparseMatrixCSC) = etree(A, false) | |
function csc_permute{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, pinv::Vector{Ti}, q::Vector{Ti}) | |
error(string("csc_permute(A, pinv, q) now lives in package SuiteSparse.jl. Run", | |
"Pkg.add(\"SuiteSparse\") to install SuiteSparse on Julia v0.5.")) | |
end | |
function symperm{Tv,Ti}(A::SparseMatrixCSC{Tv,Ti}, pinv::Vector{Ti}) | |
error(string("symperm(A, pinv) now lives in package SuiteSparse.jl. Run,", | |
"Pkg.add(\"SuiteSparse\") to install SuiteSparse on Julia v0.5.")) | |
end | |
# Deprecate no-op transpose fallback. Please see #13171 and #17075. | |
function transpose(x) | |
depwarn(string("the no-op `transpose` fallback is deprecated, and no more specific ", | |
"`transpose` method for $(typeof(x)) exists. Consider `permutedims(x, [2, 1])` ", | |
"or writing a specific `transpose(x::$(typeof(x)))` method if appropriate."), | |
:transpose) | |
return x | |
end | |
@deprecate cholfact!(A::Base.LinAlg.HermOrSym, uplo::Symbol, ::Type{Val{false}}) cholfact!(A, Val{false}) | |
@deprecate cholfact!(A::Base.LinAlg.HermOrSym, uplo::Symbol = :U) cholfact!(A) | |
# During the 0.5 development cycle, do not add any deprecations below this line | |
# To be deprecated in 0.6 | |
const _oldstyle_array_vcat_ = false | |
@deprecate write(x) write(STDOUT::IO, x) | |
function delete!(::EnvHash, k::AbstractString, def) | |
depwarn("`delete!(ENV, k, def)` should be replaced with `pop!(ENV, k, def)`. Be aware that `pop!` returns `k` or `def`, while `delete!` returns `ENV` or `def`.", :delete!) | |
haskey(ENV,k) ? delete!(ENV,k) : def | |
end | |
@deprecate (+)(J::UniformScaling, x::Number) J.λ + x | |
@deprecate (+)(x::Number, J::UniformScaling) x + J.λ | |
@deprecate (-)(J::UniformScaling, x::Number) J.λ - x | |
@deprecate (-)(x::Number, J::UniformScaling) x - J.λ | |
# Deprecate methods that convert Diagonal and Bidiagonal to <:AbstractTriangular. | |
function convert(::Type{UpperTriangular}, A::Diagonal) | |
depwarn(string("`convert(::Type{UpperTriangular}, A::Diagonal)` and other methods ", | |
"that convert `Diagonal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `UpperTriangular` constructor directly ", | |
"(`UpperTriangular(A)`) instead."), :convert) | |
UpperTriangular(A) | |
end | |
function convert(::Type{LowerTriangular}, A::Diagonal) | |
depwarn(string("`convert(::Type{LowerTriangular}, A::Diagonal)` and other methods ", | |
"that convert `Diagonal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `LowerTriangular` constructor directly ", | |
"(`LowerTriangular(A)`) instead."), :convert) | |
LowerTriangular(A) | |
end | |
function convert(::Type{Base.LinAlg.UnitUpperTriangular}, A::Diagonal) | |
depwarn(string("`convert(::Type{UnitUpperTriangular}, A::Diagonal)` and other methods ", | |
"that convert `Diagonal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `UnitUpperTriangular` constructor directly ", | |
"(`Base.LinAlg.UnitUpperTriangular(A)`) instead."), :convert) | |
if !all(A.diag .== one(eltype(A))) | |
throw(ArgumentError("matrix cannot be represented as UnitUpperTriangular")) | |
end | |
Base.LinAlg.UnitUpperTriangular(Array(A)) | |
end | |
function convert(::Type{Base.LinAlg.UnitLowerTriangular}, A::Diagonal) | |
depwarn(string("`convert(::Type{UnitLowerTriangular}, A::Diagonal)` and other methods ", | |
"that convert `Diagonal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `UnitLowerTriangular` constructor directly ", | |
"(`Base.LinAlg.UnitLowerTriangular(A)`) instead."), :convert) | |
if !all(A.diag .== one(eltype(A))) | |
throw(ArgumentError("matrix cannot be represented as UnitLowerTriangular")) | |
end | |
Base.LinAlg.UnitLowerTriangular(Array(A)) | |
end | |
function convert(::Type{LowerTriangular}, A::Bidiagonal) | |
depwarn(string("`convert(::Type{LowerTriangular}, A::Bidiagonal)` and other methods ", | |
"that convert `Diagonal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `LowerTriangular` constructor directly (`LowerTriangular(A)`) ", | |
"instead."), :convert) | |
if !A.isupper | |
LowerTriangular(Array(A)) | |
else | |
throw(ArgumentError("Bidiagonal matrix must have lower off diagonal to be converted to LowerTriangular")) | |
end | |
end | |
function convert(::Type{UpperTriangular}, A::Bidiagonal) | |
depwarn(string("`convert(::Type{UpperTriangular}, A::Bidiagonal)` and other methods ", | |
"that convert `Diagoinal`/`Bidiagonal` to `<:AbstractTriangular` are deprecated. ", | |
"Consider calling the `UpperTriangular` constructor directly (`UpperTriangular(A)`) ", | |
"instead."), :convert) | |
if A.isupper | |
UpperTriangular(Array(A)) | |
else | |
throw(ArgumentError("Bidiagonal matrix must have upper off diagonal to be converted to UpperTriangular")) | |
end | |
end | |
# Deprecate vectorized unary functions over sparse matrices in favor of compact broadcast syntax (#17265). | |
for f in (:sin, :sinh, :sind, :asin, :asinh, :asind, | |
:tan, :tanh, :tand, :atan, :atanh, :atand, | |
:sinpi, :cosc, :ceil, :floor, :trunc, :round, :real, :imag, | |
:log1p, :expm1, :abs, :abs2, :conj, | |
:log, :log2, :log10, :exp, :exp2, :exp10, :sinc, :cospi, | |
:cos, :cosh, :cosd, :acos, :acosd, | |
:cot, :coth, :cotd, :acot, :acotd, | |
:sec, :sech, :secd, :asech, | |
:csc, :csch, :cscd, :acsch) | |
@eval @deprecate $f(A::SparseMatrixCSC) $f.(A) | |
end | |
# For deprecating vectorized functions in favor of compact broadcast syntax | |
macro dep_vectorize_1arg(S, f) | |
S = esc(S) | |
f = esc(f) | |
T = esc(:T) | |
x = esc(:x) | |
AbsArr = esc(:AbstractArray) | |
:( @deprecate $f{$T<:$S}($x::$AbsArr{$T}) $f.($x) ) | |
end | |
macro dep_vectorize_2arg(S, f) | |
S = esc(S) | |
f = esc(f) | |
T1 = esc(:T1) | |
T2 = esc(:T2) | |
x = esc(:x) | |
y = esc(:y) | |
AbsArr = esc(:AbstractArray) | |
quote | |
@deprecate $f{$T1<:$S}($x::$S, $y::$AbsArr{$T1}) $f.($x,$y) | |
@deprecate $f{$T1<:$S}($x::$AbsArr{$T1}, $y::$S) $f.($x,$y) | |
@deprecate $f{$T1<:$S,$T2<:$S}($x::$AbsArr{$T1}, $y::$AbsArr{$T2}) $f.($x,$y) | |
end | |
end | |
# Deprecate @vectorize_1arg-vectorized functions from... | |
for f in ( | |
# base/special/trig.jl | |
:sinpi, :cospi, :sinc, :cosc, | |
# base/special/log.jl | |
:log, :log1p, | |
# base/special/gamma.jl | |
:gamma, :lfact, :digamma, :trigamma, :zeta, :eta, | |
# base/special/erf.jl | |
:erfcx, :erfi, :dawson, | |
# base/special/bessel.jl | |
:airyprime, :airyai, :airyaiprime, :airybi, :airybiprime, | |
:airy, :airyx, :besselj0, :besselj1, :bessely0, :bessely1, | |
# base/math.jl | |
:cbrt, :sinh, :cosh, :tanh, :atan, :asinh, :exp, :erf, :erfc, :exp2, | |
:expm1, :exp10, :sin, :cos, :tan, :asin, :acos, :acosh, :atanh, | |
#=:log,=# :log2, :log10, :lgamma, #=:log1p,=# :sqrt, | |
# base/floatfuncs.jl | |
:abs, :abs2, :angle, :isnan, :isinf, :isfinite, | |
# base/complex.jl | |
:cis, | |
) | |
@eval @dep_vectorize_1arg Number $f | |
end | |
# base/fastmath.jl | |
for f in ( :acos_fast, :acosh_fast, :angle_fast, :asin_fast, :asinh_fast, | |
:atan_fast, :atanh_fast, :cbrt_fast, :cis_fast, :cos_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)) | |
end | |
for f in ( | |
:invdigamma, # base/special/gamma.jl | |
:erfinc, :erfcinv, # base/special/erf.jl | |
: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 @dep_vectorize_1arg Real $f | |
end | |
# base/complex.jl | |
@dep_vectorize_1arg Complex round | |
@dep_vectorize_1arg Complex float | |
# base/dates/*.jl | |
for f in (:unix2datetime, :rata2datetime, :julian2datetime) # base/dates/conversions.jl | |
eval(Dates, :(Base.@dep_vectorize_1arg Real $f)) | |
end | |
for f in ( | |
# base/dates/accessors.jl | |
:year, :month, :day, :week, :dayofmonth, :yearmonth, :monthday, :yearmonthday, | |
# base/dates/adjusters.jl | |
:firstdayofweek, :lastdayofweek, :firstdayofmonth, | |
:lastdayofmonth, :firstdayofyear, :lastdayofyear, | |
:firstdayofquarter, :lastdayofquarter, | |
# base/dates/query.jl | |
:dayname, :dayabbr, :dayofweek, :dayofweekofmonth, | |
:daysofweekinmonth, :monthname, :monthabbr, :daysinmonth, | |
:isleapyear, :dayofyear, :daysinyear, :quarterofyear, :dayofquarter, | |
) | |
eval(Dates, :(Base.@dep_vectorize_1arg Dates.TimeType $f)) | |
end | |
for f in ( | |
:hour, :minute, :second, :millisecond, # base/dates/accessors.jl | |
:Date, :datetime2unix, :datetime2rata, :datetime2julian, # base/dates/conversions.jl | |
) | |
eval(Dates, :(Base.@dep_vectorize_1arg Dates.DateTime $f)) | |
end | |
eval(Dates, :(Base.@dep_vectorize_1arg Dates.Date Datetime)) # base/dates/conversions.jl | |
# Deprecate @vectorize_2arg-vectorized functions from... | |
for f in ( | |
# base/special/gamma.jl | |
:polygamma, :zeta, :beta, :lbeta, | |
# base/special/bessel.jl | |
:airy, :airyx, :besseli, :besselix, :besselj, :besseljx, | |
:besselk, :besselkx, :bessely, :besselyx, :besselh, | |
:besselhx, :hankelh1, :hankelh2, :hankelh1x, :hankelh2x, | |
# base/math.jl | |
:log, :hypot, :atan2, | |
) | |
@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)) | |
end | |
for f in ( | |
:max, :min, # base/math.jl | |
:copysign, :flipsign, # base/floatfuncs.jl | |
) | |
@eval @dep_vectorize_2arg Real $f | |
end | |
# Deprecate @vectorize_1arg and @vectorize_2arg themselves | |
macro vectorize_1arg(S,f) | |
depwarn(string("`@vectorize_1arg` is deprecated in favor of compact broadcast syntax. ", | |
"Instead of `@vectorize_1arg`'ing function `f` and calling `f(arg)`, call `f.(arg)`."), | |
:vectorize_1arg) | |
quote | |
@dep_vectorize_1arg($(esc(S)),$(esc(f))) | |
end | |
end | |
macro vectorize_2arg(S,f) | |
depwarn(string("`@vectorize_2arg` is deprecated in favor of compact broadcast syntax. ", | |
"Instead of `@vectorize_2arg`'ing function `f` and calling `f(arg1, arg2)`, call ", | |
"`f.(arg1,arg2)`. "), :vectorize_2arg) | |
quote | |
@dep_vectorize_2arg($(esc(S)),$(esc(f))) | |
end | |
end | |
export @vectorize_1arg, @vectorize_2arg | |
# Devectorize manually vectorized abs methods in favor of compact broadcast syntax | |
@deprecate abs(f::Base.Pkg.Resolve.MaxSum.Field) abs.(f) | |
@deprecate abs(B::BitArray) abs.(B) | |
@deprecate abs(M::Bidiagonal) abs.(M) | |
@deprecate abs(D::Diagonal) abs.(D) | |
@deprecate abs(M::Tridiagonal) abs.(M) | |
@deprecate abs(M::SymTridiagonal) abs.(M) | |
@deprecate abs(x::AbstractSparseVector) abs.(x) | |
# Deprecate @textmime into the Multimedia module, #18441 | |
eval(Multimedia, :(macro textmime(mime) | |
Base.depwarn(string("`@textmime \"mime\"` is deprecated; use ", | |
"`Base.Multimedia.istextmime(::MIME\"mime\") = true` instead" | |
), :textmime) | |
quote | |
Base.Multimedia.istextmime(::MIME{$(Meta.quot(Symbol(mime)))}) = true | |
end | |
end)) | |
@deprecate ipermutedims(A::AbstractArray,p) permutedims(A, invperm(p)) | |
@deprecate is (===) | |
@deprecate_binding Filter Iterators.Filter | |
@deprecate_binding Zip Iterators.Zip | |
@deprecate filter(flt, itr) Iterators.filter(flt, itr) | |
@deprecate_binding rest Iterators.rest | |
@deprecate_binding countfrom Iterators.countfrom | |
@deprecate_binding take Iterators.take | |
@deprecate_binding drop Iterators.drop | |
@deprecate_binding cycle Iterators.cycle | |
@deprecate_binding repeated Iterators.repeated | |
# NOTE: Deprecation of Channel{T}() is implemented in channels.jl. | |
# To be removed from there when 0.6 deprecations are removed. | |
# Not exported, but probably better to have deprecations anyway | |
function reduced_dims(::Tuple{}, d::Int) | |
d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) | |
() | |
end | |
reduced_dims(::Tuple{}, region) = () | |
function reduced_dims(dims::Dims, region) | |
Base.depwarn("`reduced_dims` is deprecated for Dims-tuples; pass `indices` instead", :reduced_dims) | |
map(last, reduced_dims(map(n->OneTo(n), dims), region)) | |
end | |
function reduced_dims0(::Tuple{}, d::Int) | |
d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) | |
() | |
end | |
reduced_dims0(::Tuple{}, region) = () | |
function reduced_dims0(dims::Dims, region) | |
Base.depwarn("`reduced_dims0` is deprecated for Dims-tuples; pass `indices` instead", :reduced_dims0) | |
map(last, reduced_dims0(map(n->OneTo(n), dims), region)) | |
end | |
# #18218 | |
eval(Base.LinAlg, quote | |
function arithtype(T) | |
depwarn(string("arithtype is now deprecated. If you were using it inside a ", | |
"promote_op call, use promote_op(LinAlg.matprod, Ts...) instead. Otherwise, ", | |
"if you need its functionality, consider defining it locally."), | |
:arithtype) | |
T | |
end | |
function arithtype(::Type{Bool}) | |
depwarn(string("arithtype is now deprecated. If you were using it inside a ", | |
"promote_op call, use promote_op(LinAlg.matprod, Ts...) instead. Otherwise, ", | |
"if you need its functionality, consider defining it locally."), | |
:arithtype) | |
Int | |
end | |
end) | |
# End deprecations scheduled for 0.6 | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module DFT | |
# DFT plan where the inputs are an array of eltype T | |
abstract Plan{T} | |
import Base: show, summary, size, ndims, length, eltype, | |
*, A_mul_B!, inv, \, A_ldiv_B! | |
eltype{T}(::Type{Plan{T}}) = T | |
# size(p) should return the size of the input array for p | |
size(p::Plan, d) = size(p)[d] | |
ndims(p::Plan) = length(size(p)) | |
length(p::Plan) = prod(size(p))::Int | |
############################################################################## | |
export fft, ifft, bfft, fft!, ifft!, bfft!, | |
plan_fft, plan_ifft, plan_bfft, plan_fft!, plan_ifft!, plan_bfft!, | |
rfft, irfft, brfft, plan_rfft, plan_irfft, plan_brfft | |
typealias FFTWFloat Union{Float32,Float64} | |
fftwfloat(x) = _fftwfloat(float(x)) | |
_fftwfloat{T<:FFTWFloat}(::Type{T}) = T | |
_fftwfloat(::Type{Float16}) = Float32 | |
_fftwfloat{T}(::Type{Complex{T}}) = Complex{_fftwfloat(T)} | |
_fftwfloat{T}(::Type{T}) = error("type $T not supported") | |
_fftwfloat{T}(x::T) = _fftwfloat(T)(x) | |
complexfloat{T<:FFTWFloat}(x::StridedArray{Complex{T}}) = x | |
realfloat{T<:FFTWFloat}(x::StridedArray{T}) = x | |
# return an Array, rather than similar(x), to avoid an extra copy for FFTW | |
# (which only works on StridedArray types). | |
complexfloat{T<:Complex}(x::AbstractArray{T}) = copy1(typeof(fftwfloat(one(T))), x) | |
complexfloat{T<:Real}(x::AbstractArray{T}) = copy1(typeof(complex(fftwfloat(one(T)))), x) | |
realfloat{T<:Real}(x::AbstractArray{T}) = copy1(typeof(fftwfloat(one(T))), x) | |
# copy to a 1-based array, using circular permutation | |
function copy1{T}(::Type{T}, x) | |
y = Array{T}(map(length, indices(x))) | |
Base.circcopy!(y, x) | |
end | |
to1(x::AbstractArray) = _to1(indices(x), x) | |
_to1(::Tuple{Base.OneTo,Vararg{Base.OneTo}}, x) = x | |
_to1(::Tuple, x) = copy1(eltype(x), x) | |
# implementations only need to provide plan_X(x, region) | |
# for X in (:fft, :bfft, ...): | |
for f in (:fft, :bfft, :ifft, :fft!, :bfft!, :ifft!, :rfft) | |
pf = Symbol("plan_", f) | |
@eval begin | |
$f(x::AbstractArray) = (y = to1(x); $pf(y) * y) | |
$f(x::AbstractArray, region) = (y = to1(x); $pf(y, region) * y) | |
$pf(x::AbstractArray; kws...) = (y = to1(x); $pf(y, 1:ndims(y); kws...)) | |
end | |
end | |
""" | |
plan_ifft(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Same as [`plan_fft`](:func:`plan_fft`), but produces a plan that performs inverse transforms | |
[`ifft`](:func:`ifft`). | |
""" | |
plan_ifft | |
""" | |
plan_ifft!(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Same as [`plan_ifft`](:func:`plan_ifft`), but operates in-place on `A`. | |
""" | |
plan_ifft! | |
""" | |
plan_bfft!(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Same as [`plan_bfft`](:func:`plan_bfft`), but operates in-place on `A`. | |
""" | |
plan_bfft! | |
""" | |
plan_bfft(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Same as [`plan_fft`](:func:`plan_fft`), but produces a plan that performs an unnormalized | |
backwards transform [`bfft`](:func:`bfft`). | |
""" | |
plan_bfft | |
""" | |
plan_fft(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Pre-plan an optimized FFT along given dimensions (`dims`) of arrays matching the shape and | |
type of `A`. (The first two arguments have the same meaning as for [`fft`](:func:`fft`).) | |
Returns an object `P` which represents the linear operator computed by the FFT, and which | |
contains all of the information needed to compute `fft(A, dims)` quickly. | |
To apply `P` to an array `A`, use `P * A`; in general, the syntax for applying plans is much | |
like that of matrices. (A plan can only be applied to arrays of the same size as the `A` | |
for which the plan was created.) You can also apply a plan with a preallocated output array `Â` | |
by calling `A_mul_B!(Â, plan, A)`. (For `A_mul_B!`, however, the input array `A` must | |
be a complex floating-point array like the output `Â`.) You can compute the inverse-transform plan by `inv(P)` | |
and apply the inverse plan with `P \\ Â` (the inverse plan is cached and reused for | |
subsequent calls to `inv` or `\\`), and apply the inverse plan to a pre-allocated output | |
array `A` with `A_ldiv_B!(A, P, Â)`. | |
The `flags` argument is a bitwise-or of FFTW planner flags, defaulting to `FFTW.ESTIMATE`. | |
e.g. passing `FFTW.MEASURE` or `FFTW.PATIENT` will instead spend several seconds (or more) | |
benchmarking different possible FFT algorithms and picking the fastest one; see the FFTW | |
manual for more information on planner flags. The optional `timelimit` argument specifies a | |
rough upper bound on the allowed planning time, in seconds. Passing `FFTW.MEASURE` or | |
`FFTW.PATIENT` may cause the input array `A` to be overwritten with zeros during plan | |
creation. | |
[`plan_fft!`](:func:`plan_fft!`) is the same as [`plan_fft`](:func:`plan_fft`) but creates a | |
plan that operates in-place on its argument (which must be an array of complex | |
floating-point numbers). [`plan_ifft`](:func:`plan_ifft`) and so on are similar but produce | |
plans that perform the equivalent of the inverse transforms [`ifft`](:func:`ifft`) and so on. | |
""" | |
plan_fft | |
""" | |
plan_fft!(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Same as [`plan_fft`](:func:`plan_fft`), but operates in-place on `A`. | |
""" | |
plan_fft! | |
""" | |
rfft(A [, dims]) | |
Multidimensional FFT of a real array `A`, exploiting the fact that the transform has | |
conjugate symmetry in order to save roughly half the computational time and storage costs | |
compared with [`fft`](:func:`fft`). If `A` has size `(n_1, ..., n_d)`, the result has size | |
`(div(n_1,2)+1, ..., n_d)`. | |
The optional `dims` argument specifies an iterable subset of one or more dimensions of `A` | |
to transform, similar to [`fft`](:func:`fft`). Instead of (roughly) halving the first | |
dimension of `A` in the result, the `dims[1]` dimension is (roughly) halved in the same way. | |
""" | |
rfft | |
""" | |
ifft!(A [, dims]) | |
Same as [`ifft`](:func:`ifft`), but operates in-place on `A`. | |
""" | |
ifft! | |
""" | |
ifft(A [, dims]) | |
Multidimensional inverse FFT. | |
A one-dimensional inverse FFT computes | |
```math | |
\\operatorname{IDFT}(A)[k] = \\frac{1}{\\operatorname{length}(A)} | |
\\sum_{n=1}^{\\operatorname{length}(A)} \\exp\\left(+i\\frac{2\\pi (n-1)(k-1)} | |
{\\operatorname{length}(A)} \\right) A[n]. | |
``` | |
A multidimensional inverse FFT simply performs this operation along each transformed dimension of `A`. | |
""" | |
ifft | |
""" | |
fft!(A [, dims]) | |
Same as [`fft`](:func:`fft`), but operates in-place on `A`, which must be an array of | |
complex floating-point numbers. | |
""" | |
fft! | |
""" | |
bfft(A [, dims]) | |
Similar to [`ifft`](:func:`ifft`), but computes an unnormalized inverse (backward) | |
transform, which must be divided by the product of the sizes of the transformed dimensions | |
in order to obtain the inverse. (This is slightly more efficient than [`ifft`](:func:`ifft`) | |
because it omits a scaling step, which in some applications can be combined with other | |
computational steps elsewhere.) | |
```math | |
\\operatorname{BDFT}(A)[k] = \\operatorname{length}(A) \\operatorname{IDFT}(A)[k] | |
``` | |
""" | |
bfft | |
""" | |
bfft!(A [, dims]) | |
Same as [`bfft`](:func:`bfft`), but operates in-place on `A`. | |
""" | |
bfft! | |
# promote to a complex floating-point type (out-of-place only), | |
# so implementations only need Complex{Float} methods | |
for f in (:fft, :bfft, :ifft) | |
pf = Symbol("plan_", f) | |
@eval begin | |
$f{T<:Real}(x::AbstractArray{T}, region=1:ndims(x)) = $f(complexfloat(x), region) | |
$pf{T<:Real}(x::AbstractArray{T}, region; kws...) = $pf(complexfloat(x), region; kws...) | |
$f{T<:Union{Integer,Rational}}(x::AbstractArray{Complex{T}}, region=1:ndims(x)) = $f(complexfloat(x), region) | |
$pf{T<:Union{Integer,Rational}}(x::AbstractArray{Complex{T}}, region; kws...) = $pf(complexfloat(x), region; kws...) | |
end | |
end | |
rfft{T<:Union{Integer,Rational}}(x::AbstractArray{T}, region=1:ndims(x)) = rfft(realfloat(x), region) | |
plan_rfft(x::AbstractArray, region; kws...) = plan_rfft(realfloat(x), region; kws...) | |
# only require implementation to provide *(::Plan{T}, ::Array{T}) | |
*{T}(p::Plan{T}, x::AbstractArray) = p * copy1(T, x) | |
# Implementations should also implement A_mul_B!(Y, plan, X) so as to support | |
# pre-allocated output arrays. We don't define * in terms of A_mul_B! | |
# generically here, however, because of subtleties for in-place and rfft plans. | |
############################################################################## | |
# To support inv, \, and A_ldiv_B!(y, p, x), we require Plan subtypes | |
# to have a pinv::Plan field, which caches the inverse plan, and which | |
# should be initially undefined. They should also implement | |
# plan_inv(p) to construct the inverse of a plan p. | |
# hack from @simonster (in #6193) to compute the return type of plan_inv | |
# without actually calling it or even constructing the empty arrays. | |
_pinv_type(p::Plan) = typeof([plan_inv(x) for x in typeof(p)[]]) | |
pinv_type(p::Plan) = eltype(_pinv_type(p)) | |
inv(p::Plan) = | |
isdefined(p, :pinv) ? p.pinv::pinv_type(p) : (p.pinv = plan_inv(p)) | |
\(p::Plan, x::AbstractArray) = inv(p) * x | |
A_ldiv_B!(y::AbstractArray, p::Plan, x::AbstractArray) = A_mul_B!(y, inv(p), x) | |
############################################################################## | |
# implementations only need to provide the unnormalized backwards FFT, | |
# similar to FFTW, and we do the scaling generically to get the ifft: | |
type ScaledPlan{T,P,N} <: Plan{T} | |
p::P | |
scale::N # not T, to avoid unnecessary promotion to Complex | |
pinv::Plan | |
ScaledPlan(p, scale) = new(p, scale) | |
end | |
(::Type{ScaledPlan{T}}){T,P,N}(p::P, scale::N) = ScaledPlan{T,P,N}(p, scale) | |
ScaledPlan{T}(p::Plan{T}, scale::Number) = ScaledPlan{T}(p, scale) | |
ScaledPlan(p::ScaledPlan, α::Number) = ScaledPlan(p.p, p.scale * α) | |
size(p::ScaledPlan) = size(p.p) | |
show(io::IO, p::ScaledPlan) = print(io, p.scale, " * ", p.p) | |
summary(p::ScaledPlan) = string(p.scale, " * ", summary(p.p)) | |
*(p::ScaledPlan, x::AbstractArray) = scale!(p.p * x, p.scale) | |
*(α::Number, p::Plan) = ScaledPlan(p, α) | |
*(p::Plan, α::Number) = ScaledPlan(p, α) | |
*(I::UniformScaling, p::ScaledPlan) = ScaledPlan(p, I.λ) | |
*(p::ScaledPlan, I::UniformScaling) = ScaledPlan(p, I.λ) | |
*(I::UniformScaling, p::Plan) = ScaledPlan(p, I.λ) | |
*(p::Plan, I::UniformScaling) = ScaledPlan(p, I.λ) | |
# Normalization for ifft, given unscaled bfft, is 1/prod(dimensions) | |
normalization(T, sz, region) = (one(T) / prod([sz...][[region...]]))::T | |
normalization(X, region) = normalization(real(eltype(X)), size(X), region) | |
plan_ifft(x::AbstractArray, region; kws...) = | |
ScaledPlan(plan_bfft(x, region; kws...), normalization(x, region)) | |
plan_ifft!(x::AbstractArray, region; kws...) = | |
ScaledPlan(plan_bfft!(x, region; kws...), normalization(x, region)) | |
plan_inv(p::ScaledPlan) = ScaledPlan(plan_inv(p.p), inv(p.scale)) | |
A_mul_B!(y::AbstractArray, p::ScaledPlan, x::AbstractArray) = | |
scale!(p.scale, A_mul_B!(y, p.p, x)) | |
############################################################################## | |
# Real-input DFTs are annoying because the output has a different size | |
# than the input if we want to gain the full factor-of-two(ish) savings | |
# For backward real-data transforms, we must specify the original length | |
# of the first dimension, since there is no reliable way to detect this | |
# from the data (we can't detect whether the dimension was originally even | |
# or odd). | |
for f in (:brfft, :irfft) | |
pf = Symbol("plan_", f) | |
@eval begin | |
$f(x::AbstractArray, d::Integer) = $pf(x, d) * x | |
$f(x::AbstractArray, d::Integer, region) = $pf(x, d, region) * x | |
$pf(x::AbstractArray, d::Integer;kws...) = $pf(x, d, 1:ndims(x);kws...) | |
end | |
end | |
for f in (:brfft, :irfft) | |
@eval begin | |
$f{T<:Real}(x::AbstractArray{T}, d::Integer, region=1:ndims(x)) = $f(complexfloat(x), d, region) | |
$f{T<:Union{Integer,Rational}}(x::AbstractArray{Complex{T}}, d::Integer, region=1:ndims(x)) = $f(complexfloat(x), d, region) | |
end | |
end | |
""" | |
irfft(A, d [, dims]) | |
Inverse of [`rfft`](:func:`rfft`): for a complex array `A`, gives the corresponding real | |
array whose FFT yields `A` in the first half. As for [`rfft`](:func:`rfft`), `dims` is an | |
optional subset of dimensions to transform, defaulting to `1:ndims(A)`. | |
`d` is the length of the transformed real array along the `dims[1]` dimension, which must | |
satisfy `div(d,2)+1 == size(A,dims[1])`. (This parameter cannot be inferred from `size(A)` | |
since both `2*size(A,dims[1])-2` as well as `2*size(A,dims[1])-1` are valid sizes for the | |
transformed real array.) | |
""" | |
irfft | |
""" | |
brfft(A, d [, dims]) | |
Similar to [`irfft`](:func:`irfft`) but computes an unnormalized inverse transform (similar | |
to [`bfft`](:func:`bfft`)), which must be divided by the product of the sizes of the | |
transformed dimensions (of the real output array) in order to obtain the inverse transform. | |
""" | |
brfft | |
function rfft_output_size(x::AbstractArray, region) | |
d1 = first(region) | |
osize = [size(x)...] | |
osize[d1] = osize[d1]>>1 + 1 | |
return osize | |
end | |
function brfft_output_size(x::AbstractArray, d::Integer, region) | |
d1 = first(region) | |
osize = [size(x)...] | |
@assert osize[d1] == d>>1 + 1 | |
osize[d1] = d | |
return osize | |
end | |
plan_irfft{T}(x::AbstractArray{Complex{T}}, d::Integer, region; kws...) = | |
ScaledPlan(plan_brfft(x, d, region; kws...), | |
normalization(T, brfft_output_size(x, d, region), region)) | |
""" | |
plan_irfft(A, d [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Pre-plan an optimized inverse real-input FFT, similar to [`plan_rfft`](:func:`plan_rfft`) | |
except for [`irfft`](:func:`irfft`) and [`brfft`](:func:`brfft`), respectively. The first | |
three arguments have the same meaning as for [`irfft`](:func:`irfft`). | |
""" | |
plan_irfft | |
############################################################################## | |
export fftshift, ifftshift | |
fftshift(x) = circshift(x, div([size(x)...],2)) | |
""" | |
fftshift(x) | |
Swap the first and second halves of each dimension of `x`. | |
""" | |
fftshift(x) | |
function fftshift(x,dim) | |
s = zeros(Int,ndims(x)) | |
s[dim] = div(size(x,dim),2) | |
circshift(x, s) | |
end | |
""" | |
fftshift(x,dim) | |
Swap the first and second halves of the given dimension of array `x`. | |
""" | |
fftshift(x,dim) | |
ifftshift(x) = circshift(x, div([size(x)...],-2)) | |
""" | |
ifftshift(x, [dim]) | |
Undoes the effect of `fftshift`. | |
""" | |
ifftshift | |
function ifftshift(x,dim) | |
s = zeros(Int,ndims(x)) | |
s[dim] = -div(size(x,dim),2) | |
circshift(x, s) | |
end | |
############################################################################## | |
# FFTW module (may move to an external package at some point): | |
if Base.USE_GPL_LIBS | |
@doc """ | |
fft(A [, dims]) | |
Performs a multidimensional FFT of the array `A`. The optional `dims` argument specifies an | |
iterable subset of dimensions (e.g. an integer, range, tuple, or array) to transform along. | |
Most efficient if the size of `A` along the transformed dimensions is a product of small | |
primes; see `nextprod()`. See also `plan_fft()` for even greater efficiency. | |
A one-dimensional FFT computes the one-dimensional discrete Fourier transform (DFT) as | |
defined by | |
```math | |
\\operatorname{DFT}(A)[k] = | |
\\sum_{n=1}^{\\operatorname{length}(A)} | |
\\exp\\left(-i\\frac{2\\pi | |
(n-1)(k-1)}{\\operatorname{length}(A)} \\right) A[n]. | |
``` | |
A multidimensional FFT simply performs this operation along each transformed dimension of `A`. | |
!!! note | |
* Julia starts FFTW up with 1 thread by default. Higher performance is usually possible by | |
increasing number of threads. Use `FFTW.set_num_threads(Sys.CPU_CORES)` to use as many | |
threads as cores on your system. | |
* This performs a multidimensional FFT by default. FFT libraries in other languages such as | |
Python and Octave perform a one-dimensional FFT along the first non-singleton dimension | |
of the array. This is worth noting while performing comparisons. For more details, | |
refer to the ["Noteworthy Differences from other Languages"](:ref:`man-noteworthy-differences`) | |
section of the manual. | |
""" -> | |
fft | |
include("fft/FFTW.jl") | |
importall .FFTW | |
export FFTW, dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct! | |
end | |
############################################################################## | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
function _truncate_at_width_or_chars(str, width, chars="", truncmark="…") | |
truncwidth = strwidth(truncmark) | |
(width <= 0 || width < truncwidth) && return "" | |
wid = truncidx = lastidx = 0 | |
idx = start(str) | |
while !done(str, idx) | |
lastidx = idx | |
c, idx = next(str, idx) | |
wid += charwidth(c) | |
wid >= width - truncwidth && truncidx == 0 && (truncidx = lastidx) | |
(wid >= width || c in chars) && break | |
end | |
lastidx != 0 && str[lastidx] in chars && (lastidx = prevind(str, lastidx)) | |
truncidx == 0 && (truncidx = lastidx) | |
if lastidx < endof(str) | |
return String(SubString(str, 1, truncidx) * truncmark) | |
else | |
return String(str) | |
end | |
end | |
function show{K,V}(io::IO, t::Associative{K,V}) | |
recur_io = IOContext(io, :SHOWN_SET => t) | |
limit::Bool = get(io, :limit, false) | |
if !haskey(io, :compact) | |
recur_io = IOContext(recur_io, :compact => true) | |
end | |
# show in a Julia-syntax-like form: Dict(k=>v, ...) | |
if isempty(t) | |
print(io, typeof(t), "()") | |
else | |
if isleaftype(K) && isleaftype(V) | |
print(io, typeof(t).name) | |
else | |
print(io, typeof(t)) | |
end | |
print(io, '(') | |
if !show_circular(io, t) | |
first = true | |
n = 0 | |
for pair in t | |
first || print(io, ',') | |
first = false | |
show(recur_io, pair) | |
n+=1 | |
limit && n >= 10 && (print(io, "…"); break) | |
end | |
end | |
print(io, ')') | |
end | |
end | |
abstract AbstractSerializer | |
# Dict | |
# These can be changed, to trade off better performance for space | |
const global maxallowedprobe = 16 | |
const global maxprobeshift = 6 | |
_tablesz(x::Integer) = x < 16 ? 16 : one(x)<<((sizeof(x)<<3)-leading_zeros(x-1)) | |
""" | |
Dict([itr]) | |
`Dict{K,V}()` constructs a hash table with keys of type `K` and values of type `V`. | |
Given a single iterable argument, constructs a [`Dict`](:obj:`Dict`) whose key-value pairs | |
are taken from 2-tuples `(key,value)` generated by the argument. | |
```jldoctest | |
julia> Dict([("A", 1), ("B", 2)]) | |
Dict{String,Int64} with 2 entries: | |
"B" => 2 | |
"A" => 1 | |
``` | |
Alternatively, a sequence of pair arguments may be passed. | |
```jldoctest | |
julia> Dict("A"=>1, "B"=>2) | |
Dict{String,Int64} with 2 entries: | |
"B" => 2 | |
"A" => 1 | |
``` | |
""" | |
type Dict{K,V} <: Associative{K,V} | |
slots::Array{UInt8,1} | |
keys::Array{K,1} | |
vals::Array{V,1} | |
ndel::Int | |
count::Int | |
age::UInt | |
idxfloor::Int # an index <= the indexes of all used slots | |
maxprobe::Int | |
function Dict() | |
n = 16 | |
new(zeros(UInt8,n), Array{K,1}(n), Array{V,1}(n), 0, 0, 0, 1, 0) | |
end | |
function Dict(kv) | |
h = Dict{K,V}() | |
for (k,v) in kv | |
h[k] = v | |
end | |
return h | |
end | |
Dict(p::Pair) = setindex!(Dict{K,V}(), p.second, p.first) | |
function Dict(ps::Pair...) | |
h = Dict{K,V}() | |
sizehint!(h, length(ps)) | |
for p in ps | |
h[p.first] = p.second | |
end | |
return h | |
end | |
function Dict(d::Dict{K,V}) | |
if d.ndel > 0 | |
rehash!(d) | |
end | |
@assert d.ndel == 0 | |
new(copy(d.slots), copy(d.keys), copy(d.vals), 0, d.count, d.age, d.idxfloor, | |
d.maxprobe) | |
end | |
end | |
Dict() = Dict{Any,Any}() | |
Dict(kv::Tuple{}) = Dict() | |
copy(d::Dict) = Dict(d) | |
const AnyDict = Dict{Any,Any} | |
Dict{K,V}(ps::Pair{K,V}...) = Dict{K,V}(ps) | |
Dict{K }(ps::Pair{K}...,) = Dict{K,Any}(ps) | |
Dict{V }(ps::Pair{TypeVar(:K),V}...,) = Dict{Any,V}(ps) | |
Dict( ps::Pair...) = Dict{Any,Any}(ps) | |
function Dict(kv) | |
try | |
Base.dict_with_eltype(kv, eltype(kv)) | |
catch e | |
if any(x->isempty(methods(x, (typeof(kv),))), [start, next, done]) || | |
!all(x->isa(x,Union{Tuple,Pair}),kv) | |
throw(ArgumentError("Dict(kv): kv needs to be an iterator of tuples or pairs")) | |
else | |
rethrow(e) | |
end | |
end | |
end | |
dict_with_eltype{K,V}(kv, ::Type{Tuple{K,V}}) = Dict{K,V}(kv) | |
dict_with_eltype{K,V}(kv, ::Type{Pair{K,V}}) = Dict{K,V}(kv) | |
dict_with_eltype{K,V}(::Type{Pair{K,V}}) = Dict{K,V}() | |
dict_with_eltype(::Type) = Dict() | |
dict_with_eltype(kv, t) = grow_to!(dict_with_eltype(_default_eltype(typeof(kv))), kv) | |
# this is a special case due to (1) allowing both Pairs and Tuples as elements, | |
# and (2) Pair being invariant. a bit annoying. | |
function grow_to!(dest::Associative, itr) | |
out = grow_to!(similar(dest, Pair{Union{},Union{}}), itr, start(itr)) | |
return isempty(out) ? dest : out | |
end | |
function grow_to!{K,V}(dest::Associative{K,V}, itr, st) | |
while !done(itr, st) | |
(k,v), st = next(itr, st) | |
if isa(k,K) && isa(v,V) | |
dest[k] = v | |
else | |
new = similar(dest, Pair{typejoin(K,typeof(k)), typejoin(V,typeof(v))}) | |
copy!(new, dest) | |
new[k] = v | |
return grow_to!(new, itr, st) | |
end | |
end | |
return dest | |
end | |
similar{K,V}(d::Dict{K,V}) = Dict{K,V}() | |
similar{K,V}(d::Dict, ::Type{Pair{K,V}}) = Dict{K,V}() | |
# conversion between Dict types | |
function convert{K,V}(::Type{Dict{K,V}},d::Associative) | |
h = Dict{K,V}() | |
for (k,v) in d | |
ck = convert(K,k) | |
if !haskey(h,ck) | |
h[ck] = convert(V,v) | |
else | |
error("key collision during dictionary conversion") | |
end | |
end | |
return h | |
end | |
convert{K,V}(::Type{Dict{K,V}},d::Dict{K,V}) = d | |
hashindex(key, sz) = (((hash(key)%Int) & (sz-1)) + 1)::Int | |
isslotempty(h::Dict, i::Int) = h.slots[i] == 0x0 | |
isslotfilled(h::Dict, i::Int) = h.slots[i] == 0x1 | |
isslotmissing(h::Dict, i::Int) = h.slots[i] == 0x2 | |
function rehash!{K,V}(h::Dict{K,V}, newsz = length(h.keys)) | |
olds = h.slots | |
oldk = h.keys | |
oldv = h.vals | |
sz = length(olds) | |
newsz = _tablesz(newsz) | |
h.age += 1 | |
h.idxfloor = 1 | |
if h.count == 0 | |
resize!(h.slots, newsz) | |
fill!(h.slots, 0) | |
resize!(h.keys, newsz) | |
resize!(h.vals, newsz) | |
h.ndel = 0 | |
return h | |
end | |
slots = zeros(UInt8,newsz) | |
keys = Array{K,1}(newsz) | |
vals = Array{V,1}(newsz) | |
age0 = h.age | |
count = 0 | |
maxprobe = h.maxprobe | |
for i = 1:sz | |
if olds[i] == 0x1 | |
k = oldk[i] | |
v = oldv[i] | |
index0 = index = hashindex(k, newsz) | |
while slots[index] != 0 | |
index = (index & (newsz-1)) + 1 | |
end | |
probe = (index - index0) & (newsz-1) | |
probe > maxprobe && (maxprobe = probe) | |
slots[index] = 0x1 | |
keys[index] = k | |
vals[index] = v | |
count += 1 | |
if h.age != age0 | |
# if `h` is changed by a finalizer, retry | |
return rehash!(h, newsz) | |
end | |
end | |
end | |
h.slots = slots | |
h.keys = keys | |
h.vals = vals | |
h.count = count | |
h.ndel = 0 | |
h.maxprobe = maxprobe | |
@assert h.age == age0 | |
return h | |
end | |
function sizehint!(d::Dict, newsz) | |
oldsz = length(d.slots) | |
if newsz <= oldsz | |
# todo: shrink | |
# be careful: rehash!() assumes everything fits. it was only designed | |
# for growing. | |
return d | |
end | |
# grow at least 25% | |
newsz = max(newsz, (oldsz*5)>>2) | |
rehash!(d, newsz) | |
end | |
function empty!{K,V}(h::Dict{K,V}) | |
fill!(h.slots, 0x0) | |
sz = length(h.slots) | |
empty!(h.keys) | |
empty!(h.vals) | |
resize!(h.keys, sz) | |
resize!(h.vals, sz) | |
h.ndel = 0 | |
h.count = 0 | |
h.age += 1 | |
h.idxfloor = 1 | |
return h | |
end | |
# get the index where a key is stored, or -1 if not present | |
function ht_keyindex{K,V}(h::Dict{K,V}, key) | |
sz = length(h.keys) | |
iter = 0 | |
maxprobe = h.maxprobe | |
index = hashindex(key, sz) | |
keys = h.keys | |
while true | |
if isslotempty(h,index) | |
break | |
end | |
if !isslotmissing(h,index) && (key === keys[index] || isequal(key,keys[index])) | |
return index | |
end | |
index = (index & (sz-1)) + 1 | |
iter += 1 | |
iter > maxprobe && break | |
end | |
return -1 | |
end | |
# get the index where a key is stored, or -pos if not present | |
# and the key would be inserted at pos | |
# This version is for use by setindex! and get! | |
function ht_keyindex2{K,V}(h::Dict{K,V}, key) | |
age0 = h.age | |
sz = length(h.keys) | |
iter = 0 | |
maxprobe = h.maxprobe | |
index = hashindex(key, sz) | |
avail = 0 | |
keys = h.keys | |
while true | |
if isslotempty(h,index) | |
if avail < 0 | |
return avail | |
end | |
return -index | |
end | |
if isslotmissing(h,index) | |
if avail == 0 | |
# found an available slot, but need to keep scanning | |
# in case "key" already exists in a later collided slot. | |
avail = -index | |
end | |
elseif key === keys[index] || isequal(key, keys[index]) | |
return index | |
end | |
index = (index & (sz-1)) + 1 | |
iter += 1 | |
iter > maxprobe && break | |
end | |
avail < 0 && return avail | |
maxallowed = max(maxallowedprobe, sz>>maxprobeshift) | |
# Check if key is not present, may need to keep searching to find slot | |
while iter < maxallowed | |
if !isslotfilled(h,index) | |
h.maxprobe = iter | |
return -index | |
end | |
index = (index & (sz-1)) + 1 | |
iter += 1 | |
end | |
rehash!(h, h.count > 64000 ? sz*2 : sz*4) | |
return ht_keyindex2(h, key) | |
end | |
function _setindex!(h::Dict, v, key, index) | |
h.slots[index] = 0x1 | |
h.keys[index] = key | |
h.vals[index] = v | |
h.count += 1 | |
h.age += 1 | |
if index < h.idxfloor | |
h.idxfloor = index | |
end | |
sz = length(h.keys) | |
# Rehash now if necessary | |
if h.ndel >= ((3*sz)>>2) || h.count*3 > sz*2 | |
# > 3/4 deleted or > 2/3 full | |
rehash!(h, h.count > 64000 ? h.count*2 : h.count*4) | |
end | |
end | |
function setindex!{K,V}(h::Dict{K,V}, v0, key0) | |
key = convert(K, key0) | |
if !isequal(key, key0) | |
throw(ArgumentError("$key0 is not a valid key for type $K")) | |
end | |
setindex!(h, v0, key) | |
end | |
function setindex!{K,V}(h::Dict{K,V}, v0, key::K) | |
v = convert(V, v0) | |
index = ht_keyindex2(h, key) | |
if index > 0 | |
h.age += 1 | |
h.keys[index] = key | |
h.vals[index] = v | |
else | |
_setindex!(h, v, key, -index) | |
end | |
return h | |
end | |
get!{K,V}(h::Dict{K,V}, key0, default) = get!(()->default, h, key0) | |
function get!{K,V}(default::Callable, h::Dict{K,V}, key0) | |
key = convert(K, key0) | |
if !isequal(key, key0) | |
throw(ArgumentError("$key0 is not a valid key for type $K")) | |
end | |
return get!(default, h, key) | |
end | |
function get!{K,V}(default::Callable, h::Dict{K,V}, key::K) | |
index = ht_keyindex2(h, key) | |
index > 0 && return h.vals[index] | |
age0 = h.age | |
v = convert(V, default()) | |
if h.age != age0 | |
index = ht_keyindex2(h, key) | |
end | |
if index > 0 | |
h.age += 1 | |
h.keys[index] = key | |
h.vals[index] = v | |
else | |
_setindex!(h, v, key, -index) | |
end | |
return v | |
end | |
# NOTE: this macro is trivial, and should | |
# therefore not be exported as-is: it's for internal use only. | |
macro get!(h, key0, default) | |
return quote | |
get!(()->$(esc(default)), $(esc(h)), $(esc(key0))) | |
end | |
end | |
function getindex{K,V}(h::Dict{K,V}, key) | |
index = ht_keyindex(h, key) | |
return (index < 0) ? throw(KeyError(key)) : h.vals[index]::V | |
end | |
function get{K,V}(h::Dict{K,V}, key, default) | |
index = ht_keyindex(h, key) | |
return (index < 0) ? default : h.vals[index]::V | |
end | |
function get{K,V}(default::Callable, h::Dict{K,V}, key) | |
index = ht_keyindex(h, key) | |
return (index < 0) ? default() : h.vals[index]::V | |
end | |
""" | |
haskey(collection, key) -> Bool | |
Determine whether a collection has a mapping for a given key. | |
```jldoctest | |
julia> a = Dict('a'=>2, 'b'=>3) | |
Dict{Char,Int64} with 2 entries: | |
'b' => 3 | |
'a' => 2 | |
julia> haskey(a,'a') | |
true | |
julia> haskey(a,'c') | |
false | |
``` | |
""" | |
haskey(h::Dict, key) = (ht_keyindex(h, key) >= 0) | |
in{T<:Dict}(key, v::KeyIterator{T}) = (ht_keyindex(v.dict, key) >= 0) | |
""" | |
getkey(collection, key, default) | |
Return the key matching argument `key` if one exists in `collection`, otherwise return `default`. | |
```jldoctest | |
julia> a = Dict('a'=>2, 'b'=>3) | |
Dict{Char,Int64} with 2 entries: | |
'b' => 3 | |
'a' => 2 | |
julia> getkey(a,'a',1) | |
'a' | |
julia> getkey(a,'d','a') | |
'a' | |
``` | |
""" | |
function getkey{K,V}(h::Dict{K,V}, key, default) | |
index = ht_keyindex(h, key) | |
return (index<0) ? default : h.keys[index]::K | |
end | |
function _pop!(h::Dict, index) | |
val = h.vals[index] | |
_delete!(h, index) | |
return val | |
end | |
function pop!(h::Dict, key) | |
index = ht_keyindex(h, key) | |
return index > 0 ? _pop!(h, index) : throw(KeyError(key)) | |
end | |
function pop!(h::Dict, key, default) | |
index = ht_keyindex(h, key) | |
return index > 0 ? _pop!(h, index) : default | |
end | |
function _delete!(h::Dict, index) | |
h.slots[index] = 0x2 | |
ccall(:jl_arrayunset, Void, (Any, UInt), h.keys, index-1) | |
ccall(:jl_arrayunset, Void, (Any, UInt), h.vals, index-1) | |
h.ndel += 1 | |
h.count -= 1 | |
h.age += 1 | |
return h | |
end | |
function delete!(h::Dict, key) | |
index = ht_keyindex(h, key) | |
if index > 0 | |
_delete!(h, index) | |
end | |
return h | |
end | |
function skip_deleted(h::Dict, i) | |
L = length(h.slots) | |
while i<=L && !isslotfilled(h,i) | |
i += 1 | |
end | |
return i | |
end | |
function start(t::Dict) | |
i = skip_deleted(t, t.idxfloor) | |
t.idxfloor = i | |
return i | |
end | |
done(t::Dict, i) = i > length(t.vals) | |
next{K,V}(t::Dict{K,V}, i) = (Pair{K,V}(t.keys[i],t.vals[i]), skip_deleted(t,i+1)) | |
isempty(t::Dict) = (t.count == 0) | |
length(t::Dict) = t.count | |
next{T<:Dict}(v::KeyIterator{T}, i) = (v.dict.keys[i], skip_deleted(v.dict,i+1)) | |
next{T<:Dict}(v::ValueIterator{T}, 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) | |
end | |
end | |
return d | |
end | |
immutable ImmutableDict{K, V} <: Associative{K,V} | |
parent::ImmutableDict{K, V} | |
key::K | |
value::V | |
ImmutableDict() = new() # represents an empty dictionary | |
ImmutableDict(key, value) = (empty = new(); new(empty, key, value)) | |
ImmutableDict(parent::ImmutableDict, key, value) = new(parent, key, value) | |
end | |
""" | |
ImmutableDict | |
ImmutableDict is a Dictionary implemented as an immutable linked list, | |
which is optimal for small dictionaries that are constructed over many individual insertions | |
Note that it is not possible to remove a value, although it can be partially overridden and hidden | |
by inserting a new value with the same key | |
ImmutableDict(KV::Pair) | |
Create a new entry in the Immutable Dictionary for the key => value pair | |
- use `(key => value) in dict` to see if this particular combination is in the properties set | |
- use `get(dict, key, default)` to retrieve the most recent value for a particular key | |
""" | |
ImmutableDict | |
ImmutableDict{K,V}(KV::Pair{K,V}) = ImmutableDict{K,V}(KV[1], KV[2]) | |
ImmutableDict{K,V}(t::ImmutableDict{K,V}, KV::Pair) = ImmutableDict{K,V}(t, KV[1], KV[2]) | |
function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) | |
key, value = key_value | |
while isdefined(dict, :parent) | |
if dict.key == key | |
valcmp(value, dict.value) && return true | |
end | |
dict = dict.parent | |
end | |
return false | |
end | |
function haskey(dict::ImmutableDict, key) | |
while isdefined(dict, :parent) | |
dict.key == key && return true | |
dict = dict.parent | |
end | |
return false | |
end | |
function getindex(dict::ImmutableDict, key) | |
while isdefined(dict, :parent) | |
dict.key == key && return dict.value | |
dict = dict.parent | |
end | |
throw(KeyError(key)) | |
end | |
function get(dict::ImmutableDict, key, default) | |
while isdefined(dict, :parent) | |
dict.key == key && return dict.value | |
dict = dict.parent | |
end | |
return default | |
end | |
# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) | |
start(t::ImmutableDict) = t | |
next{K,V}(::ImmutableDict{K,V}, t) = (Pair{K,V}(t.key, t.value), t.parent) | |
done(::ImmutableDict, t) = !isdefined(t, :parent) | |
length(t::ImmutableDict) = count(x->1, t) | |
isempty(t::ImmutableDict) = done(t, start(t)) | |
function similar(t::ImmutableDict) | |
while isdefined(t, :parent) | |
t = t.parent | |
end | |
return t | |
end | |
_similar_for{P<:Pair}(c::Dict, ::Type{P}, itr, isz) = similar(c, P) | |
_similar_for(c::Associative, T, itr, isz) = throw(ArgumentError("for Associatives, similar requires an element type of Pair;\n if calling map, consider a comprehension instead")) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module BaseDocs | |
immutable Keyword | |
name :: Symbol | |
end | |
macro kw_str(text) Keyword(Symbol(text)) end | |
"Hello, Human." | |
kw"hello", kw"hi" | |
""" | |
**Welcome to Julia $(string(VERSION)).** The full manual is available at | |
http://docs.julialang.org/ | |
as well many great tutorials and learning resources: | |
http://julialang.org/learning/ | |
For help on a specific function or macro, type `?` followed | |
by its name, e.g. `?fft`, or `?@time`, and press enter. | |
""" | |
kw"help", kw"?", kw"julia" | |
""" | |
`using` will load the given module or package and make some of its names available for | |
use (see also `export`). For example: | |
using Gadfly | |
loads the plotting package, Gadfly, so that the `plot` function can be used. | |
Names can be used via dot syntax, whether they are exported or not: | |
Gadfly.plot(...) | |
If you don't want to use the packages exports directly, see also `import`. If you're not | |
sure, `using` is almost definitely what you want. | |
""" | |
kw"using" | |
""" | |
import Gadfly | |
`import`, like `using`, will load modules and packages for use. Unlike `using`, however, | |
it will *not* make any `export`ed names available for use. To use Gadfly's `plot` | |
function after importing it, for example, you have to write: | |
Gadfly.plot(...) | |
Import can also be used with specific names, for example | |
import Gadfly: plot, render | |
This syntax is used when you want to extend the modules functions with new methods. | |
""" | |
kw"import" | |
""" | |
`export` is used within modules and packages to tell Julia which functions should be | |
made available to the user. For example: | |
module Test | |
export foo # foo is exported, but bar isn't | |
foo(x) = x | |
bar(y) = y | |
end | |
using Test | |
foo(1) # 1 | |
bar(1) # Error: bar not defined | |
Test.bar(1) # 1 | |
""" | |
kw"export" | |
""" | |
`abstract` declares a type that cannot be instantiated, and serves only as a node in the | |
type graph, thereby describing sets of related concrete types: those concrete types | |
which are their descendants. Abstract types form the conceptual hierarchy which makes | |
Julia’s type system more than just a collection of object implementations. For example: | |
abstract Number | |
abstract Real <: Number | |
`abstract Number` has no supertype, whereas `abstract Real` is an abstract subtype of `Number`. | |
""" | |
kw"abstract" | |
""" | |
`module` declares a Module, which is a separate global variable workspace. Within a | |
module, you can control which names from other modules are visible (via importing), and | |
specify which of your names are intended to be public (via exporting). For example: | |
module | |
import Base.show | |
export MyType, foo | |
type MyType | |
x | |
end | |
bar(x) = 2x | |
foo(a::MyType) = bar(a.x) + 1 | |
show(io, a::MyType) = print(io, "MyType \$(a.x)") | |
end | |
Modules allow you to create top-level definitions without worrying about name conflicts | |
when your code is used together with somebody else’s. | |
""" | |
kw"module" | |
""" | |
`baremodule` declares a module that does not contain `using Base` | |
or a definition of `eval`. It does still import `Core`. | |
""" | |
kw"baremodule" | |
""" | |
`bitstype` declares a concrete type whose data consists of plain old bits. Classic | |
examples of bits types are integers and floating-point values. Some example built-in | |
bits type declarations: | |
bitstype 32 Char | |
bitstype 8 Bool <: Integer | |
The first parameter indicates how many bits of storage the type requires. Currently, | |
only sizes that are multiples of 8 bits are supported. The second parameter gives the | |
name of the type. The `Bool` declaration shows how a bits type can be optionally | |
declared to be a subtype of some supertype. | |
""" | |
kw"bitstype" | |
""" | |
`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 | |
include expressions, literal values, and symbols. For example: | |
macro sayhello(name) | |
return :( println("Hello, ", \$name) ) | |
end | |
This macro takes one argument: `name`. When `@sayhello` is encountered, the quoted | |
expression is expanded to interpolate the value of the argument into the final | |
expression. | |
""" | |
kw"macro" | |
""" | |
`importall` imports all names exported by the specified module, as if `import` were used | |
individually on all of them. For example: | |
importall Distributions | |
As with `import`, functions imported by `importall` can be extended. | |
""" | |
kw"importall" | |
""" | |
`local` introduces a new local variable. For example: | |
function foo(n) | |
x = 0 | |
for i = 1:n | |
local x | |
x = i | |
end | |
x | |
end | |
julia> foo(10) | |
0 | |
Here `local x` introduces a separate `x` inside the loop, so the function returns `0`. | |
""" | |
kw"local" | |
""" | |
`global x` makes `x` in the current scope and its inner scopes refer to the global | |
variable of that name. In the example below, `global` is needed so the function can | |
modify the global variable `z`: | |
z=3 | |
function foo() | |
global z=6 | |
end | |
julia> foo() | |
6 | |
julia> z | |
6 | |
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" | |
""" | |
`let` statements allocate new variable bindings each time they run. Whereas an | |
assignment modifies an existing value location, `let` creates new locations. This | |
difference is only detectable in the case of variables that outlive their scope via | |
closures. The `let` syntax accepts a comma-separated series of assignments and variable | |
names: | |
let var1 = value1, var2, var3 = value3 | |
code | |
end | |
The assignments are evaluated in order, with each right-hand side evaluated in the scope | |
before the new variable on the left-hand side has been introduced. Therefore it makes | |
sense to write something like `let x = x`, since the two `x` variables are distinct and | |
have separate storage. | |
""" | |
kw"let" | |
""" | |
`quote` creates multiple expression objects in a block without using the explicit `Expr` | |
constructor. For example: | |
ex = quote | |
x = 1 | |
y = 2 | |
x + y | |
end | |
Unlike the other means of quoting, `:( ... )`, this form introduces `QuoteNode` elements | |
to the expression tree, which must be considered when directly manipulating the tree. | |
For other purposes, `:( ... )` and `quote .. end` blocks are treated identically. | |
""" | |
kw"quote" | |
""" | |
`'` is the conjugate transposition operator: | |
julia> A = reshape(1:4, 2,2) | |
2×2 Array{Int64,2}: | |
1 3 | |
2 4 | |
julia> A' | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> B = A + im | |
2×2 Array{Complex{Int64},2}: | |
1+1im 3+1im | |
2+1im 4+1im | |
julia> B' | |
2×2 Array{Complex{Int64},2}: | |
1-1im 2-1im | |
3-1im 4-1im | |
""" | |
kw"'" | |
""" | |
`.'` is the transposition operator: | |
julia> A = reshape(1:4, 2,2) | |
2×2 Array{Int64,2}: | |
1 3 | |
2 4 | |
julia> A.' | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> B = A + im | |
2×2 Array{Complex{Int64},2}: | |
1+1im 3+1im | |
2+1im 4+1im | |
julia> B.' | |
2×2 Array{Complex{Int64},2}: | |
1+1im 2+1im | |
3+1im 4+1im | |
""" | |
kw".'" | |
""" | |
`const` is used to declare global variables which are also constant. In almost all code | |
(and particularly performance sensitive code) global variables should be declared | |
constant in this way. | |
const x = 5 | |
Note that "constant-ness" is not enforced inside containers, so if `x` is an array or | |
dictionary (for example) you can still add and remove elements. | |
Technically, you can even redefine `const` variables, although this will generate a | |
warning from the compiler. The only strict requirement is that the *type* of the | |
variable does not change, which is why `const` variables are much faster than regular | |
globals. | |
""" | |
kw"const" | |
""" | |
Functions are defined with the `function` keyword: | |
function add(a, b) | |
return a + b | |
end | |
Or the short form notation: | |
add(a, b) = a + b | |
The use of the `return` keyword is exactly the same as in other languages, but is often | |
optional. When it's not used, the last expression in the function body will be returned | |
by default: | |
function compare(a, b) | |
a == b && return "equal to" | |
a < b ? "less than" : "greater than" | |
end | |
""" | |
kw"function" | |
""" | |
`return` can be used function bodies to exit early and return a given value, e.g. | |
function compare(a, b) | |
a == b && return "equal to" | |
a < b ? "less than" : "greater than" | |
end | |
In general you can place a `return` statement anywhere within a function body, including | |
within deeply nested loops or conditionals, but be careful with `do` blocks. For | |
example: | |
function test1(xs) | |
for x in xs | |
iseven(x) && return 2x | |
end | |
end | |
function test2(xs) | |
map(xs) do x | |
iseven(x) && return 2x | |
x | |
end | |
end | |
In the first example, the return breaks out of its enclosing function as soon as it hits | |
an even number, so `test1([5,6,7])` returns `12`. | |
You might expect the second example to behave the same way, but in fact the `return` | |
there only breaks out of the *inner* function (inside the `do` block) and gives a value | |
back to `map`. `test2([5,6,7])` then returns `[5,12,7]`. | |
""" | |
kw"return" | |
""" | |
`if`-`elseif`-`else` performs conditional evaluation, which allows portions of code to | |
be evaluated or not evaluated depending on the value of a boolean expression. Here is | |
the anatomy of the `if`-`elseif`-`else` conditional syntax: | |
if x < y | |
println("x is less than y") | |
elseif x > y | |
println("x is greater than y") | |
else | |
println("x is equal to y") | |
end | |
If the condition expression `x < y` is true, then the corresponding block is evaluated; | |
otherwise the condition expression `x > y` is evaluated, and if it is true, the | |
corresponding block is evaluated; if neither expression is true, the `else` block is | |
evaluated. The `elseif` and `else` blocks are optional, and as many `elseif` blocks as | |
desired can be used. | |
""" | |
kw"if", kw"elseif", kw"else" | |
""" | |
`for` loops repeatedly evaluate the body of the loop by iterating over a sequence of | |
values. For example: | |
for i in [1,4,0] | |
println(i) | |
end | |
""" | |
kw"for" | |
""" | |
`while` loops repeatedly evaluate a conditional expression, and continues evaluating the | |
body of the while loop so long as the expression remains `true`. If the condition | |
expression is false when the while loop is first reached, the body is never evaluated. | |
For example: | |
while i <= 5 | |
println(i) | |
i += 1 | |
end | |
""" | |
kw"while" | |
""" | |
`end` marks the conclusion of a block of expressions. In the example below, `end` marks | |
the conclusion of a `function`. | |
function foo() | |
println("hello, world") | |
end | |
`end` marks the conclusion of all kinds of expression blocks: `module`, `type`, `begin`, | |
`let`, `for`, etc. | |
In addition, `end` may be used when indexing into an array to represent the last index | |
of each dimension: | |
x[1:end, 2:end-1] | |
""" | |
kw"end" | |
""" | |
A `try/catch` statement allows for `Exception`s to be tested for. For example, a | |
customized square root function can be written to automatically call either the real or | |
complex square root method on demand using `Exception`s: | |
f(x) = try | |
sqrt(x) | |
catch | |
sqrt(complex(x, 0)) | |
end | |
`try/catch` statements also allow the `Exception` to be saved in a variable, e.g. `catch y`. | |
The `catch` clause is not strictly necessary; when omitted, the default return value is | |
`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. | |
""" | |
kw"try", kw"catch" | |
""" | |
`finally` provides a way to run some code when a given block of code exits, regardless | |
of how it exits. For example, here is how we can guarantee that an opened file is | |
closed: | |
f = open("file") | |
try | |
operate_on_file(f) | |
finally | |
close(f) | |
end | |
When control leaves the `try` block (for example due to a `return`, or just finishing | |
normally), `close(f)` will be executed. If the `try` block exits due to an exception, | |
the exception will continue propagating. A `catch` block may be combined with `try` and | |
`finally` as well. In this case the `finally` block will run after `catch` has handled | |
the error. | |
""" | |
kw"finally" | |
""" | |
`break` breaks out of a loop immediately. For example | |
i = 0 | |
while true | |
i += 1 | |
i > 10 && break | |
println(i) | |
end | |
prints the numbers 1 to 10. | |
""" | |
kw"break" | |
""" | |
`continue` skips the rest of the current loop, then carries on looping. For example | |
for i = 1:10 | |
iseven(i) && continue | |
println(i) | |
end | |
prints the numbers 1, 3, 5..., skipping the even numbers. | |
""" | |
kw"continue" | |
""" | |
The `do` keyword creates an anonymous function. For example | |
map(1:10) do x | |
2x | |
end | |
is equivalent to `map(x->2x, 1:10)`. | |
Use multiple arguments like so: | |
map(1:10, 11:20) do x, y | |
x + y | |
end | |
""" | |
kw"do" | |
""" | |
The "splat" operator, `...`, represents a sequence of arguments. For example | |
add(xs...) = reduce(+, xs) | |
can take any number of arguments: | |
add(1, 2, 3, 4, 5) | |
`...` can also be used to apply a function to a sequence of arguments like so: | |
add([1, 2, 3]...) # 6 | |
add(7, 1:100..., 1000:1100...) # 111107 | |
""" | |
kw"..." | |
""" | |
`;` has a similar role in Julia as in many C-like languages, and is used to delimit the | |
end of the previous statement. `;` is not necessary after new lines, but can be used to | |
separate statements on a single line or to join statements into a single expression: | |
function foo() | |
println("Hello, "); println("World!") | |
return true | |
end | |
foo() = (println("Hello, World!"); true) | |
`;` is also used to suppress output in the REPL and similar interfaces. | |
""" | |
kw";" | |
""" | |
x && y | |
Short-circuiting boolean AND. | |
""" | |
kw"&&" | |
""" | |
x || y | |
Short-circuiting boolean OR. | |
""" | |
kw"||" | |
""" | |
ccall((symbol, library) or function_pointer, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) | |
Call function in C-exported shared library, specified by `(function name, library)` | |
tuple, where each component is a string or symbol. | |
Note that the argument type tuple must be a literal tuple, and not a tuple-valued | |
variable or expression. Alternatively, `ccall` may also be used to call a function | |
pointer, such as one returned by `dlsym`. | |
Each `ArgumentValue` to the `ccall` will be converted to the corresponding | |
`ArgumentType`, by automatic insertion of calls to `unsafe_convert(ArgumentType, | |
cconvert(ArgumentType, ArgumentValue))`. (See also the documentation for each of these | |
functions for further details.) In most cases, this simply results in a call to | |
`convert(ArgumentType, ArgumentValue)`. | |
""" | |
kw"ccall" | |
""" | |
llvmcall(IR::String, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) | |
llvmcall((declarations::String, IR::String), ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) | |
Call LLVM IR string in the first argument. Similar to an LLVM function `define` block, | |
arguments are available as consecutive unnamed SSA variables (%0, %1, etc.). | |
The optional declarations string contains external functions declarations that are | |
necessary for llvm to compile the IR string. Multiple declarations can be passed in by | |
separating them with line breaks. | |
Note that the argument type tuple must be a literal tuple, and not a tuple-valued | |
variable or expression. | |
Each `ArgumentValue` to `llvmcall` will be converted to the corresponding | |
`ArgumentType`, by automatic insertion of calls to `unsafe_convert(ArgumentType, | |
cconvert(ArgumentType, ArgumentValue))`. (see also the documentation for each of these | |
functions for further details). In most cases, this simply results in a call to | |
`convert(ArgumentType, ArgumentValue)`. | |
See `test/llvmcall.jl` for usage examples. | |
""" | |
Core.Intrinsics.llvmcall | |
""" | |
`begin...end` denotes a block of code. | |
begin | |
println("Hello, ") | |
println("World!") | |
end | |
Usually `begin` will not be necessary, since keywords such as `function` and `let` | |
implicitly begin blocks of code. See also `;`. | |
""" | |
kw"begin" | |
""" | |
At their most basic, Julia types are specified as a name and a set of fields. | |
type Point | |
x | |
y | |
end | |
Fields can have type restrictions, which may be parametrised: | |
type Point{X} | |
x::X | |
y::Float64 | |
end | |
Type can also declare an abstract super type via `<:` syntax: | |
type Point <: AbstractPoint | |
... | |
See the manual for more details, such as information on inner constructors. | |
""" | |
kw"type" | |
""" | |
Introduce a new name for an already expressible type. For example, in `base/boot.jl`, | |
`UInt` is type aliased to either `UInt64` or `UInt32` as appropriate for the size of | |
pointers on the system: | |
if Int === Int64 | |
typealias UInt UInt64 | |
else | |
typealias UInt UInt32 | |
end | |
For parametric types, `typealias` can be convenient for providing names in cases where | |
some parameter choices are fixed. In `base` for example: | |
typealias Vector{T} Array{T,1} | |
""" | |
kw"typealias" | |
""" | |
`immutable` acts in the same way as `type`, but declares that the fields of the type may | |
not be set after construction. See `type` and the manual for more information. | |
""" | |
kw"immutable" | |
""" | |
@__LINE__ -> Int | |
`@__LINE__` expands to the line number of the call-site. | |
""" | |
kw"@__LINE__" | |
""" | |
ans | |
A variable referring to the last computed value, automatically set at the interactive prompt. | |
""" | |
kw"ans" | |
""" | |
nothing | |
The singleton instance of type `Void`, used by convention when there is no value to return | |
(as in a C `void` function). Can be converted to an empty `Nullable` value. | |
""" | |
nothing | |
""" | |
ANY | |
Equivalent to `Any` for dispatch purposes, but signals the compiler to skip code | |
generation specialization for that field. | |
""" | |
ANY | |
""" | |
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 | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
export @var | |
immutable Binding | |
mod::Module | |
var::Symbol | |
function Binding(m::Module, v::Symbol) | |
# Normalise the binding module for module symbols so that: | |
# Binding(Base, :Base) === Binding(Main, :Base) | |
m = module_name(m) === v ? module_parent(m) : m | |
new(Base.binding_module(m, v), v) | |
end | |
end | |
bindingexpr(x) = Expr(:call, Binding, splitexpr(x)...) | |
defined(b::Binding) = isdefined(b.mod, b.var) | |
resolve(b::Binding) = getfield(b.mod, b.var) | |
function splitexpr(x::Expr) | |
isexpr(x, :macrocall) ? splitexpr(x.args[1]) : | |
isexpr(x, :.) ? (x.args[1], x.args[2]) : | |
error("Invalid @var syntax `$x`.") | |
end | |
splitexpr(s::Symbol) = Expr(:call, current_module), quot(s) | |
splitexpr(other) = error("Invalid @var syntax `$other`.") | |
macro var(x) | |
esc(bindingexpr(x)) | |
end | |
function Base.show(io::IO, b::Binding) | |
if b.mod === Main | |
print(io, b.var) | |
else | |
print(io, b.mod, '.', Base.isoperator(b.var) ? ":" : "", b.var) | |
end | |
end | |
aliasof(b::Binding) = defined(b) ? (a = aliasof(resolve(b), b); defined(a) ? a : b) : b | |
aliasof(d::DataType, b) = Binding(d.name.module, d.name.name) | |
aliasof(λ::Function, b) = (m = typeof(λ).name.mt; Binding(m.module, m.name)) | |
aliasof(m::Module, b) = Binding(m, module_name(m)) | |
aliasof(other, b) = b | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module CoreDocs | |
import ..esc, ..push!, ..getindex, ..current_module, ..unsafe_load, ..Csize_t | |
function doc!(str, ex) | |
ptr = unsafe_load(Core.Intrinsics.cglobal(:jl_filename, Ptr{UInt8})) | |
len = ccall(:strlen, Csize_t, (Ptr{UInt8},), ptr) | |
file = ccall(:jl_symbol_n, Any, (Ptr{UInt8}, Int32), ptr, len) | |
line = unsafe_load(Core.Intrinsics.cglobal(:jl_lineno, Int32)) # Cint | |
push!(DOCS, (current_module(), ex, str, file, line)) | |
end | |
const DOCS = Array{Any, 1}() | |
isexpr(x, h) = isa(x, Expr) && x.head === h | |
lazy_iterpolate(s::AbstractString) = Expr(:call, Core.svec, s) | |
lazy_iterpolate(x) = isexpr(x, :string) ? Expr(:call, Core.svec, x.args...) : x | |
function docm(str, x) | |
out = esc(Expr(:call, doc!, lazy_iterpolate(str), Expr(:quote, x))) | |
isexpr(x, :module) ? Expr(:toplevel, out, esc(x)) : | |
isexpr(x, :call) ? out : Expr(:block, esc(x), out) | |
end | |
docm(x) = isexpr(x, :->) ? docm(x.args[1], x.args[2].args[2]) : error("invalid '@doc'.") | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
""" | |
The Docs module provides the `@doc` macro which can be used to set and retrieve | |
documentation metadata for Julia objects. Please see docs for the `@doc` macro for more | |
information. | |
""" | |
module Docs | |
""" | |
# Documentation | |
Functions, methods and types can be documented by placing a string before the definition: | |
\""" | |
# The Foo Function | |
`foo(x)`: Foo the living hell out of `x`. | |
\""" | |
foo(x) = ... | |
The `@doc` macro can be used directly to both set and retrieve documentation / metadata. By | |
default, documentation is written as Markdown, but any object can be placed before the | |
arrow. For example: | |
@doc "blah" -> | |
function foo() ... | |
The `->` is not required if the object is on the same line, e.g. | |
@doc "foo" foo | |
## Documenting objects after they are defined | |
You can document an object after its definition by | |
@doc "foo" function_to_doc | |
@doc "bar" TypeToDoc | |
For macros, the syntax is `@doc "macro doc" :(@Module.macro)` or `@doc "macro doc" | |
:(string_macro"")` for string macros. Without the quote `:()` the expansion of the macro | |
will be documented. | |
## Retrieving Documentation | |
You can retrieve docs for functions, macros and other objects as follows: | |
@doc foo | |
@doc @time | |
@doc md"" | |
## Functions & Methods | |
Placing documentation before a method definition (e.g. `function foo() ...` or `foo() = ...`) | |
will cause that specific method to be documented, as opposed to the whole function. Method | |
docs are concatenated together in the order they were defined to provide docs for the | |
function. | |
""" | |
:(Core.@doc) | |
include("bindings.jl") | |
import Base.Markdown: @doc_str, MD | |
import Base.Meta: quot, isexpr | |
import Base: Callable | |
import ..CoreDocs: lazy_iterpolate | |
export doc | |
# Basic API / Storage | |
const modules = Module[] | |
const META = gensym(:meta) | |
meta(m::Module = current_module()) = isdefined(m, META) ? getfield(m, META) : ObjectIdDict() | |
function initmeta(m::Module = current_module()) | |
if !isdefined(m, META) | |
eval(m, :(const $META = $(ObjectIdDict()))) | |
push!(modules, m) | |
end | |
nothing | |
end | |
function signature(expr::Expr) | |
if isexpr(expr, [:call, :macrocall]) | |
sig = :(Union{Tuple{}}) | |
for arg in expr.args[2:end] | |
isexpr(arg, :parameters) && continue | |
if isexpr(arg, :kw) # optional arg | |
push!(sig.args, :(Tuple{$(sig.args[end].args[2:end]...)})) | |
end | |
push!(sig.args[end].args, argtype(arg)) | |
end | |
Expr(:let, Expr(:block, sig), typevars(expr)...) | |
else | |
signature(expr.args[1]) | |
end | |
end | |
signature(other) = :(Union{}) | |
function argtype(expr::Expr) | |
isexpr(expr, :(::)) && return expr.args[end] | |
isexpr(expr, :(...)) && return :(Vararg{$(argtype(expr.args[1]))}) | |
argtype(expr.args[1]) | |
end | |
argtype(other) = :Any | |
function typevars(expr::Expr) | |
isexpr(expr, :curly) && return [tvar(x) for x in expr.args[2:end]] | |
typevars(expr.args[1]) | |
end | |
typevars(::Symbol) = [] | |
tvar(x::Expr) = :($(x.args[1]) = TypeVar($(quot(x.args[1])), $(x.args[2]), true)) | |
tvar(s::Symbol) = :($(s) = TypeVar($(quot(s)), Any, true)) | |
# Docsystem types. | |
# ================ | |
""" | |
Docs.DocStr | |
Stores the contents of a single docstring as well as related metadata. | |
Both the raw text, `.text`, and the parsed markdown, `.object`, are tracked by this type. | |
Parsing of the raw text is done lazily when a request is made to render the docstring, | |
which helps to reduce total precompiled image size. | |
The `.data` fields stores several values related to the docstring, such as: path, | |
linenumber, source code, and fielddocs. | |
""" | |
type DocStr | |
text :: Core.SimpleVector | |
object :: Nullable | |
data :: Dict{Symbol, Any} | |
end | |
function docstr(binding::Binding, typesig::ANY = Union{}) | |
for m in modules | |
dict = meta(m) | |
if haskey(dict, binding) | |
docs = dict[binding].docs | |
if haskey(docs, typesig) | |
return docs[typesig] | |
end | |
end | |
end | |
error("could not find matching docstring for '$binding :: $typesig'.") | |
end | |
docstr(object, data = Dict()) = _docstr(object, data) | |
_docstr(vec::Core.SimpleVector, data) = DocStr(vec, Nullable(), data) | |
_docstr(str::AbstractString, data) = DocStr(Core.svec(str), Nullable(), data) | |
_docstr(object, data) = DocStr(Core.svec(), Nullable(object), data) | |
_docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc) | |
macro ref(x) | |
binding = bindingexpr(namify(x)) | |
typesig = signature(x) | |
esc(docexpr(binding, typesig)) | |
end | |
docexpr(args...) = Expr(:call, docstr, args...) | |
function formatdoc(d::DocStr) | |
buffer = IOBuffer() | |
for part in d.text | |
formatdoc(buffer, d, part) | |
end | |
Markdown.parse(seekstart(buffer)) | |
end | |
@noinline formatdoc(buffer, d, part) = print(buffer, part) | |
function parsedoc(d::DocStr) | |
if isnull(d.object) | |
md = formatdoc(d) | |
md.meta[:module] = d.data[:module] | |
md.meta[:path] = d.data[:path] | |
d.object = Nullable(md) | |
end | |
get(d.object) | |
end | |
""" | |
MultiDoc | |
Stores a collection of docstrings for related objects, ie. a `Function`/`DataType` and | |
associated `Method` objects. | |
Each documented object in a `MultiDoc` is referred to by it's signature which is represented | |
by a `Union` of `Tuple` types. For example the following `Method` definition | |
f(x, y) = ... | |
is stored as `Tuple{Any, Any}` in the `MultiDoc` while | |
f{T}(x::T, y = ?) = ... | |
is stored as `Union{Tuple{T}, Tuple{T, Any}}`. | |
Note: The `Function`/`DataType` object's signature is always `Union{}`. | |
""" | |
type MultiDoc | |
"Ordered (via definition order) vector of object signatures." | |
order::Vector{Type} | |
"Documentation for each object. Keys are signatures." | |
docs::ObjectIdDict | |
MultiDoc() = new(Type[], ObjectIdDict()) | |
end | |
# Docstring registration. | |
# ======================= | |
""" | |
Docs.doc!(binding, str, sig) | |
Adds a new docstring `str` to the docsystem for `binding` and signature `sig`. | |
""" | |
function doc!(b::Binding, str::DocStr, sig::ANY = Union{}) | |
initmeta() | |
m = get!(meta(), b, MultiDoc()) | |
if haskey(m.docs, sig) | |
# We allow for docstrings to be updated, but print a warning since it is possible | |
# that over-writing a docstring *may* have been accidental. | |
s = "replacing docs for '$b :: $sig' in module '$(current_module())'." | |
isdefined(Base, :STDERR) ? warn(s) : ccall(:jl_, Void, (Any,), "WARNING: $s") | |
else | |
# The ordering of docstrings for each Binding is defined by the order in which they | |
# are initially added. Replacing a specific docstring does not change it's ordering. | |
push!(m.order, sig) | |
end | |
m.docs[sig] = str | |
str.data[:binding] = b | |
str.data[:typesig] = sig | |
return b | |
end | |
# Docstring lookup. | |
# ================= | |
""" | |
Docs.doc(binding, sig) | |
Returns all documentation that matches both `binding` and `sig`. | |
""" | |
function doc(binding::Binding, sig::Type = Union{}) | |
results, groups = DocStr[], MultiDoc[] | |
# Lookup `binding` and `sig` for matches in all modules of the docsystem. | |
for mod in modules | |
dict = meta(mod) | |
if haskey(dict, binding) | |
multidoc = dict[binding] | |
push!(groups, multidoc) | |
for msig in multidoc.order | |
sig <: msig && push!(results, multidoc.docs[msig]) | |
end | |
end | |
end | |
if isempty(groups) | |
# When no `MultiDoc`s are found that match `binding` then we check whether `binding` | |
# is an alias of some other `Binding`. When it is we then re-run `doc` with that | |
# `Binding`, otherwise if it's not an alias then we generate a summary for the | |
# `binding` and display that to the user instead. | |
alias = aliasof(binding) | |
alias == binding ? summarize(alias, sig) : doc(alias, sig) | |
else | |
# There was at least one match for `binding` while searching. If there weren't any | |
# matches for `sig` then we concatenate *all* the docs from the matching `Binding`s. | |
if isempty(results) | |
for group in groups, each in group.order | |
push!(results, group.docs[each]) | |
end | |
end | |
# Get parsed docs and concatenate them. | |
md = catdoc(map(parsedoc, results)...) | |
# Save metadata in the generated markdown. | |
if isa(md, Markdown.MD) | |
md.meta[:results] = results | |
md.meta[:binding] = binding | |
md.meta[:typesig] = sig | |
end | |
return md | |
end | |
end | |
# Some additional convenience `doc` methods that take objects rather than `Binding`s. | |
doc(object, sig::Type = Union{}) = doc(aliasof(object, typeof(object)), sig) | |
doc(object, sig...) = doc(object, Tuple{sig...}) | |
""" | |
Docs.fielddoc(binding, field) | |
Returns documentation for a particular `field` of a type if it exists. | |
""" | |
function fielddoc(binding::Binding, field::Symbol) | |
for mod in modules | |
dict = meta(mod) | |
if haskey(dict, binding) | |
multidoc = dict[binding] | |
if haskey(multidoc.docs, Union{}) | |
fields = multidoc.docs[Union{}].data[:fields] | |
if haskey(fields, field) | |
doc = fields[field] | |
return isa(doc, Markdown.MD) ? doc : Markdown.parse(doc) | |
end | |
end | |
end | |
end | |
fields = join(["`$f`" for f in fieldnames(resolve(binding))], ", ", ", and ") | |
fields = isempty(fields) ? "no fields" : "fields $fields" | |
Markdown.parse("`$(resolve(binding))` has $fields.") | |
end | |
# As with the additional `doc` methods, this converts an object to a `Binding` first. | |
fielddoc(object, field::Symbol) = fielddoc(aliasof(object, typeof(object)), field) | |
# Object Summaries. | |
# ================= | |
function summarize(binding::Binding, sig) | |
io = IOBuffer() | |
println(io, "No documentation found.\n") | |
if defined(binding) | |
summarize(io, resolve(binding), binding) | |
else | |
println(io, "Binding `", binding, "` does not exist.") | |
end | |
md = Markdown.parse(seekstart(io)) | |
# Save metadata in the generated markdown. | |
md.meta[:results] = DocStr[] | |
md.meta[:binding] = binding | |
md.meta[:typesig] = sig | |
return md | |
end | |
function summarize(io::IO, λ::Function, binding) | |
kind = startswith(string(binding.var), '@') ? "macro" : "`Function`" | |
println(io, "`", binding, "` is a ", kind, ".") | |
println(io, "```\n", methods(λ), "\n```") | |
end | |
function summarize(io::IO, T::DataType, binding) | |
println(io, "**Summary:**") | |
println(io, "```") | |
println(io, | |
T.abstract ? "abstract" : T.mutable ? "type" : "immutable", | |
" ", T, " <: ", supertype(T) | |
) | |
println(io, "```") | |
if !isempty(fieldnames(T)) | |
println(io, "**Fields:**") | |
println(io, "```") | |
pad = maximum(length(string(f)) for f in fieldnames(T)) | |
for (f, t) in zip(fieldnames(T), T.types) | |
println(io, rpad(f, pad), " :: ", t) | |
end | |
println(io, "```") | |
end | |
if !isempty(subtypes(T)) | |
println(io, "**Subtypes:**") | |
println(io, "```") | |
for t in subtypes(T) | |
println(io, t) | |
end | |
println(io, "```") | |
end | |
end | |
function summarize(io::IO, m::Module, binding) | |
readme = Pkg.dir(string(m), "README.md") | |
if isfile(readme) | |
println(io, "Displaying the `README.md` for the module instead.\n") | |
println(io, "---\n") | |
println(io, readstring(readme)) | |
else | |
println(io, "No `README.md` found for module `", m, "`.\n") | |
end | |
end | |
function summarize{T}(io::IO, ::T, binding) | |
println(io, "`", binding, "` is of type `", T, "`.\n") | |
summarize(io, T, binding) | |
end | |
# Utilities. | |
# ========== | |
""" | |
`catdoc(xs...)`: Combine the documentation metadata `xs` into a single meta object. | |
""" | |
catdoc() = nothing | |
catdoc(xs...) = vcat(xs...) | |
const keywords = Dict{Symbol, DocStr}() | |
isdoc(s::AbstractString) = true | |
isdoc(x) = isexpr(x, :string) || | |
(isexpr(x, :macrocall) && x.args[1] === Symbol("@doc_str")) || | |
(isexpr(x, :call) && x.args[1] === Base.Markdown.doc_str) | |
function unblock(ex) | |
isexpr(ex, :block) || return ex | |
exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args) | |
length(exs) == 1 || return ex | |
return unblock(exs[1]) | |
end | |
uncurly(ex) = isexpr(ex, :curly) ? ex.args[1] : ex | |
namify(x) = nameof(x, isexpr(x, :macro)) | |
function nameof(x::Expr, ismacro) | |
if isexpr(x, :.) | |
ismacro ? macroname(x) : x | |
else | |
n = isexpr(x, [:module, :type, :bitstype]) ? 2 : 1 | |
nameof(x.args[n], ismacro) | |
end | |
end | |
nameof(q::QuoteNode, ismacro) = nameof(q.value, ismacro) | |
nameof(s::Symbol, ismacro) = ismacro ? macroname(s) : s | |
nameof(other, ismacro) = other | |
macroname(s::Symbol) = Symbol('@', s) | |
macroname(x::Expr) = Expr(x.head, x.args[1], macroname(x.args[end].value)) | |
isfield(x) = isexpr(x, :.) && | |
(isa(x.args[1], Symbol) || isfield(x.args[1])) && | |
(isa(x.args[2], QuoteNode) || isexpr(x.args[2], :quote)) | |
# @doc expression builders. | |
# ========================= | |
""" | |
Docs.metadata(expr) | |
Build a `Dict` expression containing metadata captured from the expression `expr`. | |
Fields that may be included in the returned `Dict`: | |
- `:path`: String representing the file where `expr` is defined. | |
- `:linenumber`: Linenumber where `expr` is defined. | |
- `:module`: Module where the docstring is defined. | |
- `:fields`: `Dict` of all field docs found in `expr`. Only for concrete types. | |
""" | |
function metadata(expr) | |
args = [] | |
# Filename and linenumber of the docstring. | |
push!(args, :($(Pair)(:path, $(Base).@__FILE__))) | |
push!(args, :($(Pair)(:linenumber, $(unsafe_load(cglobal(:jl_lineno, Cint)))))) | |
# Module in which the docstring is defined. | |
push!(args, :($(Pair)(:module, $(current_module)()))) | |
# Field docs for concrete types. | |
if isexpr(expr, :type) | |
fields = [] | |
tmp = nothing | |
for each in expr.args[3].args | |
if isdoc(each) | |
tmp = each | |
elseif tmp !== nothing && (isa(each, Symbol) || isexpr(each, :(::))) | |
push!(fields, (namify(each), tmp)) | |
tmp = nothing | |
end | |
end | |
dict = :($(Dict)($([:($(Pair)($(quot(f)), $d)) for (f, d) in fields]...))) | |
push!(args, :($(Pair)(:fields, $dict))) | |
end | |
:($(Dict)($(args...))) | |
end | |
function keyworddoc(str, def) | |
docstr = esc(docexpr(lazy_iterpolate(str), metadata(def))) | |
:($(keywords)[$(esc(quot(def.name)))] = $docstr) | |
end | |
function objectdoc(str, def, expr, sig = :(Union{})) | |
binding = esc(bindingexpr(namify(expr))) | |
docstr = esc(docexpr(lazy_iterpolate(str), metadata(expr))) | |
quote | |
$(esc(def)) | |
$(doc!)($binding, $docstr, $(esc(sig))) | |
end | |
end | |
function calldoc(str, def) | |
args = def.args[2:end] | |
if isempty(args) || all(validcall, args) | |
objectdoc(str, nothing, def, signature(def)) | |
else | |
docerror(def) | |
end | |
end | |
validcall(x) = isa(x, Symbol) || isexpr(x, [:(::), :..., :kw, :parameters]) | |
function moduledoc(meta, def, def′) | |
name = namify(def′) | |
docex = Expr(:call, doc!, bindingexpr(name), | |
docexpr(lazy_iterpolate(meta), metadata(name)) | |
) | |
if def === nothing | |
esc(:(eval($name, $(quot(docex))))) | |
else | |
def = unblock(def) | |
block = def.args[3].args | |
if !def.args[1] | |
isempty(block) && error("empty baremodules are not documentable.") | |
insert!(block, 2, :(import Base: @doc)) | |
end | |
push!(block, docex) | |
esc(Expr(:toplevel, def)) | |
end | |
end | |
# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`. | |
function multidoc(meta, ex, define) | |
out = Expr(:toplevel) | |
str = docexpr(lazy_iterpolate(meta), metadata(ex)) | |
ref = Ref{DocStr}() | |
for (n, arg) in enumerate(ex.args) | |
# The first `arg` to be documented needs to also create the docstring for the group. | |
# Subsequent `arg`s just need `ref` to be able to find the docstring without having | |
# to create an entirely new one each. | |
docstr = n === 1 ? :($(ref)[] = $str) : :($(ref)[]) | |
push!(out.args, :(@doc($docstr, $arg, $define))) | |
end | |
esc(out) | |
end | |
""" | |
@__doc__(ex) | |
Low-level macro used to mark expressions returned by a macro that should be documented. If | |
more than one expression is marked then the same docstring is applied to each expression. | |
macro example(f) | |
quote | |
\$(f)() = 0 | |
@__doc__ \$(f)(x) = 1 | |
\$(f)(x, y) = 2 | |
end |> esc | |
end | |
`@__doc__` has no effect when a macro that uses it is not documented. | |
""" | |
:(Core.@__doc__) | |
function __doc__!(meta, def, define) | |
# Two cases must be handled here to avoid redefining all definitions contained in `def`: | |
if define | |
# `def` has not been defined yet (this is the common case, i.e. when not generating | |
# the Base image). We just need to convert each `@__doc__` marker to an `@doc`. | |
finddoc(def) do each | |
each.head = :macrocall | |
each.args = [Symbol("@doc"), meta, each.args[end], define] | |
end | |
else | |
# `def` has already been defined during Base image gen so we just need to find and | |
# document any subexpressions marked with `@__doc__`. | |
docs = [] | |
found = finddoc(def) do each | |
push!(docs, :(@doc($meta, $(each.args[end]), $define))) | |
end | |
# If any subexpressions have been documented then replace the entire expression with | |
# just those documented subexpressions to avoid redefining any definitions. | |
if found | |
def.head = :toplevel | |
def.args = docs | |
end | |
found | |
end | |
end | |
# Walk expression tree `def` and call `λ` when any `@__doc__` markers are found. Returns | |
# `true` to signify that at least one `@__doc__` has been found, and `false` otherwise. | |
function finddoc(λ, def::Expr) | |
if isexpr(def, :block, 2) && isexpr(def.args[1], :meta, 1) && def.args[1].args[1] === :doc | |
# Found the macroexpansion of an `@__doc__` expression. | |
λ(def) | |
true | |
else | |
found = false | |
for each in def.args | |
found |= finddoc(λ, each) | |
end | |
found | |
end | |
end | |
finddoc(λ, def) = false | |
# Predicates and helpers for `docm` expression selection: | |
const FUNC_HEADS = [:function, :stagedfunction, :macro, :(=)] | |
const BINDING_HEADS = [:typealias, :const, :global, :(=)] | |
# For the special `:@mac` / `:(Base.@mac)` syntax for documenting a macro after definition. | |
isquotedmacrocall(x) = | |
isexpr(x, :copyast, 1) && | |
isa(x.args[1], QuoteNode) && | |
isexpr(x.args[1].value, :macrocall, 1) | |
# Simple expressions / atoms the may be documented. | |
isbasicdoc(x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol}) | |
is_signature(x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) | |
function docm(meta, ex, define = true) | |
# Some documented expressions may be decorated with macro calls which obscure the actual | |
# expression. Expand the macro calls and remove extra blocks. | |
x = unblock(macroexpand(ex)) | |
# Don't try to redefine expressions. This is only needed for `Base` img gen since | |
# otherwise calling `loaddocs` would redefine all documented functions and types. | |
def = define ? x : nothing | |
# Keywords using the `@kw_str` macro in `base/docs/basedocs.jl`. | |
# | |
# "..." | |
# kw"if", kw"else" | |
# | |
isa(x, Base.BaseDocs.Keyword) ? keyworddoc(meta, x) : | |
# Method / macro definitions and "call" syntax. | |
# | |
# function f(...) ... end | |
# f(...) = ... | |
# macro m(...) end | |
# function f end | |
# f(...) | |
# | |
isexpr(x, FUNC_HEADS) && is_signature(x.args[1]) ? objectdoc(meta, def, x, signature(x)) : | |
isexpr(x, :function) && !isexpr(x.args[1], :call) ? objectdoc(meta, def, x) : | |
isexpr(x, :call) ? calldoc(meta, x) : | |
# Type definitions. | |
# | |
# type T ... end | |
# abstract T | |
# bitstype N T | |
# | |
isexpr(x, [:type, :abstract, :bitstype]) ? objectdoc(meta, def, x) : | |
# "Bindings". Names that resolve to objects with different names, ie. | |
# | |
# typealias T S | |
# const T = S | |
# T = S | |
# global T = S | |
# | |
isexpr(x, BINDING_HEADS) && !isexpr(x.args[1], :call) ? objectdoc(meta, def, x) : | |
# Quoted macrocall syntax. `:@time` / `:(Base.@time)`. | |
isquotedmacrocall(x) ? objectdoc(meta, def, x) : | |
# Modules and baremodules. | |
isexpr(x, :module) ? moduledoc(meta, def, x) : | |
# Document several expressions with the same docstring. `a, b, c`. | |
isexpr(x, :tuple) ? multidoc(meta, x, define) : | |
# Errors generated by calling `macroexpand` are passed back to the call site. | |
isexpr(x, :error) ? esc(x) : | |
# When documenting macro-generated code we look for embedded `@__doc__` calls. | |
__doc__!(meta, x, define) ? esc(x) : | |
# Any "basic" expression such as a bare function or module name or numeric literal. | |
isbasicdoc(x) ? objectdoc(meta, nothing, x) : | |
# All other expressions are undocumentable and should be handled on a case-by-case basis | |
# with `@__doc__`. Unbound string literals are also undocumentable since they cannot be | |
# retrieved from the module's metadata `ObjectIdDict` without a reference to the string. | |
docerror(ex) | |
end | |
function docerror(ex) | |
txt = """ | |
cannot document the following expression: | |
$(isa(ex, AbstractString) ? repr(ex) : ex)""" | |
if isexpr(ex, :macrocall) | |
txt *= "\n\n'$(ex.args[1])' not documentable. See 'Base.@__doc__' docs for details." | |
end | |
:($(error)($txt, "\n")) | |
end | |
function docm(ex) | |
if isexpr(ex, :->) | |
docm(ex.args...) | |
elseif haskey(keywords, ex) | |
parsedoc(keywords[ex]) | |
elseif isa(ex, Union{Expr, Symbol}) | |
binding = esc(bindingexpr(namify(ex))) | |
if isexpr(ex, [:call, :macrocall]) | |
sig = esc(signature(ex)) | |
:($(doc)($binding, $sig)) | |
else | |
:($(doc)($binding)) | |
end | |
else | |
:($(doc)($(typeof)($(esc(ex))))) | |
end | |
end | |
# MD support | |
catdoc(md::MD...) = MD(md...) | |
include("utils.jl") | |
# Swap out the bootstrap macro with the real one. | |
Core.atdoc!(docm) | |
function loaddocs(docs) | |
for (mod, ex, str, file, line) in docs | |
data = Dict(:path => string(file), :linenumber => line) | |
doc = docstr(str, data) | |
eval(mod, :(@doc($doc, $ex, false))) | |
end | |
empty!(docs) | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Base | |
""" | |
systemerror(sysfunc, iftrue) | |
Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true` | |
""" | |
systemerror | |
""" | |
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. | |
""" | |
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! | |
""" | |
empty!(collection) -> collection | |
Remove all elements from a `collection`. | |
```jldoctest | |
julia> A = Dict("a" => 1, "b" => 2) | |
Dict{String,Int64} with 2 entries: | |
"b" => 2 | |
"a" => 1 | |
julia> empty!(A); | |
julia> A | |
Dict{String,Int64} with 0 entries | |
``` | |
""" | |
empty! | |
""" | |
asin(x) | |
Compute the inverse sine of `x`, where the output is in radians. | |
""" | |
asin | |
""" | |
takebuf_array(b::IOBuffer) | |
Obtain the contents of an `IOBuffer` as an array, without copying. Afterwards, the | |
`IOBuffer` is reset to its initial state. | |
""" | |
takebuf_array | |
""" | |
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 | |
""" | |
//(num, den) | |
Divide two integers or rational numbers, giving a `Rational` result. | |
""" | |
Base.:(//) | |
""" | |
isinteger(x) -> Bool | |
Test whether `x` or all its elements are numerically equal to some integer. | |
```jldoctest | |
julia> isinteger(4.0) | |
true | |
julia> isinteger([1; 2; 5.5]) | |
false | |
``` | |
""" | |
isinteger | |
""" | |
./(x, y) | |
Element-wise right division operator. | |
```jldoctest | |
julia> [1 2 3] ./ [1 2 3] | |
1×3 Array{Float64,2}: | |
1.0 1.0 1.0 | |
``` | |
""" | |
Base.:(./) | |
""" | |
prod!(r, A) | |
Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. | |
""" | |
prod! | |
""" | |
cummin(A, [dim]) | |
Cumulative minimum along a dimension. The dimension defaults to 1. | |
""" | |
cummin | |
""" | |
minabs!(r, A) | |
Compute the minimum absolute values over the singleton dimensions of `r`, and write values to `r`. | |
""" | |
minabs! | |
""" | |
eigfact!(A, [B]) | |
Same as [`eigfact`](:func:`eigfact`), but saves space by overwriting the input `A` (and | |
`B`), instead of creating a copy. | |
""" | |
eigfact! | |
""" | |
cosh(x) | |
Compute hyperbolic cosine of `x`. | |
""" | |
cosh | |
""" | |
precision(num::AbstractFloat) | |
Get the precision of a floating point number, as defined by the effective number of bits in | |
the mantissa. | |
""" | |
precision | |
""" | |
promote_type(type1, type2) | |
Determine a type big enough to hold values of each argument type without loss, whenever | |
possible. In some cases, where no type exists to which both types can be promoted | |
losslessly, some loss is tolerated; for example, `promote_type(Int64,Float64)` returns | |
`Float64` even though strictly, not all `Int64` values can be represented exactly as | |
`Float64` values. | |
""" | |
promote_type | |
""" | |
``` | |
.*(x, y) | |
``` | |
Element-wise multiplication operator. | |
```jldoctest | |
julia> [1 2 3] .* [1 2 3] | |
1×3 Array{Int64,2}: | |
1 4 9 | |
``` | |
""" | |
Base.:(.*) | |
""" | |
backtrace() | |
Get a backtrace object for the current program point. | |
""" | |
backtrace | |
""" | |
-(x) | |
Unary minus operator. | |
""" | |
-(x) | |
""" | |
-(x, y) | |
Subtraction operator. | |
""" | |
-(x, y) | |
""" | |
bits(n) | |
A string giving the literal bit representation of a number. | |
```jldoctest | |
julia> bits(4) | |
"0000000000000000000000000000000000000000000000000000000000000100" | |
julia> bits(2.2) | |
"0100000000000001100110011001100110011001100110011001100110011010" | |
``` | |
""" | |
bits | |
""" | |
getindex(type[, elements...]) | |
Construct a 1-d array of the specified type. This is usually called with the syntax | |
`Type[]`. Element values can be specified using `Type[a,b,c,...]`. | |
""" | |
getindex(::Type, elements...) | |
""" | |
getindex(A, inds...) | |
Returns a subset of array `A` as specified by `inds`, where each `ind` may be an | |
`Int`, a `Range`, or a `Vector`. See the manual section on | |
[array indexing](:ref:`array indexing <man-array-indexing>`) for details. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> getindex(A, 1) | |
1 | |
julia> getindex(A, [2, 1]) | |
2-element Array{Int64,1}: | |
3 | |
1 | |
julia> getindex(A, 2:4) | |
3-element Array{Int64,1}: | |
3 | |
2 | |
4 | |
``` | |
""" | |
getindex(::AbstractArray, inds...) | |
""" | |
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, ...)`. | |
```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 of type `T`, typically by calling `convert(T,x)` | |
In cases where `x` cannot be safely converted to `T`, unlike [`convert`](:func:`convert`), `cconvert` may | |
return an object of a type different from `T`, which however is suitable for | |
[`unsafe_convert`](:func:`unsafe_convert`) to handle. | |
Neither `convert` nor `cconvert` should take a Julia object and turn it into a `Ptr`. | |
""" | |
cconvert | |
""" | |
assert(cond) | |
Throw an [`AssertionError`](:obj:`AssertionError`) if `cond` is `false`. | |
Also available as the macro `@assert expr`. | |
""" | |
assert | |
""" | |
sech(x) | |
Compute the hyperbolic secant of `x` | |
""" | |
sech | |
""" | |
acos(x) | |
Compute the inverse cosine of `x`, where the output is in radians | |
""" | |
acos | |
""" | |
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) | |
""" | |
.^(x, y) | |
Element-wise exponentiation operator. | |
```jldoctest | |
julia> [1 2 3] .^ [1 2 3] | |
1×3 Array{Int64,2}: | |
1 4 27 | |
``` | |
""" | |
Base.:(.^) | |
""" | |
Float32(x [, mode::RoundingMode]) | |
Create a Float32 from `x`. If `x` is not exactly representable then `mode` determines how | |
`x` is rounded. | |
```jldoctest | |
julia> Float32(1/3, RoundDown) | |
0.3333333f0 | |
julia> Float32(1/3, RoundUp) | |
0.33333334f0 | |
``` | |
See [`RoundingMode`](:obj:`RoundingMode`) for available rounding modes. | |
""" | |
Float32 | |
""" | |
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` 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`](:func:`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 = ?) | |
""" | |
bessely0(x) | |
Bessel function of the second kind of order 0, ``Y_0(x)``. | |
""" | |
bessely0 | |
""" | |
any!(r, A) | |
Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write | |
results to `r`. | |
""" | |
any! | |
""" | |
filter!(function, collection) | |
Update `collection`, removing elements for which `function` is `false`. | |
For associative collections, the function is passed two arguments (key and value). | |
```jldoctest | |
julia> filter!(isodd, collect(1:10)) | |
5-element Array{Int64,1}: | |
1 | |
3 | |
5 | |
7 | |
9 | |
``` | |
""" | |
filter! | |
""" | |
sizeof(T) | |
Size, in bytes, of the canonical binary representation of the given DataType `T`, if any. | |
```jldoctest | |
julia> sizeof(Float32) | |
4 | |
julia> sizeof(Complex128) | |
16 | |
``` | |
If `T` is not a bitstype, an error is thrown. | |
```jldoctest | |
julia> sizeof(Base.LinAlg.LU) | |
ERROR: argument is an abstract type; size is indeterminate | |
in sizeof(::Type{T}) at ./essentials.jl:89 | |
... | |
``` | |
""" | |
sizeof(::Type) | |
""" | |
ReadOnlyMemoryError() | |
An operation tried to write to memory that is read-only. | |
""" | |
ReadOnlyMemoryError | |
""" | |
last(coll) | |
Get the last element of an ordered collection, if it can be computed in O(1) time. This is | |
accomplished by calling [`endof`](:func:`endof`) to get the last index. Returns the end | |
point of a [`Range`](:obj:`Range`) even if it is empty. | |
```jldoctest | |
julia> last(1:2:10) | |
9 | |
julia> last([1; 2; 3; 4]) | |
4 | |
``` | |
""" | |
last | |
""" | |
sinh(x) | |
Compute hyperbolic sine of `x`. | |
""" | |
sinh | |
""" | |
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`](:func:`round`). | |
""" | |
ceil | |
""" | |
oftype(x, y) | |
Convert `y` to the type of `x` (`convert(typeof(x), y)`). | |
""" | |
oftype | |
""" | |
maxabs!(r, A) | |
Compute the maximum absolute values over the singleton dimensions of `r`, and write values to `r`. | |
""" | |
maxabs! | |
""" | |
isfinite(f) -> Bool | |
Test whether a number is finite. | |
```jldoctest | |
julia> isfinite(5) | |
true | |
julia> isfinite(NaN32) | |
false | |
``` | |
""" | |
isfinite | |
""" | |
push!(collection, items...) -> collection | |
Insert one or more `items` at the end of `collection`. | |
```jldoctest | |
julia> push!([1, 2, 3], 4, 5, 6) | |
6-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
``` | |
Use [`append!`](:func:`append!`) 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! | |
""" | |
prevpow(a, x) | |
The largest `a^n` not greater than `x`, where `n` is a non-negative integer. | |
`a` must be greater than 1, and `x` must not be less than 1. | |
""" | |
prevpow | |
""" | |
promote(xs...) | |
Convert all arguments to their common promotion type (if any), and return them all (as a tuple). | |
""" | |
promote | |
""" | |
tan(x) | |
Compute tangent of `x`, where `x` is in radians. | |
""" | |
tan | |
""" | |
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(type, dims) | |
Create an array of all ones of specified type. The type defaults to `Float64` if not specified. | |
```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 | |
``` | |
""" | |
ones(t,dims) | |
""" | |
ones(A) | |
Create an array of all ones with the same element type and shape as `A`. | |
```jldoctest | |
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 | |
``` | |
""" | |
ones(A) | |
""" | |
reshape(A, dims) | |
Create an array with the same data as the given array, but with different dimensions. | |
```jldoctest | |
julia> A = collect(1:16) | |
16-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
julia> reshape(A, (2, 8)) | |
2×8 Array{Int64,2}: | |
1 3 5 7 9 11 13 15 | |
2 4 6 8 10 12 14 16 | |
``` | |
""" | |
reshape | |
""" | |
randsubseq!(S, A, p) | |
Like [`randsubseq`](:func:`randsubseq`), but the results are stored in `S` | |
(which is resized as needed). | |
""" | |
randsubseq! | |
""" | |
maximum(A, dims) | |
Compute the maximum value of an array over the given dimensions. See also the | |
[`max(a,b)`](:func:`max`) function to take the maximum of two or more arguments, | |
which can be applied elementwise to arrays via `max.(a,b)`. | |
""" | |
maximum(A,dims) | |
""" | |
redisplay(x) | |
redisplay(d::Display, x) | |
redisplay(mime, x) | |
redisplay(d::Display, mime, x) | |
By default, the `redisplay` functions simply call [`display`](:func:`display`). | |
However, some display backends may override `redisplay` to modify an existing | |
display of `x` (if any). | |
Using `redisplay` is also a hint to the backend that `x` may be redisplayed | |
several times, and the backend may choose to defer the display until | |
(for example) the next interactive prompt. | |
""" | |
redisplay | |
""" | |
searchsorted(a, x, [by=<transform>,] [lt=<comparison>,] [rev=false]) | |
Returns the range of indices of `a` which compare as equal to `x` according to the order | |
specified by the `by`, `lt` and `rev` keywords, assuming that `a` is already sorted in that | |
order. Returns an empty range located at the insertion point if `a` does not contain values | |
equal to `x`. | |
""" | |
searchsorted | |
""" | |
/(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 | |
""" | |
sumabs(A, dims) | |
Sum absolute values of elements of an array over the given dimensions. | |
""" | |
sumabs(A, dims) | |
""" | |
consume(task, values...) | |
Receive the next value passed to `produce` by the specified task. Additional arguments may | |
be passed, to be returned from the last `produce` call in the producer. | |
""" | |
consume | |
""" | |
cummax(A, [dim]) | |
Cumulative maximum along a dimension. The dimension defaults to 1. | |
""" | |
cummax | |
""" | |
isinteractive() -> Bool | |
Determine whether Julia is running an interactive session. | |
""" | |
isinteractive | |
""" | |
sum!(r, A) | |
Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. | |
""" | |
sum! | |
""" | |
parentindexes(A) | |
From an array view `A`, returns the corresponding indexes in the parent. | |
""" | |
parentindexes | |
""" | |
display(x) | |
display(d::Display, x) | |
display(mime, x) | |
display(d::Display, mime, x) | |
Display `x` using the topmost applicable display in the display stack, typically using the | |
richest supported multimedia output for `x`, with plain-text [`STDOUT`](:obj:`STDOUT`) output as a fallback. | |
The `display(d, x)` variant attempts to display `x` on the given display `d` only, throwing | |
a `MethodError` if `d` cannot display objects of this type. | |
There are also two variants with a `mime` argument (a MIME type string, such as | |
`"image/png"`), which attempt to display `x` using the requested MIME type *only*, throwing | |
a `MethodError` if this type is not supported by either the display(s) or by `x`. With these | |
variants, one can also supply the "raw" data in the requested MIME type by passing | |
`x::AbstractString` (for MIME types with text-based storage, such as text/html or | |
application/postscript) or `x::Vector{UInt8}` (for binary MIME types). | |
""" | |
display | |
""" | |
@spawnat | |
Accepts two arguments, `p` and an expression. A closure is created around the expression and | |
run asynchronously on process `p`. Returns a [`Future`](:obj:`Future`) to the result. | |
""" | |
:@spawnat | |
""" | |
print_shortest(io, x) | |
Print the shortest possible representation, with the minimum number of consecutive non-zero | |
digits, of number `x`, ensuring that it would parse to the exact same number. | |
""" | |
print_shortest | |
""" | |
tuple(xs...) | |
Construct a tuple of the given objects. | |
""" | |
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 | |
""" | |
log10(x) | |
Compute the logarithm of `x` to base 10. | |
Throws [`DomainError`](:obj:`DomainError`) for negative `Real` arguments. | |
```jldoctest | |
julia> log10(100) | |
2.0 | |
julia> log10(2) | |
0.3010299956639812 | |
``` | |
""" | |
log10 | |
""" | |
num2hex(f) | |
Get a hexadecimal string of the binary representation of a floating point number. | |
```jldoctest | |
julia> num2hex(2.2) | |
"400199999999999a" | |
``` | |
""" | |
num2hex | |
""" | |
displayable(mime) -> Bool | |
displayable(d::Display, mime) -> Bool | |
Returns a boolean value indicating whether the given `mime` type (string) is displayable by | |
any of the displays in the current display stack, or specifically by the display `d` in the | |
second variant. | |
""" | |
displayable | |
""" | |
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``. | |
```jldoctest | |
julia> exp10(2) | |
100.0 | |
julia> exp10(0.2) | |
1.5848931924611136 | |
``` | |
""" | |
exp10 | |
""" | |
&(x, y) | |
Bitwise and. | |
```jldoctest | |
julia> 4 & 10 | |
0 | |
julia> 4 & 12 | |
4 | |
``` | |
""" | |
& | |
""" | |
cumsum!(B, A, [dim]) | |
Cumulative sum of `A` along a dimension, storing the result in `B`. The dimension defaults | |
to 1. | |
""" | |
cumsum! | |
""" | |
select(v, k, [by=<transform>,] [lt=<comparison>,] [rev=false]) | |
Variant of `select!` 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 | |
""" | |
readstring(stream::IO) | |
readstring(filename::AbstractString) | |
Read the entire contents of an I/O stream or a file as a string. | |
The text is assumed to be encoded in UTF-8. | |
""" | |
readstring | |
""" | |
eachline(stream::IO) | |
eachline(filename::AbstractString) | |
Create an iterable object that will yield each line from an I/O stream or a file. | |
The text is assumed to be encoded in UTF-8. | |
""" | |
eachline | |
""" | |
complex(r, [i]) | |
Convert real numbers or arrays to complex. `i` defaults to zero. | |
""" | |
complex | |
""" | |
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 | |
""" | |
erfi(x) | |
Compute the imaginary error function of `x`, defined by ``-i \\operatorname{erf}(ix)``. | |
""" | |
erfi | |
""" | |
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`](:func:`round`). | |
""" | |
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. | |
""" | |
reverse | |
""" | |
reverse!(v [, start=1 [, stop=length(v) ]]) -> v | |
In-place version of [`reverse`](:func:`reverse`). | |
""" | |
reverse! | |
""" | |
num(x) | |
Numerator of the rational representation of `x`. | |
""" | |
num | |
""" | |
.<(x, y) | |
Element-wise less-than comparison operator. | |
```jldoctest | |
julia> [1; 2; 3] .< [2; 1; 4] | |
3-element BitArray{1}: | |
true | |
false | |
true | |
``` | |
""" | |
Base.:(.<) | |
""" | |
UndefRefError() | |
The item or field is not defined for the given object. | |
""" | |
UndefRefError | |
""" | |
bessely1(x) | |
Bessel function of the second kind of order 1, ``Y_1(x)``. | |
""" | |
bessely1 | |
""" | |
append!(collection, collection2) -> collection. | |
Add the elements of `collection2` to the end of `collection`. | |
```jldoctest | |
julia> append!([1],[2,3]) | |
3-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
``` | |
```jldoctest | |
julia> append!([1, 2, 3], [4, 5, 6]) | |
6-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
``` | |
Use [`push!`](:func:`push!`) 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 | |
""" | |
setdiff!(s, iterable) | |
Remove each element of `iterable` from set `s` in-place. | |
""" | |
setdiff! | |
""" | |
copysign(x, y) -> z | |
Return `z` which has the magnitude of `x` and the same sign as `y`. | |
```jldoctest | |
julia> copysign(1, -2) | |
-1 | |
julia> copysign(-1, 2) | |
1 | |
``` | |
""" | |
copysign | |
""" | |
@show | |
Show an expression and result, returning the result. | |
""" | |
:@show | |
""" | |
showcompact(x) | |
Show a compact representation of a value. | |
This is used for printing array elements without repeating type information (which would | |
be redundant with that printed once for the whole array), and without line breaks inside | |
the representation of an element. | |
To offer a compact representation different from its standard one, a custom type should | |
test `get(io, :compact, false)` in its normal `show` method. | |
""" | |
showcompact | |
""" | |
erfc(x) | |
Compute the complementary error function of `x`, defined by ``1 - \\operatorname{erf}(x)``. | |
""" | |
erfc | |
""" | |
getfield(value, name::Symbol) | |
Extract a named field from a `value` of composite type. The syntax `a.b` calls | |
`getfield(a, :b)`. | |
""" | |
getfield | |
""" | |
besselj1(x) | |
Bessel function of the first kind of order 1, ``J_1(x)``. | |
""" | |
besselj1 | |
""" | |
select!(v, k, [by=<transform>,] [lt=<comparison>,] [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. | |
""" | |
select! | |
""" | |
maximum!(r, A) | |
Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. | |
""" | |
maximum! | |
""" | |
prod(A, dims) | |
Multiply elements of an array over the given dimensions. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> prod(A, 1) | |
1×2 Array{Int64,2}: | |
3 8 | |
julia> prod(A, 2) | |
2×1 Array{Int64,2}: | |
2 | |
12 | |
``` | |
""" | |
prod(A, dims) | |
""" | |
log1p(x) | |
Accurate natural logarithm of `1+x`. Throws `DomainError` for `Real` arguments less than -1. | |
There is an experimental variant in the `Base.Math.JuliaLibm` module, which is typically | |
faster and more accurate. | |
""" | |
log1p | |
""" | |
flipsign(x, y) | |
Return `x` with its sign flipped if `y` is negative. For example `abs(x) = flipsign(x,x)`. | |
""" | |
flipsign | |
""" | |
randstring([rng,] len=8) | |
Create a random ASCII string of length `len`, consisting of upper- and | |
lower-case letters and the digits 0-9. The optional `rng` argument | |
specifies a random number generator, see [Random Numbers](:ref:`Random Numbers <random-numbers>`). | |
""" | |
randstring | |
""" | |
Float64(x [, mode::RoundingMode]) | |
Create a Float64 from `x`. If `x` is not exactly representable then `mode` determines how | |
`x` is rounded. | |
```jldoctest | |
julia> Float64(pi, RoundDown) | |
3.141592653589793 | |
julia> Float64(pi, RoundUp) | |
3.1415926535897936 | |
``` | |
See [`RoundingMode`](:obj:`RoundingMode`) for available rounding modes. | |
""" | |
Float64 | |
""" | |
union(s1,s2...) | |
∪(s1,s2...) | |
Construct the union of two or more sets. Maintains order with arrays. | |
""" | |
union | |
""" | |
realmax(T) | |
The highest finite value representable by the given floating-point DataType `T`. | |
""" | |
realmax | |
""" | |
serialize(stream, value) | |
Write an arbitrary value to a stream in an opaque format, such that it can be read back by | |
[`deserialize`](:func:`deserialize`). 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 | |
""" | |
sum(A, dims) | |
Sum elements of an array over the given dimensions. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> sum(A, 1) | |
1×2 Array{Int64,2}: | |
4 6 | |
julia> sum(A, 2) | |
2×1 Array{Int64,2}: | |
3 | |
7 | |
``` | |
""" | |
sum(A, dims) | |
""" | |
typemin(T) | |
The lowest value representable by the given (real) numeric DataType `T`. | |
""" | |
typemin | |
""" | |
typeof(x) | |
Get the concrete type of `x`. | |
""" | |
typeof | |
""" | |
log(x) | |
Compute the natural logarithm of `x`. Throws `DomainError` for negative `Real` arguments. | |
Use complex negative arguments to obtain complex results. | |
There is an experimental variant in the `Base.Math.JuliaLibm` module, which is typically | |
faster and more accurate. | |
""" | |
log(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`](:func:`round`). | |
""" | |
trunc | |
""" | |
unsafe_convert(T,x) | |
Convert `x` to a value of type `T` | |
In cases where [`convert`](:func:`convert`) 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. | |
""" | |
unsafe_convert | |
""" | |
erfinv(x) | |
Compute the inverse error function of a real `x`, defined by ``\\operatorname{erf}(\\operatorname{erfinv}(x)) = x``. | |
""" | |
erfinv | |
""" | |
seek(s, pos) | |
Seek a stream to the given position. | |
""" | |
seek | |
""" | |
besselj0(x) | |
Bessel function of the first kind of order 0, ``J_0(x)``. | |
""" | |
besselj0 | |
""" | |
erfcinv(x) | |
Compute the inverse error complementary function of a real `x`, defined by | |
``\\operatorname{erfc}(\\operatorname{erfcinv}(x)) = x``. | |
""" | |
erfcinv | |
""" | |
minabs(A, dims) | |
Compute the minimum absolute values over given dimensions. | |
""" | |
minabs(A, dims) | |
""" | |
popdisplay() | |
popdisplay(d::Display) | |
Pop the topmost backend off of the display-backend stack, or the topmost copy of `d` in the | |
second variant. | |
""" | |
popdisplay | |
""" | |
cglobal((symbol, library) [, type=Void]) | |
Obtain a pointer to a global variable in a C-exported shared library, specified exactly as | |
in [`ccall`](:func:`ccall`). | |
Returns a `Ptr{Type}`, defaulting to `Ptr{Void}` if no `Type` argument is | |
supplied. | |
The values can be read or written by [`unsafe_load`](:func:`unsafe_load`) or [`unsafe_store!`](:func:`unsafe_store!`), | |
respectively. | |
""" | |
cglobal | |
""" | |
one(x) | |
Get the multiplicative identity element for the type of `x` (`x` can also specify the type | |
itself). For matrices, returns an identity matrix of the appropriate size and type. | |
""" | |
one | |
""" | |
endof(collection) -> Integer | |
Returns the last index of the collection. | |
```jldoctest | |
julia> endof([1,2,4]) | |
3 | |
``` | |
""" | |
endof | |
""" | |
Channel{T}(sz::Int) | |
Constructs a `Channel` with an internal buffer that can hold a maximum of `sz` objects | |
of type `T`. `put!` calls on a full channel block until an object is removed with `take!`. | |
`Channel(0)` constructs an unbuffered channel. `put!` blocks until a matching `take!` is called. | |
And vice-versa. | |
Other constructors: | |
- `Channel(Inf)` - equivalent to `Channel{Any}(typemax(Int))` | |
- `Channel(sz)` equivalent to `Channel{Any}(sz)` | |
""" | |
Channel | |
""" | |
next(iter, state) -> item, state | |
For a given iterable object and iteration state, return the current item and the next iteration state. | |
""" | |
next | |
""" | |
log2(x) | |
Compute the logarithm of `x` to base 2. Throws `DomainError` for negative `Real` arguments. | |
```jldoctest | |
julia> log2(4) | |
2.0 | |
julia> log2(10) | |
3.321928094887362 | |
``` | |
""" | |
log2 | |
""" | |
abs2(x) | |
Squared absolute value of `x`. | |
""" | |
abs2 | |
""" | |
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, `i`. | |
""" | |
BoundsError | |
""" | |
invoke(f, (types...), args...) | |
Invoke a method for the given generic function matching the specified types (as a tuple), on | |
the specified arguments. The arguments must be compatible with the specified types. This | |
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). | |
""" | |
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. | |
""" | |
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. | |
""" | |
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. | |
""" | |
parse(T::Type, str, base=Int) | |
""" | |
^(x, y) | |
Exponentiation operator. | |
""" | |
Base.:(^)(x, y) | |
""" | |
position(s) | |
Get the current position of a stream. | |
""" | |
position | |
""" | |
selectperm(v, k, [alg=<algorithm>,] [by=<transform>,] [lt=<comparison>,] [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` 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`. | |
```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 | |
""" | |
~(x) | |
Bitwise not. | |
```jldoctest | |
julia> ~4 | |
-5 | |
julia> ~10 | |
-11 | |
julia> ~true | |
false | |
``` | |
""" | |
~ | |
""" | |
bswap(n) | |
Byte-swap an integer. | |
""" | |
bswap | |
""" | |
sumabs2!(r, A) | |
Sum squared absolute values of elements of `A` over the singleton dimensions of `r`, and | |
write results to `r`. | |
""" | |
sumabs2! | |
""" | |
tanh(x) | |
Compute hyperbolic tangent of `x`. | |
""" | |
tanh | |
""" | |
maxintfloat(T) | |
The largest integer losslessly representable by the given floating-point DataType `T`. | |
""" | |
maxintfloat | |
""" | |
delete!(collection, key) | |
Delete the mapping for the given key in a collection, and return the collection. | |
""" | |
delete! | |
""" | |
eps(T) | |
The distance between 1.0 and the next larger representable floating-point value of | |
`DataType` `T`. Only floating-point types are sensible arguments. | |
""" | |
eps(::Union{Type{BigFloat},Type{Float64},Type{Float32},Type{Float16}}) | |
""" | |
eps() | |
The distance between 1.0 and the next larger representable floating-point value of `Float64`. | |
""" | |
eps() | |
""" | |
eps(x) | |
The distance between `x` and the next larger representable floating-point value of the same | |
`DataType` as `x`. | |
""" | |
eps(::AbstractFloat) | |
""" | |
searchsortedfirst(a, x, [by=<transform>,] [lt=<comparison>,] [rev=false]) | |
Returns the index of the first value in `a` greater than or equal to `x`, according to the | |
specified order. Returns `length(a)+1` if `x` is greater than all values in `a`. | |
""" | |
searchsortedfirst | |
""" | |
big(x) | |
Convert a number to a maximum precision representation (typically `BigInt` or `BigFloat`). | |
See [`BigFloat`](:obj:`BigFloat`) for information about some pitfalls with floating-point numbers. | |
""" | |
big | |
""" | |
quit() | |
Quit the program indicating that the processes completed successfully. This function calls | |
`exit(0)` (see [`exit`](:func:`exit`)). | |
""" | |
quit | |
""" | |
typejoin(T, S) | |
Compute a type that contains both `T` and `S`. | |
""" | |
typejoin | |
""" | |
sin(x) | |
Compute sine of `x`, where `x` is in radians. | |
""" | |
sin | |
""" | |
selectperm!(ix, v, k, [alg=<algorithm>,] [by=<transform>,] [lt=<comparison>,] [rev=false,] [initialized=false]) | |
Like `selectperm`, 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 | |
""" | |
asinh(x) | |
Compute the inverse hyperbolic sine of `x`. | |
""" | |
asinh | |
""" | |
minimum(A, dims) | |
Compute the minimum value of an array over the given dimensions. See also the | |
[`min(a,b)`](:func:`min`) function to take the minimum of two or more arguments, | |
which can be applied elementwise to arrays via `min.(a,b)`. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> minimum(A, 1) | |
1×2 Array{Int64,2}: | |
1 2 | |
julia> minimum(A, 2) | |
2×1 Array{Int64,2}: | |
1 | |
3 | |
``` | |
""" | |
minimum(A,dims) | |
""" | |
view(A, inds...) | |
Like [`getindex`](:func:`getindex`), but returns a view into the parent array `A` with the | |
given indices instead of making a copy. Calling [`getindex`](:func:`getindex`) or | |
[`setindex!`](:func:`setindex!`) on the returned [`SubArray`](:obj:`SubArray`) computes the | |
indices to the parent array on the fly without checking bounds. | |
""" | |
view | |
""" | |
cot(x) | |
Compute the cotangent of `x`, where `x` is in radians. | |
""" | |
cot | |
""" | |
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. | |
""" | |
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!`](:func:`get!`) 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! | |
""" | |
csc(x) | |
Compute the cosecant of `x`, where `x` is in radians. | |
""" | |
csc | |
""" | |
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 | |
""" | |
atanh(x) | |
Compute the inverse hyperbolic tangent of `x`. | |
""" | |
atanh | |
""" | |
read(stream::IO, T) | |
Read a single value of type `T` from `stream`, in canonical binary representation. | |
""" | |
read(stream, t) | |
""" | |
shift!(collection) -> item | |
Remove the first `item` from `collection`. | |
```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`](:obj:`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. With a single symbol argument, | |
tests whether a global variable with that name is defined in `current_module()`. | |
""" | |
isdefined | |
""" | |
cotd(x) | |
Compute the cotangent of `x`, where `x` is in degrees. | |
""" | |
cotd | |
""" | |
wait([x]) | |
Block the current task until some event occurs, depending on the type of the argument: | |
* [`RemoteChannel`](:obj:`RemoteChannel`) : Wait for a value to become available on the specified remote channel. | |
* [`Future`](:obj:`Future`) : Wait for a value to become available for the specified future. | |
* [`Channel`](:obj:`Channel`): Wait for a value to be appended to the channel. | |
* [`Condition`](:obj:`Condition`): Wait for [`notify`](:func:`notify`) on a condition. | |
* [`Process`](:obj:`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`](:obj:`Task`): 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`](:obj:`RawFD`): Wait for changes on a file descriptor (see [`poll_fd`](:func:`poll_fd`) 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` or `yieldto`. | |
Often `wait` is called within a `while` loop to ensure a waited-for condition is met before proceeding. | |
""" | |
wait | |
""" | |
atexit(f) | |
Register a zero-argument function `f()` to be called at process exit. `atexit()` hooks are | |
called in last in first out (LIFO) order and run before object finalizers. | |
""" | |
atexit | |
""" | |
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). | |
```jldoctest | |
julia> isempty([]) | |
true | |
julia> isempty([1 2 3]) | |
false | |
``` | |
""" | |
isempty | |
""" | |
sumabs!(r, A) | |
Sum absolute values of elements of `A` over the singleton dimensions of `r`, and write | |
results to `r`. | |
""" | |
sumabs! | |
""" | |
hex2num(str) | |
Convert a hexadecimal string to the floating point number it represents. | |
""" | |
hex2num | |
""" | |
InexactError() | |
Type conversion cannot be done exactly. | |
""" | |
InexactError | |
""" | |
typemax(T) | |
The highest value representable by the given (real) numeric `DataType`. | |
""" | |
typemax | |
""" | |
all(A, dims) | |
Test whether all values along the given dimensions of an array are `true`. | |
```jldoctest | |
julia> A = [true false; true true] | |
2×2 Array{Bool,2}: | |
true false | |
true true | |
julia> all(A, 1) | |
1×2 Array{Bool,2}: | |
true false | |
julia> all(A, 2) | |
2×1 Array{Bool,2}: | |
false | |
true | |
``` | |
""" | |
all(A::AbstractArray, dims) | |
""" | |
DomainError() | |
The arguments to a function or constructor are outside the valid domain. | |
""" | |
DomainError | |
""" | |
acosh(x) | |
Compute the inverse hyperbolic cosine of `x`. | |
""" | |
acosh | |
""" | |
IntSet([itr]) | |
Construct a sorted set of positive `Int`s generated by the given iterable object, or an | |
empty set. Implemented as a bit string, and therefore designed for dense integer sets. Only | |
`Int`s greater than 0 can be stored. If the set will be sparse (for example holding a few | |
very large integers), use [`Set`](:obj:`Set`) instead. | |
""" | |
IntSet | |
""" | |
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. | |
""" | |
Task | |
""" | |
pushdisplay(d::Display) | |
Pushes a new display `d` on top of the global display-backend stack. Calling `display(x)` or | |
`display(mime, x)` will display `x` on the topmost compatible backend in the stack (i.e., | |
the topmost backend that does not throw a `MethodError`). | |
""" | |
pushdisplay | |
""" | |
produce(value) | |
Send the given value to the last `consume` call, switching to the consumer task. If the next | |
`consume` call passes any values, they are returned by `produce`. | |
""" | |
produce | |
""" | |
StackOverflowError() | |
The function call grew beyond the size of the call stack. This usually happens when a call | |
recurses infinitely. | |
""" | |
StackOverflowError | |
""" | |
BigInt(x) | |
Create an arbitrary precision integer. `x` may be an `Int` (or anything that can be | |
converted to an `Int`). The usual mathematical operators are defined for this type, and | |
results are promoted to a `BigInt`. | |
Instances can be constructed from strings via [`parse`](:func:`parse`), or using the `big` | |
string literal. | |
""" | |
BigInt | |
""" | |
==(x, y) | |
Generic equality operator, giving a single `Bool` 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::DataType) -> Int | |
Get the number of fields of a `DataType`. | |
""" | |
nfields | |
""" | |
show(stream, mime, x) | |
The `display` 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`](:obj:`IOContext`) specifying output format properties. | |
See [`IOContext`](:obj:`IOContext`) for details. | |
""" | |
show(stream, mime, x) | |
""" | |
mean!(r, v) | |
Compute the mean of `v` over the singleton dimensions of `r`, and write results to `r`. | |
""" | |
mean! | |
""" | |
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`. 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 | |
""" | |
expm1(x) | |
Accurately compute ``e^x-1``. | |
""" | |
expm1 | |
""" | |
showerror(io, e) | |
Show a descriptive representation of an exception object. | |
""" | |
showerror | |
""" | |
error(message::AbstractString) | |
Raise an `ErrorException` with the given message. | |
""" | |
error | |
""" | |
sqrtm(A) | |
If `A` has no negative real eigenvalues, compute the principal matrix square root of `A`, | |
that is the unique matrix ``X`` with eigenvalues having positive real part such that | |
``X^2 = A``. Otherwise, a nonprincipal square root is returned. | |
If `A` is symmetric or Hermitian, its eigendecomposition ([`eigfact`](:func:`eigfact`)) is | |
used to compute the square root. Otherwise, the square root is determined by means of the | |
Björck-Hammarling method, which computes the complex Schur form ([`schur`](:func:`schur`)) | |
and then the complex square root of the triangular factor. | |
[^BH83]: Åke Björck and Sven Hammarling, "A Schur method for the square root of a matrix", Linear Algebra and its Applications, 52-53, 1983, 127-140. [doi:10.1016/0024-3795(83)80010-X](http://dx.doi.org/10.1016/0024-3795(83)80010-X) | |
""" | |
sqrtm | |
""" | |
unsafe_store!(p::Ptr{T}, x, [i::Integer=1]) | |
Store a value of type `T` to the address of the ith element (1-indexed) starting at `p`. | |
This is equivalent to the C expression `p[i-1] = x`. | |
The `unsafe` prefix on this function indicates that no validation is performed on the | |
pointer `p` to ensure that it is valid. Incorrect usage may corrupt or segfault your | |
program, in the same manner as C. | |
""" | |
unsafe_store! | |
""" | |
readcsv(source, [T::Type]; options...) | |
Equivalent to `readdlm` with `delim` set to comma, and type optionally defined by `T`. | |
""" | |
readcsv | |
""" | |
erfcx(x) | |
Compute the scaled complementary error function of `x`, defined by ``e^{x^2} \\operatorname{erfc}(x)``. | |
Note also that ``\\operatorname{erfcx}(-ix)`` computes the Faddeeva function ``w(x)``. | |
""" | |
erfcx | |
""" | |
UndefVarError(var::Symbol) | |
A symbol in the current scope is not defined. | |
""" | |
UndefVarError | |
""" | |
gc() | |
Perform garbage collection. This should not generally be used. | |
""" | |
gc | |
""" | |
minimum!(r, A) | |
Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. | |
""" | |
minimum! | |
""" | |
.-(x, y) | |
Element-wise subtraction operator. | |
```jldoctest | |
julia> [4; 5; 6] .- [1; 2; 4] | |
3-element Array{Int64,1}: | |
3 | |
3 | |
2 | |
``` | |
""" | |
Base.:(.-) | |
""" | |
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. | |
""" | |
parent | |
""" | |
nextpow(a, x) | |
The smallest `a^n` not less than `x`, where `n` is a non-negative integer. `a` must be | |
greater than 1, and `x` must be greater than 0. | |
""" | |
nextpow | |
""" | |
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 | |
""" | |
atan(x) | |
Compute the inverse tangent of `x`, where the output is in radians. | |
""" | |
atan | |
""" | |
isinf(f) -> Bool | |
Test whether a number is infinite. | |
""" | |
isinf | |
""" | |
secd(x) | |
Compute the secant of `x`, where `x` is in degrees. | |
""" | |
secd | |
""" | |
OverflowError() | |
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) | |
""" | |
Array(dims) | |
`Array{T}(dims)` constructs an uninitialized dense array with element type `T`. `dims` may | |
be a tuple or a series of integer arguments. The syntax `Array(T, dims)` is also available, | |
but deprecated. | |
""" | |
Array | |
""" | |
isreal(x) -> Bool | |
Test whether `x` or all its elements are numerically equal to some real number. | |
```jldoctest | |
julia> isreal(5.) | |
true | |
julia> isreal([4.; complex(0,1)]) | |
false | |
``` | |
""" | |
isreal | |
""" | |
issubtype(type1, type2) | |
Return `true` if and only if all values of `type1` are also of `type2`. Can also be written | |
using the `<:` infix operator as `type1 <: type2`. | |
```jldoctest | |
julia> issubtype(Int8, Int32) | |
false | |
julia> Int8 <: Integer | |
true | |
``` | |
""" | |
issubtype(type1, type2) | |
""" | |
finalizer(x, function) | |
Register a function `f(x)` to be called when there are no program-accessible references to | |
`x`. The behavior of this function is unpredictable if `x` is of a bits type. | |
""" | |
finalizer | |
""" | |
csch(x) | |
Compute the hyperbolic cosecant of `x`. | |
""" | |
csch | |
""" | |
sec(x) | |
Compute the secant of `x`, where `x` is in radians. | |
""" | |
sec | |
""" | |
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! | |
""" | |
countlines(io,[eol::Char]) | |
Read `io` until the end of the stream/file and count the number of lines. To specify a file | |
pass the filename as the first argument. EOL markers other than '\\n' are supported by | |
passing them as the second argument. | |
""" | |
countlines | |
""" | |
.\\(x, y) | |
Element-wise left division operator. | |
```jldoctest | |
julia> A = [1 2; 3 4] | |
2×2 Array{Int64,2}: | |
1 2 | |
3 4 | |
julia> A .\\ [1 2] | |
2×2 Array{Float64,2}: | |
1.0 1.0 | |
0.333333 0.5 | |
``` | |
```jldoctest | |
julia> A = [1 0; 0 -1]; | |
julia> B = [0 1; 1 0]; | |
julia> C = [A, B] | |
2-element Array{Array{Int64,2},1}: | |
[1 0; 0 -1] | |
[0 1; 1 0] | |
julia> x = [1; 0]; | |
julia> y = [0; 1]; | |
julia> D = [x, y] | |
2-element Array{Array{Int64,1},1}: | |
[1,0] | |
[0,1] | |
julia> C .\\ D | |
2-element Array{Array{Float64,1},1}: | |
[1.0,-0.0] | |
[1.0,0.0] | |
``` | |
See also [`broadcast`](:func:`broadcast`). | |
""" | |
Base.:(.\)(x,y) | |
""" | |
``` | |
*(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() | |
""" | |
TextDisplay(stream) | |
Returns a `TextDisplay <: Display`, which can display any object as the text/plain MIME type | |
(only), writing the text representation to the given I/O stream. (The text representation is | |
the same as the way an object is printed in the Julia REPL.) | |
""" | |
TextDisplay | |
""" | |
ismatch(r::Regex, s::AbstractString) -> Bool | |
Test whether a string contains a match of the given regular expression. | |
""" | |
ismatch | |
""" | |
exp(x) | |
Compute ``e^x``. | |
""" | |
exp | |
""" | |
matchall(r::Regex, s::AbstractString[, overlap::Bool=false]) -> Vector{AbstractString} | |
Return a vector of the matching substrings from [`eachmatch`](:func:`eachmatch`). | |
""" | |
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`. | |
""" | |
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: | |
get!(dict, key) do | |
# default value calculated here | |
time() | |
end | |
""" | |
get!(f::Function,collection,key) | |
""" | |
@assert cond [text] | |
Throw an `AssertionError` if `cond` is `false`. Preferred syntax for writing assertions. | |
Message `text` is optionally displayed upon assertion failure. | |
""" | |
:@assert | |
""" | |
deserialize(stream) | |
Read a value written by [`serialize`](:func:`serialize`). `deserialize` assumes the binary data read from | |
`stream` is correct and has been serialized by a compatible implementation of [`serialize`](:func:`serialize`). | |
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 | |
""" | |
first(coll) | |
Get the first element of an iterable collection. Returns the start point of a | |
[`Range`](:obj:`Range`) even if it is empty. | |
```jldoctest | |
julia> first(2:2:10) | |
2 | |
julia> first([1; 2; 3; 4]) | |
1 | |
``` | |
""" | |
first | |
""" | |
median!(v) | |
Like [`median`](:func:`median`), but may overwrite the input vector. | |
""" | |
median! | |
""" | |
cumprod!(B, A, [dim]) | |
Cumulative product of `A` along a dimension, storing the result in `B`. The dimension defaults to 1. | |
""" | |
cumprod! | |
""" | |
rethrow([e]) | |
Throw an object without changing the current exception backtrace. The default argument is | |
the current exception (if called within a `catch` block). | |
""" | |
rethrow | |
""" | |
!(x) | |
Boolean not. | |
```jldoctest | |
julia> !true | |
false | |
julia> !false | |
true | |
julia> ![true false true] | |
1×3 Array{Bool,2}: | |
false true false | |
``` | |
""" | |
Base.:(!) | |
""" | |
length(collection) -> Integer | |
For ordered, indexable collections, returns the maximum index `i` for which `getindex(collection, i)` | |
is valid. | |
For unordered collections, returns the number of elements. | |
```jldoctest | |
julia> length(1:5) | |
5 | |
julia> length([1; 2; 3; 4]) | |
4 | |
``` | |
""" | |
length(collection) | |
""" | |
searchsortedlast(a, x, [by=<transform>,] [lt=<comparison>,] [rev=false]) | |
Returns the index of the last value in `a` less than or equal to `x`, according to the | |
specified order. Returns `0` if `x` is less than all values in `a`. | |
""" | |
searchsortedlast | |
""" | |
InterruptException() | |
The process was stopped by a terminal interrupt (CTRL+C). | |
""" | |
InterruptException | |
""" | |
den(x) | |
Denominator of the rational representation of `x`. | |
""" | |
den | |
""" | |
issubnormal(f) -> Bool | |
Test whether a floating point number is subnormal. | |
""" | |
issubnormal | |
""" | |
NullException() | |
An attempted access to a [`Nullable`](:obj:`Nullable`) with no defined value. | |
""" | |
NullException | |
""" | |
.==(x, y) | |
Element-wise equality comparison operator. | |
```jldoctest | |
julia> [1 2 3] .== [1 2 4] | |
1×3 BitArray{2}: | |
true true false | |
``` | |
""" | |
Base.:(.==) | |
""" | |
cfunction(function::Function, ReturnType::Type, (ArgumentTypes...)) | |
Generate C-callable function pointer from Julia function. Type annotation of the return | |
value in the callback function is a must for situations where Julia cannot infer the return | |
type automatically. | |
For example: | |
function foo() | |
# body | |
retval::Float64 | |
end | |
bar = cfunction(foo, Float64, ()) | |
""" | |
cfunction | |
""" | |
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. | |
""" | |
intersect | |
""" | |
@spawn | |
Creates a closure around an expression and runs it on an automatically-chosen process, | |
returning a [`Future`](:obj:`Future`) to the result. | |
""" | |
:@spawn | |
""" | |
promote_rule(type1, type2) | |
Specifies what type should be used by [`promote`](:func:`promote`) 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 | |
""" | |
sumabs2(A, dims) | |
Sum squared absolute values of elements of an array over the given dimensions. | |
""" | |
sumabs2(A,dims) | |
""" | |
showall(x) | |
Similar to [`show`](:func:`show`), except shows all elements of arrays. | |
""" | |
showall | |
""" | |
mimewritable(mime, x) | |
Returns a boolean value indicating whether or not the object `x` can be written as the given | |
`mime` type. (By default, this is determined automatically by the existence of the | |
corresponding [`show`](:func:`show`) function for `typeof(x)`.) | |
""" | |
mimewritable | |
""" | |
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 | |
""" | |
coth(x) | |
Compute the hyperbolic cotangent of `x`. | |
""" | |
coth | |
""" | |
start(iter) -> state | |
Get initial iteration state for an iterable object. | |
""" | |
start | |
""" | |
readavailable(stream) | |
Read all available data on the stream, blocking the task only if no data is available. The | |
result is a `Vector{UInt8,1}`. | |
""" | |
readavailable | |
""" | |
isa(x, type) -> Bool | |
Determine whether `x` is of the given `type`. | |
""" | |
isa | |
""" | |
unsafe_load(p::Ptr{T}, [i::Integer=1]) | |
Load a value of type `T` from the address of the ith element (1-indexed) starting at `p`. | |
This is equivalent to the C expression `p[i-1]`. | |
The `unsafe` prefix on this function indicates that no validation is performed on the | |
pointer `p` to ensure that it is valid. Incorrect usage may segfault your program or return | |
garbage answers, in the same manner as C. | |
""" | |
unsafe_load | |
""" | |
catch_backtrace() | |
Get the backtrace of the current exception, for use within `catch` blocks. | |
""" | |
catch_backtrace | |
""" | |
cos(x) | |
Compute cosine of `x`, where `x` is in radians. | |
""" | |
cos | |
""" | |
maxabs(A, dims) | |
Compute the maximum absolute values over given dimensions. | |
""" | |
maxabs(A,dims) | |
""" | |
done(iter, state) -> Bool | |
Test whether we are done iterating. | |
""" | |
done | |
""" | |
convert(T, x) | |
Convert `x` to a value of type `T`. | |
If `T` is an `Integer` type, an [`InexactError`](:exc:`InexactError`) 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`. | |
```jldoctest | |
julia> convert(Int, 3.0) | |
3 | |
julia> convert(Int, 3.5) | |
ERROR: InexactError() | |
in convert(::Type{Int64}, ::Float64) at ./int.jl:330 | |
... | |
``` | |
If `T` is a [`AbstractFloat`](:obj:`AbstractFloat`) or [`Rational`](:obj:`Rational`) 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. | |
```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`](:func:`muladd`). | |
""" | |
fma | |
""" | |
eigvals(A,[irange,][vl,][vu]) -> values | |
Returns the eigenvalues of `A`. If `A` is `Symmetric`, `Hermitian` or `SymTridiagonal`, | |
it is possible to calculate only a subset of the eigenvalues by specifying either a | |
`UnitRange` `irange` covering indices of the sorted eigenvalues, or a pair `vl` and `vu` | |
for the lower and upper boundaries of the eigenvalues. | |
For general non-symmetric matrices it is possible to specify how the matrix is balanced | |
before the eigenvector calculation. The option `permute=true` permutes the matrix to | |
become closer to upper triangular, and `scale=true` scales the matrix by its diagonal | |
elements to make rows and columns moreequal in norm. The default is `true` for both | |
options. | |
""" | |
eigvals | |
""" | |
pointer_from_objref(object_instance) | |
Get the memory address of a Julia object as a `Ptr`. The existence of the resulting `Ptr` | |
will not protect the object from garbage collection, so you must ensure that the object | |
remains referenced for the whole time that the `Ptr` will be used. | |
""" | |
pointer_from_objref | |
""" | |
copy!(dest, src) | |
Copy all elements from collection `src` to array `dest`. Returns `dest`. | |
""" | |
copy!(dest,src) | |
""" | |
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!(A, X, inds...) | |
Store values from array `X` within some subset of `A` as specified by `inds`. | |
""" | |
setindex!(A::AbstractArray,X,inds...) | |
""" | |
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...) | |
""" | |
signif(x, digits, [base]) | |
Rounds (in the sense of [`round`](:func:`round`)) `x` so that there are `digits` significant digits, under a | |
base `base` representation, default 10. E.g., `signif(123.456, 2)` is `120.0`, and | |
`signif(357.913, 4, 2)` is `352.0`. | |
""" | |
signif | |
""" | |
full(F) | |
Reconstruct the matrix `A` from the factorization `F=factorize(A)`. | |
""" | |
full(F) | |
""" | |
throw(e) | |
Throw an object as an exception. | |
""" | |
throw | |
""" | |
issubset(a, b) | |
⊆(a,b) -> Bool | |
⊈(a,b) -> Bool | |
⊊(a,b) -> Bool | |
Determine whether every element of `a` is also in `b`, using [`in`](:func:`in`). | |
""" | |
issubset(a,b) | |
""" | |
issubset(A, S) -> Bool | |
⊆(A,S) -> Bool | |
Return `true` if `A` is a subset of or equal to `S`. | |
""" | |
issubset | |
""" | |
zero(x) | |
Get the additive identity element for the type of `x` (`x` can also specify the type itself). | |
""" | |
zero | |
""" | |
any(A, dims) | |
Test whether any values along the given dimensions of an array are `true`. | |
""" | |
any(::AbstractArray,dims) | |
""" | |
zeros(type, dims) | |
Create an array of all zeros of specified type. | |
The type defaults to `Float64` if not specified. | |
```jldoctest | |
julia> zeros(Int8, 2, 3) | |
2×3 Array{Int8,2}: | |
0 0 0 | |
0 0 0 | |
``` | |
""" | |
zeros(t,dims) | |
""" | |
zeros(A) | |
Create an array of all zeros with the same element type and shape as `A`. | |
```jldoctest | |
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 | |
``` | |
""" | |
zeros(A) | |
""" | |
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`. | |
Values for `String` can be of that type, or `Vector{UInt8}`. | |
""" | |
isvalid(T,value) | |
""" | |
unsigned(x) -> Unsigned | |
Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as | |
unsigned without checking for negative values. | |
""" | |
unsigned | |
""" | |
midpoints(e) | |
Compute the midpoints of the bins with edges `e`. The result is a vector/range of length | |
`length(e) - 1`. Note: Julia does not ignore `NaN` values in the computation. | |
""" | |
midpoints | |
""" | |
.+(x, y) | |
Element-wise addition operator. | |
```jldoctest | |
julia> A = [1 2; 3 4]; | |
julia> B = [5 6; 7 8]; | |
julia> C = [A, B] | |
2-element Array{Array{Int64,2},1}: | |
[1 2; 3 4] | |
[5 6; 7 8] | |
julia> C .+ [[1; 2] [3; 4]] | |
2×2 Array{Array{Int64,2},2}: | |
[2 3; 4 5] [4 5; 6 7] | |
[7 8; 9 10] [9 10; 11 12] | |
``` | |
See also [`broadcast`](:func:`broadcast`). | |
""" | |
Base.:(.+) | |
""" | |
reverseind(v, i) | |
Given an index `i` in `reverse(v)`, return the corresponding index in `v` so that | |
`v[reverseind(v,i)] == reverse(v)[i]`. (This can be nontrivial in the case where `v` is a | |
Unicode string.) | |
""" | |
reverseind | |
""" | |
float(x) | |
Convert a number, array, or string to a `AbstractFloat` data type. For numeric data, the | |
smallest suitable `AbstractFloat` type is used. Converts strings to `Float64`. | |
""" | |
float | |
""" | |
signbit(x) | |
Returns `true` if the value of the sign of `x` is negative, otherwise `false`. | |
```jldoctest | |
julia> signbit(-4) | |
true | |
julia> signbit(5) | |
false | |
julia> signbit(5.5) | |
false | |
julia> signbit(-4.1) | |
true | |
``` | |
""" | |
signbit | |
""" | |
cscd(x) | |
Compute the cosecant of `x`, where `x` is in degrees. | |
""" | |
cscd | |
""" | |
tryparse(type, str, [base]) | |
Like [`parse`](:func:`parse`), but returns a [`Nullable`](:obj:`Nullable`) of the requested type. The result will be null if the | |
string does not contain a valid number. | |
""" | |
tryparse | |
""" | |
all!(r, A) | |
Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. | |
""" | |
all! | |
""" | |
exit([code]) | |
Quit (or control-D at the prompt). The default exit code is zero, indicating that the | |
processes completed successfully. | |
""" | |
exit | |
""" | |
skipchars(stream, predicate; linecomment::Char) | |
Advance the stream until before the first character for which `predicate` returns `false`. | |
For example `skipchars(stream, isspace)` will skip all whitespace. If keyword argument | |
`linecomment` is specified, characters from that character through the end of a line will | |
also be skipped. | |
""" | |
skipchars | |
""" | |
realmin(T) | |
The smallest in absolute value non-subnormal value representable by the given floating-point DataType `T`. | |
""" | |
realmin | |
""" | |
union!(s, iterable) | |
Union each element of `iterable` into set `s` in-place. | |
""" | |
union! | |
""" | |
deepcopy(x) | |
Create a deep copy of `x`: everything is copied recursively, resulting in a fully | |
independent object. For example, deep-copying an array produces a new array whose elements | |
are deep copies of the original elements. Calling `deepcopy` on an object should generally | |
have the same effect as serializing and then deserializing it. | |
As a special case, functions can only be actually deep-copied if they are anonymous, | |
otherwise they are just copied. The difference is only relevant in the case of closures, | |
i.e. functions which may contain hidden internal references. | |
While it isn't normally necessary, user-defined types can override the default `deepcopy` | |
behavior by defining a specialized version of the function `deepcopy_internal(x::T, dict::ObjectIdDict)` | |
(which shouldn't otherwise be used), where `T` is the type to be specialized for, and `dict` | |
keeps track of objects copied so far within the recursion. Within the definition, | |
`deepcopy_internal` should be used in place of `deepcopy`, and the `dict` variable should be | |
updated as appropriate before returning. | |
""" | |
deepcopy | |
""" | |
widen(x) | |
If `x` is a type, return a "larger" type (for numeric types, this will be | |
a type with at least as much range and precision as the argument, and usually more). | |
Otherwise `x` is converted to `widen(typeof(x))`. | |
```jldoctest | |
julia> widen(Int32) | |
Int64 | |
julia> widen(1.5f0) | |
1.5 | |
``` | |
""" | |
widen | |
""" | |
Set([itr]) | |
Construct a [`Set`](:obj:`Set`) of the values generated by the given iterable object, or an | |
empty set. Should be used instead of [`IntSet`](:obj:`IntSet`) for sparse integer sets, or | |
for sets of arbitrary objects. | |
""" | |
Set | |
""" | |
erf(x) | |
Compute the error function of `x`, defined by ``\\frac{2}{\\sqrt{\\pi}} \\int_0^x e^{-t^2} dt`` | |
for arbitrary complex `x`. | |
""" | |
erf | |
""" | |
signed(x) | |
Convert a number to a signed integer. If the argument is unsigned, it is reinterpreted as | |
signed without checking for overflow. | |
""" | |
signed | |
""" | |
Val{c} | |
Create a "value type" out of `c`, which must be an `isbits` value. The intent of this | |
construct is to be able to dispatch on constants, e.g., `f(Val{false})` allows you to | |
dispatch directly (at compile-time) to an implementation `f(::Type{Val{false}})`, without | |
having to test the boolean value at runtime. | |
""" | |
Val | |
""" | |
|(x, y) | |
Bitwise or. | |
```jldoctest | |
julia> 4 | 10 | |
14 | |
julia> 4 | 1 | |
5 | |
``` | |
""" | |
Base.:(|) | |
""" | |
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. | |
""" | |
pop!(collection,key,?) | |
""" | |
pop!(collection) -> item | |
Remove the last item in `collection` and return it. | |
```jldoctest | |
julia> A=[1, 2, 3, 4, 5, 6] | |
6-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
julia> pop!(A) | |
6 | |
julia> A | |
5-element Array{Int64,1}: | |
1 | |
2 | |
3 | |
4 | |
5 | |
``` | |
""" | |
pop!(collection) | |
""" | |
seekend(s) | |
Seek a stream to its end. | |
""" | |
seekend | |
""" | |
DivideError() | |
Integer division was attempted with a denominator value of 0. | |
""" | |
DivideError | |
""" | |
unsafe_pointer_to_objref(p::Ptr) | |
Convert a `Ptr` to an object reference. Assumes the pointer refers to a valid heap-allocated | |
Julia object. If this is not the case, undefined behavior results, hence this function is | |
considered "unsafe" and should be used with care. | |
""" | |
unsafe_pointer_to_objref | |
""" | |
dawson(x) | |
Compute the Dawson function (scaled imaginary error function) of `x`, defined by | |
``\\frac{\\sqrt{\\pi}}{2} e^{-x^2} \\operatorname{erfi}(x)``. | |
""" | |
dawson | |
""" | |
\$(x, y) | |
Bitwise exclusive or. | |
""" | |
Base.:$(x, y) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
include("helpdb/Base.jl") | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Text / HTML objects | |
import Base: print, show | |
export HTML, @html_str | |
export HTML, Text, apropos | |
""" | |
`HTML(s)`: Create an object that renders `s` as html. | |
HTML("<div>foo</div>") | |
You can also use a stream for large amounts of data: | |
HTML() do io | |
println(io, "<div>foo</div>") | |
end | |
""" | |
type HTML{T} | |
content::T | |
end | |
function HTML(xs...) | |
HTML() do io | |
for x in xs | |
show(io, MIME"text/html"(), x) | |
end | |
end | |
end | |
show(io::IO, ::MIME"text/html", h::HTML) = print(io, h.content) | |
show{F <: Function}(io::IO, ::MIME"text/html", h::HTML{F}) = h.content(io) | |
""" | |
@html_str -> Docs.HTML | |
Create an `HTML` object from a literal string. | |
""" | |
macro html_str(s) | |
:(HTML($s)) | |
end | |
function catdoc(xs::HTML...) | |
HTML() do io | |
for x in xs | |
show(io, MIME"text/html"(), x) | |
end | |
end | |
end | |
export Text, @text_str | |
""" | |
`Text(s)`: Create an object that renders `s` as plain text. | |
Text("foo") | |
You can also use a stream for large amounts of data: | |
Text() do io | |
println(io, "foo") | |
end | |
""" | |
type Text{T} | |
content::T | |
end | |
print(io::IO, t::Text) = print(io, t.content) | |
print{F <: Function}(io::IO, t::Text{F}) = t.content(io) | |
show(io::IO, t::Text) = print(io, t) | |
""" | |
@text_str -> Docs.Text | |
Create a `Text` object from a literal string. | |
""" | |
macro text_str(s) | |
:(Text($s)) | |
end | |
function catdoc(xs::Text...) | |
Text() do io | |
for x in xs | |
show(io, MIME"text/plain"(), x) | |
end | |
end | |
end | |
# REPL help | |
function helpmode(line::AbstractString) | |
line = strip(line) | |
expr = | |
if haskey(keywords, Symbol(line)) | |
# Docs for keywords must be treated separately since trying to parse a single | |
# keyword such as `function` would throw a parse error due to the missing `end`. | |
Symbol(line) | |
else | |
x = Base.syntax_deprecation_warnings(false) do | |
parse(line, raise = false) | |
end | |
# Retrieving docs for macros requires us to make a distinction between the text | |
# `@macroname` and `@macroname()`. These both parse the same, but are used by | |
# the docsystem to return different results. The first returns all documentation | |
# for `@macroname`, while the second returns *only* the docs for the 0-arg | |
# definition if it exists. | |
(isexpr(x, :macrocall, 1) && !endswith(line, "()")) ? quot(x) : x | |
end | |
:(Base.Docs.@repl $expr) | |
end | |
function repl_search(io::IO, s) | |
pre = "search:" | |
print(io, pre) | |
printmatches(io, s, completions(s), cols = displaysize(io)[2] - length(pre)) | |
println(io, "\n") | |
end | |
repl_search(s) = repl_search(STDOUT, s) | |
function repl_corrections(io::IO, s) | |
print(io, "Couldn't find ") | |
Markdown.with_output_format(:cyan, io) do io | |
println(io, s) | |
end | |
print_correction(io, s) | |
end | |
repl_corrections(s) = repl_corrections(STDOUT, s) | |
macro repl(ex) repl(ex) end | |
function repl(s::Symbol) | |
quote | |
repl_search($(string(s))) | |
($(isdefined(s) || haskey(keywords, s))) || repl_corrections($(string(s))) | |
$(_repl(s)) | |
end | |
end | |
isregex(x) = isexpr(x, :macrocall, 2) && x.args[1] === Symbol("@r_str") && !isempty(x.args[2]) | |
repl(ex::Expr) = isregex(ex) ? :(apropos($ex)) : _repl(ex) | |
repl(str::AbstractString) = :(apropos($str)) | |
repl(other) = :(@doc $(esc(other))) | |
function _repl(x) | |
docs = (isexpr(x, :call) && !any(isexpr(x, :(::)) for x in x.args)) ? | |
Base.gen_call_with_extracted_types(doc, x) : :(@doc $(esc(x))) | |
if isfield(x) | |
quote | |
if isa($(esc(x.args[1])), DataType) | |
fielddoc($(esc(x.args[1])), $(esc(x.args[2]))) | |
else | |
$docs | |
end | |
end | |
else | |
docs | |
end | |
end | |
# Search & Rescue | |
# Utilities for correcting user mistakes and (eventually) | |
# doing full documentation searches from the repl. | |
# Fuzzy Search Algorithm | |
function matchinds(needle, haystack; acronym = false) | |
chars = collect(needle) | |
is = Int[] | |
lastc = '\0' | |
for (i, char) in enumerate(haystack) | |
isempty(chars) && break | |
while chars[1] == ' ' shift!(chars) end # skip spaces | |
if lowercase(char) == lowercase(chars[1]) && (!acronym || !isalpha(lastc)) | |
push!(is, i) | |
shift!(chars) | |
end | |
lastc = char | |
end | |
return is | |
end | |
longer(x, y) = length(x) ≥ length(y) ? (x, true) : (y, false) | |
bestmatch(needle, haystack) = | |
longer(matchinds(needle, haystack, acronym = true), | |
matchinds(needle, haystack)) | |
avgdistance(xs) = | |
isempty(xs) ? 0 : | |
(xs[end] - xs[1] - length(xs)+1)/length(xs) | |
function fuzzyscore(needle, haystack) | |
score = 0. | |
is, acro = bestmatch(needle, haystack) | |
score += (acro?2:1)*length(is) # Matched characters | |
score -= 2(length(needle)-length(is)) # Missing characters | |
!acro && (score -= avgdistance(is)/10) # Contiguous | |
!isempty(is) && (score -= mean(is)/100) # Closer to beginning | |
return score | |
end | |
function fuzzysort(search, candidates) | |
scores = map(cand -> (fuzzyscore(search, cand), -levenshtein(search, cand)), candidates) | |
candidates[sortperm(scores)] |> reverse | |
end | |
# Levenshtein Distance | |
function levenshtein(s1, s2) | |
a, b = collect(s1), collect(s2) | |
m = length(a) | |
n = length(b) | |
d = Array{Int}(m+1, n+1) | |
d[1:m+1, 1] = 0:m | |
d[1, 1:n+1] = 0:n | |
for i = 1:m, j = 1:n | |
d[i+1,j+1] = min(d[i , j+1] + 1, | |
d[i+1, j ] + 1, | |
d[i , j ] + (a[i] != b[j])) | |
end | |
return d[m+1, n+1] | |
end | |
function levsort(search, candidates) | |
scores = map(cand -> (levenshtein(search, cand), -fuzzyscore(search, cand)), candidates) | |
candidates = candidates[sortperm(scores)] | |
i = 0 | |
for i = 1:length(candidates) | |
levenshtein(search, candidates[i]) > 3 && break | |
end | |
return candidates[1:i] | |
end | |
# Result printing | |
function printmatch(io::IO, word, match) | |
is, _ = bestmatch(word, match) | |
Markdown.with_output_format(:fade, io) do io | |
for (i, char) = enumerate(match) | |
if i in is | |
Markdown.with_output_format(print, :bold, io, char) | |
else | |
print(io, char) | |
end | |
end | |
end | |
end | |
printmatch(args...) = printfuzzy(STDOUT, args...) | |
function printmatches(io::IO, word, matches; cols = displaysize(io)[2]) | |
total = 0 | |
for match in matches | |
total + length(match) + 1 > cols && break | |
fuzzyscore(word, match) < 0 && break | |
print(io, " ") | |
printmatch(io, word, match) | |
total += length(match) + 1 | |
end | |
end | |
printmatches(args...; cols = displaysize(STDOUT)[2]) = printmatches(STDOUT, args..., cols = cols) | |
function print_joined_cols(io::IO, ss, delim = "", last = delim; cols = displaysize(io)[2]) | |
i = 0 | |
total = 0 | |
for i = 1:length(ss) | |
total += length(ss[i]) | |
total + max(i-2,0)*length(delim) + (i>1?1:0)*length(last) > cols && (i-=1; break) | |
end | |
join(io, ss[1:i], delim, last) | |
end | |
print_joined_cols(args...; cols = displaysize(STDOUT)[2]) = print_joined_cols(STDOUT, args...; cols=cols) | |
function print_correction(io, word) | |
cors = levsort(word, accessible(current_module())) | |
pre = "Perhaps you meant " | |
print(io, pre) | |
print_joined_cols(io, cors, ", ", " or "; cols = displaysize(io)[2] - length(pre)) | |
println(io) | |
return | |
end | |
print_correction(word) = print_correction(STDOUT, word) | |
# Completion data | |
const builtins = ["abstract", "baremodule", "begin", "bitstype", "break", | |
"catch", "ccall", "const", "continue", "do", "else", | |
"elseif", "end", "export", "finally", "for", "function", | |
"global", "if", "immutable", "import", "importall", "let", | |
"local", "macro", "module", "quote", "return", "try", "type", | |
"typealias", "using", "while"] | |
moduleusings(mod) = ccall(:jl_module_usings, Any, (Any,), mod) | |
filtervalid(names) = filter(x->!ismatch(r"#", x), map(string, names)) | |
accessible(mod::Module) = | |
[filter!(s->Base.isdeprecated(mod, s), names(mod, true, true)); | |
map(names, moduleusings(mod))...; | |
builtins] |> unique |> filtervalid | |
completions(name) = fuzzysort(name, accessible(current_module())) | |
completions(name::Symbol) = completions(string(name)) | |
# Searching and apropos | |
# Docsearch simply returns true or false if an object contains the given needle | |
docsearch(haystack::AbstractString, needle) = !isempty(search(haystack, needle)) | |
docsearch(haystack::Symbol, needle) = docsearch(string(haystack), needle) | |
docsearch(::Void, needle) = false | |
function docsearch(haystack::Array, needle) | |
for elt in haystack | |
docsearch(elt, needle) && return true | |
end | |
false | |
end | |
function docsearch(haystack, needle) | |
Base.warn_once("unable to search documentation of type $(typeof(haystack))") | |
false | |
end | |
## Searching specific documentation objects | |
function docsearch(haystack::MultiDoc, needle) | |
for v in values(haystack.docs) | |
docsearch(v, needle) && return true | |
end | |
false | |
end | |
function docsearch(haystack::DocStr, needle) | |
docsearch(parsedoc(haystack), needle) && return true | |
if haskey(haystack.data, :fields) | |
for doc in values(haystack.data[:fields]) | |
docsearch(doc, needle) && return true | |
end | |
end | |
false | |
end | |
## Markdown search simply strips all markup and searches plain text version | |
docsearch(haystack::Markdown.MD, needle) = | |
docsearch(stripmd(haystack.content), needle) | |
""" | |
stripmd(x) | |
Strip all Markdown markup from x, leaving the result in plain text. Used | |
internally by apropos to make docstrings containing more than one markdown | |
element searchable. | |
""" | |
stripmd(x::AbstractString) = x # base case | |
stripmd(x::Void) = " " | |
stripmd(x::Vector) = string(map(stripmd, x)...) | |
stripmd(x::Markdown.BlockQuote) = "$(stripmd(x.content))" | |
stripmd(x::Markdown.Admonition) = "$(stripmd(x.content))" | |
stripmd(x::Markdown.Bold) = "$(stripmd(x.text))" | |
stripmd(x::Markdown.Code) = "$(stripmd(x.code))" | |
stripmd{N}(x::Markdown.Header{N}) = stripmd(x.text) | |
stripmd(x::Markdown.HorizontalRule) = " " | |
stripmd(x::Markdown.Image) = "$(stripmd(x.alt)) $(x.url)" | |
stripmd(x::Markdown.Italic) = "$(stripmd(x.text))" | |
stripmd(x::Markdown.LaTeX) = "$(x.formula)" | |
stripmd(x::Markdown.LineBreak) = " " | |
stripmd(x::Markdown.Link) = "$(stripmd(x.text)) $(x.url)" | |
stripmd(x::Markdown.List) = join(map(stripmd, x.items), " ") | |
stripmd(x::Markdown.MD) = join(map(stripmd, x.content), " ") | |
stripmd(x::Markdown.Paragraph) = stripmd(x.content) | |
stripmd(x::Markdown.Footnote) = "$(stripmd(x.id)) $(stripmd(x.text))" | |
stripmd(x::Markdown.Table) = | |
join([join(map(stripmd, r), " ") for r in x.rows], " ") | |
# Apropos searches through all available documentation for some string or regex | |
""" | |
apropos(string) | |
Search through all documentation for a string, ignoring case. | |
""" | |
apropos(string) = apropos(STDOUT, string) | |
apropos(io::IO, string) = apropos(io, Regex("\\Q$string", "i")) | |
function apropos(io::IO, needle::Regex) | |
for mod in modules | |
# Module doc might be in README.md instead of the META dict | |
docsearch(doc(mod), needle) && println(io, mod) | |
for (k, v) in meta(mod) | |
docsearch(v, needle) && println(io, k) | |
end | |
end | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module DSP | |
import Base.trailingsize | |
export filt, filt!, deconv, conv, conv2, xcorr | |
_zerosi(b,a,T) = zeros(promote_type(eltype(b), eltype(a), T), max(length(a), length(b))-1) | |
""" | |
filt(b, a, x, [si]) | |
Apply filter described by vectors `a` and `b` to vector `x`, with an optional initial filter | |
state vector `si` (defaults to zeros). | |
""" | |
function filt{T,S}(b::Union{AbstractVector, Number}, a::Union{AbstractVector, Number}, | |
x::AbstractArray{T}, si::AbstractArray{S}=_zerosi(b,a,T)) | |
filt!(Array{promote_type(eltype(b), eltype(a), T, S)}(size(x)), b, a, x, si) | |
end | |
# in-place filtering: returns results in the out argument, which may shadow x | |
# (and does so by default) | |
""" | |
filt!(out, b, a, x, [si]) | |
Same as [`filt`](:func:`filt`) but writes the result into the `out` argument, which may | |
alias the input `x` to modify it in-place. | |
""" | |
function filt!{T,S,N}(out::AbstractArray, b::Union{AbstractVector, Number}, a::Union{AbstractVector, Number}, | |
x::AbstractArray{T}, si::AbstractArray{S,N}=_zerosi(b,a,T)) | |
isempty(b) && throw(ArgumentError("filter vector b must be non-empty")) | |
isempty(a) && throw(ArgumentError("filter vector a must be non-empty")) | |
a[1] == 0 && throw(ArgumentError("filter vector a[1] must be nonzero")) | |
if size(x) != size(out) | |
throw(ArgumentError("output size $(size(out)) must match input size $(size(x))")) | |
end | |
as = length(a) | |
bs = length(b) | |
sz = max(as, bs) | |
silen = sz - 1 | |
ncols = trailingsize(x,2) | |
if size(si, 1) != silen | |
throw(ArgumentError("initial state vector si must have max(length(a),length(b))-1 rows")) | |
end | |
if N > 1 && trailingsize(si,2) != ncols | |
throw(ArgumentError("initial state vector si must be a vector or have the same number of columns as x")) | |
end | |
size(x,1) == 0 && return out | |
sz == 1 && return scale!(out, x, b[1]/a[1]) # Simple scaling without memory | |
# Filter coefficient normalization | |
if a[1] != 1 | |
norml = a[1] | |
a ./= norml | |
b ./= norml | |
end | |
# Pad the coefficients with zeros if needed | |
bs<sz && (b = copy!(zeros(eltype(b), sz), b)) | |
1<as<sz && (a = copy!(zeros(eltype(a), sz), a)) | |
initial_si = si | |
for col = 1:ncols | |
# Reset the filter state | |
si = initial_si[:, N > 1 ? col : 1] | |
if as > 1 | |
_filt_iir!(out, b, a, x, si, col) | |
else | |
_filt_fir!(out, b, x, si, col) | |
end | |
end | |
return out | |
end | |
function _filt_iir!(out, b, a, x, si, col) | |
silen = length(si) | |
@inbounds for i=1:size(x, 1) | |
xi = x[i,col] | |
val = si[1] + b[1]*xi | |
for j=1:(silen-1) | |
si[j] = si[j+1] + b[j+1]*xi - a[j+1]*val | |
end | |
si[silen] = b[silen+1]*xi - a[silen+1]*val | |
out[i,col] = val | |
end | |
end | |
function _filt_fir!(out, b, x, si, col) | |
silen = length(si) | |
@inbounds for i=1:size(x, 1) | |
xi = x[i,col] | |
val = si[1] + b[1]*xi | |
for j=1:(silen-1) | |
si[j] = si[j+1] + b[j+1]*xi | |
end | |
si[silen] = b[silen+1]*xi | |
out[i,col] = val | |
end | |
end | |
""" | |
deconv(b,a) -> c | |
Construct vector `c` such that `b = conv(a,c) + r`. | |
Equivalent to polynomial division. | |
""" | |
function deconv{T}(b::StridedVector{T}, a::StridedVector{T}) | |
lb = size(b,1) | |
la = size(a,1) | |
if lb < la | |
return [zero(T)] | |
end | |
lx = lb-la+1 | |
x = zeros(T, lx) | |
x[1] = 1 | |
filt(b, a, x) | |
end | |
""" | |
conv(u,v) | |
Convolution of two vectors. Uses FFT algorithm. | |
""" | |
function conv{T<:Base.LinAlg.BlasFloat}(u::StridedVector{T}, v::StridedVector{T}) | |
nu = length(u) | |
nv = length(v) | |
n = nu + nv - 1 | |
np2 = n > 1024 ? nextprod([2,3,5], n) : nextpow2(n) | |
upad = [u; zeros(T, np2 - nu)] | |
vpad = [v; zeros(T, np2 - nv)] | |
if T <: Real | |
p = plan_rfft(upad) | |
y = irfft((p*upad).*(p*vpad), np2) | |
else | |
p = plan_fft!(upad) | |
y = ifft!((p*upad).*(p*vpad)) | |
end | |
return y[1:n] | |
end | |
conv{T<:Integer}(u::StridedVector{T}, v::StridedVector{T}) = round(Int,conv(float(u), float(v))) | |
conv{T<:Integer, S<:Base.LinAlg.BlasFloat}(u::StridedVector{T}, v::StridedVector{S}) = conv(float(u), v) | |
conv{T<:Integer, S<:Base.LinAlg.BlasFloat}(u::StridedVector{S}, v::StridedVector{T}) = conv(u, float(v)) | |
""" | |
conv2(u,v,A) | |
2-D convolution of the matrix `A` with the 2-D separable kernel generated by | |
the vectors `u` and `v`. | |
Uses 2-D FFT algorithm. | |
""" | |
function conv2{T}(u::StridedVector{T}, v::StridedVector{T}, A::StridedMatrix{T}) | |
m = length(u)+size(A,1)-1 | |
n = length(v)+size(A,2)-1 | |
B = zeros(T, m, n) | |
B[1:size(A,1),1:size(A,2)] = A | |
u = fft([u;zeros(T,m-length(u))]) | |
v = fft([v;zeros(T,n-length(v))]) | |
C = ifft(fft(B) .* (u * v.')) | |
if T <: Real | |
return real(C) | |
end | |
return C | |
end | |
""" | |
conv2(B,A) | |
2-D convolution of the matrix `B` with the matrix `A`. Uses 2-D FFT algorithm. | |
""" | |
function conv2{T}(A::StridedMatrix{T}, B::StridedMatrix{T}) | |
sa, sb = size(A), size(B) | |
At = zeros(T, sa[1]+sb[1]-1, sa[2]+sb[2]-1) | |
Bt = zeros(T, sa[1]+sb[1]-1, sa[2]+sb[2]-1) | |
At[1:sa[1], 1:sa[2]] = A | |
Bt[1:sb[1], 1:sb[2]] = B | |
p = plan_fft(At) | |
C = ifft((p*At).*(p*Bt)) | |
if T <: Real | |
return real(C) | |
end | |
return C | |
end | |
conv2{T<:Integer}(A::StridedMatrix{T}, B::StridedMatrix{T}) = round(Int,conv2(float(A), float(B))) | |
conv2{T<:Integer}(u::StridedVector{T}, v::StridedVector{T}, A::StridedMatrix{T}) = round(Int,conv2(float(u), float(v), float(A))) | |
""" | |
xcorr(u,v) | |
Compute the cross-correlation of two vectors. | |
""" | |
function xcorr(u, v) | |
su = size(u,1); sv = size(v,1) | |
if su < sv | |
u = [u;zeros(eltype(u),sv-su)] | |
elseif sv < su | |
v = [v;zeros(eltype(v),su-sv)] | |
end | |
flipdim(conv(flipdim(u, 1), v), 1) | |
end | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
#= | |
import JSON | |
emojis = JSON.parsefile(download("https://raw.githubusercontent.com/iamcal/emoji-data/0f0cf4ea8845eb52d26df2a48c3c31c3b8cad14e/emoji_pretty.json")) | |
result = Dict() | |
for emj in emojis | |
name = "\\:" * emj["short_name"] * ":" | |
unicode = emj["unified"] | |
if '-' in unicode | |
continue | |
end | |
result[name] = "$(Char(parse(UInt32, unicode, 16)))" | |
end | |
skeys = sort(collect(keys(result))) | |
open("emoji_symbols.jl", "w") do fh | |
println(fh, "const emoji_symbols = Dict(") | |
for key in skeys | |
println(fh, " \"", escape_string(key), "\" => \"", | |
escape_string(result[key]), "\",") | |
end | |
println(fh, ")") | |
end | |
=# | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
if is_windows() | |
const ERROR_ENVVAR_NOT_FOUND = UInt32(203) | |
_getenvlen(var::Vector{UInt16}) = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,C_NULL,0) | |
_hasenv(s::Vector{UInt16}) = _getenvlen(s) != 0 || Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND | |
_hasenv(s::AbstractString) = _hasenv(cwstring(s)) | |
function access_env(onError::Function, str::AbstractString) | |
var = cwstring(str) | |
len = _getenvlen(var) | |
if len == 0 | |
return Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND ? "" : onError(str) | |
end | |
val = zeros(UInt16,len) | |
ret = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,val,len) | |
if (ret == 0 && len != 1) || ret != len-1 || val[end] != 0 | |
error(string("getenv: ", str, ' ', len, "-1 != ", ret, ": ", Libc.FormatMessage())) | |
end | |
pop!(val) # NUL | |
return transcode(String, val) | |
end | |
function _setenv(svar::AbstractString, sval::AbstractString, overwrite::Bool=true) | |
var = cwstring(svar) | |
val = cwstring(sval) | |
if overwrite || !_hasenv(var) | |
ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,val) | |
systemerror(:setenv, ret == 0) | |
end | |
end | |
function _unsetenv(svar::AbstractString) | |
var = cwstring(svar) | |
ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,C_NULL) | |
systemerror(:setenv, ret == 0) | |
end | |
else # !windows | |
_getenv(var::AbstractString) = ccall(:getenv, Cstring, (Cstring,), var) | |
_hasenv(s::AbstractString) = _getenv(s) != C_NULL | |
function access_env(onError::Function, var::AbstractString) | |
val = _getenv(var) | |
val == C_NULL ? onError(var) : unsafe_string(val) | |
end | |
function _setenv(var::AbstractString, val::AbstractString, overwrite::Bool=true) | |
ret = ccall(:setenv, Int32, (Cstring,Cstring,Int32), var, val, overwrite) | |
systemerror(:setenv, ret != 0) | |
end | |
function _unsetenv(var::AbstractString) | |
ret = ccall(:unsetenv, Int32, (Cstring,), var) | |
systemerror(:unsetenv, ret != 0) | |
end | |
end # os test | |
## ENV: hash interface ## | |
""" | |
EnvHash() -> EnvHash | |
A singleton of this type provides a hash table interface to environment variables. | |
""" | |
type EnvHash <: Associative{String,String}; end | |
""" | |
ENV | |
Reference to the singleton `EnvHash`, providing a dictionary interface to system environment | |
variables. | |
""" | |
const ENV = EnvHash() | |
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) | |
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 | |
delete!(::EnvHash, k::AbstractString) = (_unsetenv(k); ENV) | |
setindex!(::EnvHash, v, k::AbstractString) = _setenv(k,string(v)) | |
push!(::EnvHash, k::AbstractString, v) = setindex!(ENV, v, k) | |
if is_windows() | |
start(hash::EnvHash) = (pos = ccall(:GetEnvironmentStringsW,stdcall,Ptr{UInt16},()); (pos,pos)) | |
function done(hash::EnvHash, block::Tuple{Ptr{UInt16},Ptr{UInt16}}) | |
if unsafe_load(block[1]) == 0 | |
ccall(:FreeEnvironmentStringsW, stdcall, Int32, (Ptr{UInt16},), block[2]) | |
return true | |
end | |
return false | |
end | |
function next(hash::EnvHash, block::Tuple{Ptr{UInt16},Ptr{UInt16}}) | |
pos = block[1] | |
blk = block[2] | |
len = ccall(:wcslen, UInt, (Ptr{UInt16},), pos) | |
buf = Array{UInt16}(len) | |
unsafe_copy!(pointer(buf), pos, len) | |
env = transcode(String, buf) | |
m = match(r"^(=?[^=]+)=(.*)$"s, env) | |
if m === nothing | |
error("malformed environment entry: $env") | |
end | |
return (Pair{String,String}(m.captures[1], m.captures[2]), (pos+(len+1)*2, blk)) | |
end | |
else # !windows | |
start(::EnvHash) = 0 | |
done(::EnvHash, i) = (ccall(:jl_environ, Any, (Int32,), i) === nothing) | |
function next(::EnvHash, i) | |
env = ccall(:jl_environ, Any, (Int32,), i) | |
if env === nothing | |
throw(BoundsError()) | |
end | |
env = env::String | |
m = match(r"^(.*?)=(.*)$"s, env) | |
if m === nothing | |
error("malformed environment entry: $env") | |
end | |
return (Pair{String,String}(m.captures[1], m.captures[2]), i+1) | |
end | |
end # os-test | |
#TODO: Make these more efficent | |
function length(::EnvHash) | |
i = 0 | |
for (k,v) in ENV | |
i += 1 | |
end | |
return i | |
end | |
function show(io::IO, ::EnvHash) | |
for (k,v) = ENV | |
println(io, "$k=$v") | |
end | |
end | |
""" | |
withenv(f::Function, kv::Pair...) | |
Execute `f()` in an environment that is temporarily modified (not replaced as in `setenv`) | |
by zero or more `"var"=>val` arguments `kv`. `withenv` is generally used via the | |
`withenv(kv...) do ... end` syntax. A value of `nothing` can be used to temporarily unset an | |
environment variable (if it is set). When `withenv` returns, the original environment has | |
been restored. | |
""" | |
function withenv{T<:AbstractString}(f::Function, keyvals::Pair{T}...) | |
old = Dict{T,Any}() | |
for (key,val) in keyvals | |
old[key] = get(ENV,key,nothing) | |
val !== nothing ? (ENV[key]=val) : delete!(ENV, key) | |
end | |
try f() | |
finally | |
for (key,val) in old | |
val !== nothing ? (ENV[key]=val) : delete!(ENV, key) | |
end | |
end | |
end | |
withenv(f::Function) = f() # handle empty keyvals case; see #10853 | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
include("errno_h.jl") | |
export | |
E2BIG, | |
EACCES, | |
EADDRINUSE, | |
EADDRNOTAVAIL, | |
EADV, | |
EAFNOSUPPORT, | |
EAGAIN, | |
EALREADY, | |
EBADE, | |
EBADF, | |
EBADFD, | |
EBADMSG, | |
EBADR, | |
EBADRQC, | |
EBADSLT, | |
EBFONT, | |
EBUSY, | |
ECANCELED, | |
ECHILD, | |
ECHRNG, | |
ECOMM, | |
ECONNABORTED, | |
ECONNREFUSED, | |
ECONNRESET, | |
EDEADLK, | |
EDESTADDRREQ, | |
EDOM, | |
EDOTDOT, | |
EDQUOT, | |
EEXIST, | |
EFAULT, | |
EFBIG, | |
EHOSTDOWN, | |
EHOSTUNREACH, | |
EHWPOISON, | |
EIDRM, | |
EILSEQ, | |
EINPROGRESS, | |
EINTR, | |
EINVAL, | |
EIO, | |
EISCONN, | |
EISDIR, | |
EISNAM, | |
EKEYEXPIRED, | |
EKEYREJECTED, | |
EKEYREVOKED, | |
EL2HLT, | |
EL2NSYNC, | |
EL3HLT, | |
EL3RST, | |
ELIBACC, | |
ELIBBAD, | |
ELIBEXEC, | |
ELIBMAX, | |
ELIBSCN, | |
ELNRNG, | |
ELOOP, | |
EMEDIUMTYPE, | |
EMFILE, | |
EMLINK, | |
EMSGSIZE, | |
EMULTIHOP, | |
ENAMETOOLONG, | |
ENAVAIL, | |
ENETDOWN, | |
ENETRESET, | |
ENETUNREACH, | |
ENFILE, | |
ENOANO, | |
ENOBUFS, | |
ENOCSI, | |
ENODATA, | |
ENODEV, | |
ENOENT, | |
ENOEXEC, | |
ENOKEY, | |
ENOLCK, | |
ENOLINK, | |
ENOMEDIUM, | |
ENOMEM, | |
ENOMSG, | |
ENONET, | |
ENOPKG, | |
ENOPROTOOPT, | |
ENOSPC, | |
ENOSR, | |
ENOSTR, | |
ENOSYS, | |
ENOTBLK, | |
ENOTCONN, | |
ENOTDIR, | |
ENOTEMPTY, | |
ENOTNAM, | |
ENOTRECOVERABLE, | |
ENOTSOCK, | |
ENOTTY, | |
ENOTUNIQ, | |
ENXIO, | |
EOPNOTSUPP, | |
EOVERFLOW, | |
EOWNERDEAD, | |
EPERM, | |
EPFNOSUPPORT, | |
EPIPE, | |
EPROTO, | |
EPROTONOSUPPORT, | |
EPROTOTYPE, | |
ERANGE, | |
EREMCHG, | |
EREMOTE, | |
EREMOTEIO, | |
ERESTART, | |
ERFKILL, | |
EROFS, | |
ESHUTDOWN, | |
ESOCKTNOSUPPORT, | |
ESPIPE, | |
ESRCH, | |
ESRMNT, | |
ESTALE, | |
ESTRPIPE, | |
ETIME, | |
ETIMEDOUT, | |
ETOOMANYREFS, | |
ETXTBSY, | |
EUCLEAN, | |
EUNATCH, | |
EUSERS, | |
EXDEV, | |
EXFULL | |
const E2BIG = Int32(7) | |
const EACCES = Int32(13) | |
const EADDRINUSE = Int32(48) | |
const EADDRNOTAVAIL = Int32(49) | |
const EAFNOSUPPORT = Int32(47) | |
const EAGAIN = Int32(35) | |
const EALREADY = Int32(37) | |
const EAUTH = Int32(80) | |
const EBADARCH = Int32(86) | |
const EBADEXEC = Int32(85) | |
const EBADF = Int32(9) | |
const EBADMACHO = Int32(88) | |
const EBADMSG = Int32(94) | |
const EBADRPC = Int32(72) | |
const EBUSY = Int32(16) | |
const ECANCELED = Int32(89) | |
const ECHILD = Int32(10) | |
const ECONNABORTED = Int32(53) | |
const ECONNREFUSED = Int32(61) | |
const ECONNRESET = Int32(54) | |
const EDEADLK = Int32(11) | |
const EDESTADDRREQ = Int32(39) | |
const EDEVERR = Int32(83) | |
const EDOM = Int32(33) | |
const EDQUOT = Int32(69) | |
const EEXIST = Int32(17) | |
const EFAULT = Int32(14) | |
const EFBIG = Int32(27) | |
const EFTYPE = Int32(79) | |
const EHOSTDOWN = Int32(64) | |
const EHOSTUNREACH = Int32(65) | |
const EIDRM = Int32(90) | |
const EILSEQ = Int32(92) | |
const EINPROGRESS = Int32(36) | |
const EINTR = Int32(4) | |
const EINVAL = Int32(22) | |
const EIO = Int32(5) | |
const EISCONN = Int32(56) | |
const EISDIR = Int32(21) | |
const ELAST = Int32(106) | |
const ELOOP = Int32(62) | |
const EMFILE = Int32(24) | |
const EMLINK = Int32(31) | |
const EMSGSIZE = Int32(40) | |
const EMULTIHOP = Int32(95) | |
const ENAMETOOLONG = Int32(63) | |
const ENEEDAUTH = Int32(81) | |
const ENETDOWN = Int32(50) | |
const ENETRESET = Int32(52) | |
const ENETUNREACH = Int32(51) | |
const ENFILE = Int32(23) | |
const ENOATTR = Int32(93) | |
const ENOBUFS = Int32(55) | |
const ENODATA = Int32(96) | |
const ENODEV = Int32(19) | |
const ENOENT = Int32(2) | |
const ENOEXEC = Int32(8) | |
const ENOLCK = Int32(77) | |
const ENOLINK = Int32(97) | |
const ENOMEM = Int32(12) | |
const ENOMSG = Int32(91) | |
const ENOPOLICY = Int32(103) | |
const ENOPROTOOPT = Int32(42) | |
const ENOSPC = Int32(28) | |
const ENOSR = Int32(98) | |
const ENOSTR = Int32(99) | |
const ENOSYS = Int32(78) | |
const ENOTBLK = Int32(15) | |
const ENOTCONN = Int32(57) | |
const ENOTDIR = Int32(20) | |
const ENOTEMPTY = Int32(66) | |
const ENOTRECOVERABLE = Int32(104) | |
const ENOTSOCK = Int32(38) | |
const ENOTSUP = Int32(45) | |
const ENOTTY = Int32(25) | |
const ENXIO = Int32(6) | |
const EOPNOTSUPP = Int32(102) | |
const EOVERFLOW = Int32(84) | |
const EOWNERDEAD = Int32(105) | |
const EPERM = Int32(1) | |
const EPFNOSUPPORT = Int32(46) | |
const EPIPE = Int32(32) | |
const EPROCLIM = Int32(67) | |
const EPROCUNAVAIL = Int32(76) | |
const EPROGMISMATCH = Int32(75) | |
const EPROGUNAVAIL = Int32(74) | |
const EPROTO = Int32(100) | |
const EPROTONOSUPPORT = Int32(43) | |
const EPROTOTYPE = Int32(41) | |
const EPWROFF = Int32(82) | |
const EQFULL = Int32(106) | |
const ERANGE = Int32(34) | |
const EREMOTE = Int32(71) | |
const EROFS = Int32(30) | |
const ERPCMISMATCH = Int32(73) | |
const ESHLIBVERS = Int32(87) | |
const ESHUTDOWN = Int32(58) | |
const ESOCKTNOSUPPORT = Int32(44) | |
const ESPIPE = Int32(29) | |
const ESRCH = Int32(3) | |
const ESTALE = Int32(70) | |
const ETIME = Int32(101) | |
const ETIMEDOUT = Int32(60) | |
const ETOOMANYREFS = Int32(59) | |
const ETXTBSY = Int32(26) | |
const EUSERS = Int32(68) | |
const EXDEV = Int32(18) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# pseudo-definitions to show how everything behaves | |
# | |
# throw(label, val) = # throw a value to a dynamically enclosing block | |
# | |
# function rethrow(val) | |
# global current_exception = val | |
# throw(current_handler(), current_exception) | |
# end | |
# | |
# rethrow() = rethrow(current_exception) | |
# | |
# function throw(val) | |
# global catch_backtrace = backtrace() | |
# rethrow(val) | |
# end | |
## native julia error handling ## | |
error(s::AbstractString) = throw(ErrorException(s)) | |
error(s...) = throw(ErrorException(Main.Base.string(s...))) | |
rethrow() = ccall(:jl_rethrow, Bottom, ()) | |
rethrow(e) = ccall(:jl_rethrow_other, Bottom, (Any,), e) | |
backtrace() = ccall(:jl_backtrace_from_here, Array{Ptr{Void},1}, (Int32,), false) | |
catch_backtrace() = ccall(:jl_get_backtrace, Array{Ptr{Void},1}, ()) | |
## keyword arg lowering generates calls to this ## | |
kwerr(kw, args...) = throw(MethodError(typeof(args[1]).name.mt.kwsorter, (kw,args...))) | |
## system error handling ## | |
systemerror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(string(p), Libc.errno(), extrainfo)) : nothing | |
## assertion functions and macros ## | |
assert(x) = x ? nothing : throw(Main.Base.AssertionError()) | |
macro assert(ex, msgs...) | |
msg = isempty(msgs) ? ex : msgs[1] | |
if !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol)) | |
# message is an expression needing evaluating | |
msg = :(Main.Base.string($(esc(msg)))) | |
elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) | |
msg = Main.Base.string(msg) | |
else | |
# string() might not be defined during bootstrap | |
msg = :(Main.Base.string($(Expr(:quote,msg)))) | |
end | |
:($(esc(ex)) ? $(nothing) : throw(Main.Base.AssertionError($msg))) | |
end | |
# NOTE: Please keep the constant values specified below in sync with the doc string | |
const DEFAULT_RETRY_N = 1 | |
const DEFAULT_RETRY_ON = e->true | |
const DEFAULT_RETRY_MAX_DELAY = 10.0 | |
""" | |
retry(f, [retry_on]; n=1, max_delay=10.0) -> Function | |
Returns a lambda that retries function `f` up to `n` times in the | |
event of an exception. If `retry_on` is a `Type` then retry only | |
for exceptions of that type. If `retry_on` is a function | |
`test_error(::Exception) -> Bool` then retry only if it is true. | |
The first retry happens after a gap of 50 milliseconds or `max_delay`, | |
whichever is lower. Subsequently, the delays between retries are | |
exponentially increased with a random factor up to `max_delay`. | |
**Examples** | |
```julia | |
retry(http_get, e -> e.status == "503")(url) | |
retry(read, UVError)(io) | |
``` | |
""" | |
function retry(f::Function, retry_on::Function=DEFAULT_RETRY_ON; n=DEFAULT_RETRY_N, max_delay=DEFAULT_RETRY_MAX_DELAY) | |
(args...) -> begin | |
delay = min(0.05, max_delay) | |
for i = 1:n+1 | |
try | |
return f(args...) | |
catch e | |
if i > n || try retry_on(e) end !== true | |
rethrow(e) | |
end | |
end | |
delay = min(max_delay, delay) | |
sleep(delay * (0.8 + (rand() * 0.2))) | |
delay = delay * 5 | |
end | |
end | |
end | |
retry(f::Function, t::Type; kw...) = retry(f, e->isa(e, t); kw...) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
using Core: CodeInfo | |
typealias Callable Union{Function,DataType} | |
const Bottom = Union{} | |
abstract AbstractSet{T} | |
abstract Associative{K,V} | |
# The real @inline macro is not available until after array.jl, so this | |
# internal macro splices the meta Expr directly into the function body. | |
macro _inline_meta() | |
Expr(:meta, :inline) | |
end | |
macro _noinline_meta() | |
Expr(:meta, :noinline) | |
end | |
macro _pure_meta() | |
Expr(:meta, :pure) | |
end | |
# another version of inlining that propagates an inbounds context | |
macro _propagate_inbounds_meta() | |
Expr(:meta, :inline, :propagate_inbounds) | |
end | |
convert(::Type{Any}, x::ANY) = x | |
convert{T}(::Type{T}, x::T) = x | |
convert(::Type{Tuple{}}, ::Tuple{}) = () | |
convert(::Type{Tuple}, x::Tuple) = x | |
convert{T}(::Type{Tuple{Vararg{T}}}, x::Tuple) = cnvt_all(T, x...) | |
cnvt_all(T) = () | |
cnvt_all(T, x, rest...) = tuple(convert(T,x), cnvt_all(T, rest...)...) | |
macro generated(f) | |
isa(f, Expr) || error("invalid syntax; @generated must be used with a function definition") | |
if f.head === :function || (isdefined(:length) && f.head === :(=) && length(f.args) == 2 && f.args[1].head == :call) | |
f.head = :stagedfunction | |
return Expr(:escape, f) | |
else | |
error("invalid syntax; @generated must be used with a function definition") | |
end | |
end | |
argtail(x, rest...) = rest | |
tail(x::Tuple) = argtail(x...) | |
tuple_type_head(T::TypeConstructor) = tuple_type_head(T.body) | |
function tuple_type_head(T::DataType) | |
@_pure_meta | |
T.name === Tuple.name || throw(MethodError(tuple_type_head, (T,))) | |
return T.parameters[1] | |
end | |
tuple_type_tail(T::TypeConstructor) = tuple_type_tail(T.body) | |
function tuple_type_tail(T::DataType) | |
@_pure_meta | |
T.name === Tuple.name || throw(MethodError(tuple_type_tail, (T,))) | |
if isvatuple(T) && length(T.parameters) == 1 | |
return T | |
end | |
return Tuple{argtail(T.parameters...)...} | |
end | |
tuple_type_cons{S}(::Type{S}, ::Type{Union{}}) = Union{} | |
function tuple_type_cons{S,T<:Tuple}(::Type{S}, ::Type{T}) | |
@_pure_meta | |
Tuple{S, T.parameters...} | |
end | |
isvarargtype(t::ANY) = isa(t, DataType) && (t::DataType).name === Vararg.name | |
isvatuple(t::DataType) = (n = length(t.parameters); n > 0 && isvarargtype(t.parameters[n])) | |
unwrapva(t::ANY) = isvarargtype(t) ? t.parameters[1] : t | |
convert{T<:Tuple{Any,Vararg{Any}}}(::Type{T}, x::Tuple{Any, Vararg{Any}}) = | |
tuple(convert(tuple_type_head(T),x[1]), convert(tuple_type_tail(T), tail(x))...) | |
convert{T<:Tuple{Any,Vararg{Any}}}(::Type{T}, x::T) = x | |
oftype(x,c) = convert(typeof(x),c) | |
unsigned(x::Int) = reinterpret(UInt, x) | |
signed(x::UInt) = reinterpret(Int, x) | |
# conversions used by ccall | |
ptr_arg_cconvert{T}(::Type{Ptr{T}}, x) = cconvert(T, x) | |
ptr_arg_unsafe_convert{T}(::Type{Ptr{T}}, x) = unsafe_convert(T, x) | |
ptr_arg_unsafe_convert(::Type{Ptr{Void}}, x) = x | |
cconvert(T::Type, x) = convert(T, x) # do the conversion eagerly in most cases | |
cconvert{P<:Ptr}(::Type{P}, x) = x # but defer the conversion to Ptr to unsafe_convert | |
unsafe_convert{T}(::Type{T}, x::T) = x # unsafe_convert (like convert) defaults to assuming the convert occurred | |
unsafe_convert{P<:Ptr}(::Type{P}, x::Ptr) = convert(P, x) | |
reinterpret{T}(::Type{T}, x) = box(T, x) | |
reinterpret(::Type{Unsigned}, x::Float16) = reinterpret(UInt16,x) | |
reinterpret(::Type{Signed}, x::Float16) = reinterpret(Int16,x) | |
sizeof(x) = Core.sizeof(x) | |
function append_any(xs...) | |
# used by apply() and quote | |
# must be a separate function from append(), since apply() needs this | |
# exact function. | |
out = Array{Any}(4) | |
l = 4 | |
i = 1 | |
for x in xs | |
for y in x | |
if i > l | |
ccall(:jl_array_grow_end, Void, (Any, UInt), out, 16) | |
l += 16 | |
end | |
Core.arrayset(out, y, i) | |
i += 1 | |
end | |
end | |
ccall(:jl_array_del_end, Void, (Any, UInt), out, l-i+1) | |
out | |
end | |
# simple Array{Any} operations needed for bootstrap | |
setindex!(A::Array{Any}, x::ANY, i::Int) = Core.arrayset(A, x, i) | |
function length_checked_equal(args...) | |
n = length(args[1]) | |
for i=2:length(args) | |
if length(args[i]) != n | |
error("argument dimensions must match") | |
end | |
end | |
n | |
end | |
map(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i=1:length(a) ] | |
function precompile(f::ANY, args::Tuple) | |
ccall(:jl_compile_hint, Cint, (Any,), Tuple{Core.Typeof(f), args...}) != 0 | |
end | |
function precompile(argt::Type) | |
ccall(:jl_compile_hint, Cint, (Any,), argt) != 0 | |
end | |
""" | |
esc(e::ANY) | |
Only valid in the context of an `Expr` returned from a macro. Prevents the macro hygiene | |
pass from turning embedded variables into gensym variables. See the [macro](:ref:`man-macros`) | |
section of the Metaprogramming chapter of the manual for more details and examples. | |
""" | |
esc(e::ANY) = Expr(:escape, e) | |
macro boundscheck(blk) | |
# hack: use this syntax since it avoids introducing line numbers | |
:($(Expr(:boundscheck,true)); | |
$(esc(blk)); | |
$(Expr(:boundscheck,:pop))) | |
end | |
macro inbounds(blk) | |
:($(Expr(:inbounds,true)); | |
$(esc(blk)); | |
$(Expr(:inbounds,:pop))) | |
end | |
macro label(name::Symbol) | |
Expr(:symboliclabel, name) | |
end | |
macro goto(name::Symbol) | |
Expr(:symbolicgoto, name) | |
end | |
# SimpleVector | |
function getindex(v::SimpleVector, i::Int) | |
if !(1 <= i <= length(v)) | |
throw(BoundsError(v,i)) | |
end | |
x = unsafe_load(convert(Ptr{Ptr{Void}},data_pointer_from_objref(v)) + i*sizeof(Ptr)) | |
x == C_NULL && throw(UndefRefError()) | |
return unsafe_pointer_to_objref(x) | |
end | |
length(v::SimpleVector) = v.length | |
endof(v::SimpleVector) = v.length | |
start(v::SimpleVector) = 1 | |
next(v::SimpleVector,i) = (v[i],i+1) | |
done(v::SimpleVector,i) = (i > v.length) | |
isempty(v::SimpleVector) = (v.length == 0) | |
indices(v::SimpleVector) = (OneTo(length(v)),) | |
linearindices(v::SimpleVector) = indices(v, 1) | |
indices(v::SimpleVector, d) = d <= 1 ? indices(v)[d] : OneTo(1) | |
function ==(v1::SimpleVector, v2::SimpleVector) | |
length(v1)==length(v2) || return false | |
for i = 1:length(v1) | |
v1[i] == v2[i] || return false | |
end | |
return true | |
end | |
map(f, v::SimpleVector) = Any[ f(v[i]) for i = 1:length(v) ] | |
getindex(v::SimpleVector, I::AbstractArray) = Core.svec(Any[ v[i] for i in I ]...) | |
""" | |
isassigned(array, i) -> Bool | |
Tests whether the given array has a value associated with index `i`. Returns `false` | |
if the index is out of bounds, or has an undefined reference. | |
""" | |
function isassigned end | |
function isassigned(v::SimpleVector, i::Int) | |
1 <= i <= length(v) || return false | |
x = unsafe_load(convert(Ptr{Ptr{Void}},data_pointer_from_objref(v)) + i*sizeof(Ptr)) | |
return x != C_NULL | |
end | |
# index colon | |
immutable Colon | |
end | |
const (:) = Colon() | |
# For passing constants through type inference | |
immutable Val{T} | |
end | |
# used by interpolating quote and some other things in the front end | |
function vector_any(xs::ANY...) | |
n = length(xs) | |
a = Array{Any}(n) | |
@inbounds for i = 1:n | |
Core.arrayset(a,xs[i],i) | |
end | |
a | |
end | |
isempty(itr) = done(itr, start(itr)) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## condition variables | |
""" | |
Condition() | |
Create an edge-triggered event source that tasks can wait for. Tasks that call `wait` on a | |
`Condition` are suspended and queued. Tasks are woken up when `notify` is later called on | |
the `Condition`. Edge triggering means that only tasks waiting at the time `notify` is | |
called can be woken up. For level-triggered notifications, you must keep extra state to keep | |
track of whether a notification has happened. The [`Channel`](:class:`Channel`) type does | |
this, and so can be used for level-triggered events. | |
""" | |
type Condition | |
waitq::Vector{Any} | |
Condition() = new([]) | |
end | |
function wait(c::Condition) | |
ct = current_task() | |
push!(c.waitq, ct) | |
try | |
return wait() | |
catch | |
filter!(x->x!==ct, c.waitq) | |
rethrow() | |
end | |
end | |
""" | |
notify(condition, val=nothing; all=true, error=false) | |
Wake up tasks waiting for a condition, passing them `val`. If `all` is `true` (the default), | |
all waiting tasks are woken, otherwise only one is. If `error` is `true`, the passed value | |
is raised as an exception in the woken tasks. | |
""" | |
notify(c::Condition, arg::ANY=nothing; all=true, error=false) = notify(c, arg, all, error) | |
function notify(c::Condition, arg, all, error) | |
if all | |
for t in c.waitq | |
error ? schedule(t, arg, error=error) : schedule(t, arg) | |
end | |
empty!(c.waitq) | |
elseif !isempty(c.waitq) | |
t = shift!(c.waitq) | |
error ? schedule(t, arg, error=error) : schedule(t, arg) | |
end | |
nothing | |
end | |
notify_error(c::Condition, err) = notify(c, err, true, true) | |
n_waiters(c::Condition) = length(c.waitq) | |
# schedule an expression to run asynchronously, with minimal ceremony | |
""" | |
@schedule | |
Wrap an expression in a `Task` and add it to the local machine's scheduler queue. | |
""" | |
macro schedule(expr) | |
expr = :(()->($expr)) | |
:(enq_work(Task($(esc(expr))))) | |
end | |
## scheduler and work queue | |
global const Workqueue = Any[] | |
function enq_work(t::Task) | |
t.state == :runnable || error("schedule: Task not runnable") | |
ccall(:uv_stop, Void, (Ptr{Void},), eventloop()) | |
push!(Workqueue, t) | |
t.state = :queued | |
return t | |
end | |
schedule(t::Task) = enq_work(t) | |
""" | |
schedule(t::Task, [val]; error=false) | |
Add a task to the scheduler's queue. This causes the task to run constantly when the system | |
is otherwise idle, unless the task performs a blocking operation such as `wait`. | |
If a second argument `val` is provided, it will be passed to the task (via the return value of | |
`yieldto`) when it runs again. If `error` is `true`, the value is raised as an exception in | |
the woken task. | |
""" | |
function schedule(t::Task, arg; error=false) | |
# schedule a task to be (re)started with the given value or exception | |
if error | |
t.exception = arg | |
else | |
t.result = arg | |
end | |
return enq_work(t) | |
end | |
# fast version of schedule(t,v);wait() | |
function schedule_and_wait(t::Task, v=nothing) | |
t.state == :runnable || error("schedule: Task not runnable") | |
if isempty(Workqueue) | |
return yieldto(t, v) | |
else | |
t.result = v | |
push!(Workqueue, t) | |
t.state = :queued | |
end | |
return wait() | |
end | |
""" | |
yield() | |
Switch to the scheduler to allow another scheduled task to run. A task that calls this | |
function is still runnable, and will be restarted immediately if there are no other runnable | |
tasks. | |
""" | |
yield() = (enq_work(current_task()); wait()) | |
""" | |
yieldto(task, arg = nothing) | |
Switch to the given task. The first time a task is switched to, the task's function is | |
called with no arguments. On subsequent switches, `arg` is returned from the task's last | |
call to `yieldto`. This is a low-level call that only switches tasks, not considering states | |
or scheduling in any way. Its use is discouraged. | |
""" | |
yieldto(t::Task, x::ANY = nothing) = ccall(:jl_switchto, Any, (Any, Any), t, x) | |
# yield to a task, throwing an exception in it | |
function throwto(t::Task, exc) | |
t.exception = exc | |
yieldto(t) | |
end | |
function wait() | |
while true | |
if isempty(Workqueue) | |
c = process_events(true) | |
if c==0 && eventloop()!=C_NULL && isempty(Workqueue) | |
# if there are no active handles and no runnable tasks, just | |
# wait for signals. | |
pause() | |
end | |
else | |
t = shift!(Workqueue) | |
if t.state != :queued | |
# assume this somehow got queued twice, | |
# probably broken now, but try discarding this switch and keep going | |
# can't throw here, because it's probably not the fault of the caller to wait | |
# and don't want to use print() here, because that may try to incur a task switch | |
ccall(:jl_safe_printf, Void, (Ptr{UInt8}, Vararg{Int32}), | |
"\nWARNING: Workqueue inconsistency detected: shift!(Workqueue).state != :queued\n") | |
continue | |
end | |
arg = t.result | |
t.result = nothing | |
t.state = :runnable | |
local result | |
try | |
result = yieldto(t, arg) | |
current_task().state == :runnable || throw(AssertionError("current_task().state == :runnable")) | |
catch e | |
ct = current_task() | |
if ct.state == :queued | |
if t.state == :runnable | |
# assume we failed to queue t | |
# return it to the queue to be scheduled later | |
t.result = arg | |
t.state = :queued | |
push!(Workqueue, t) | |
end | |
# return ourself to the runnable state | |
i = findfirst(Workqueue, ct) | |
i == 0 || deleteat!(Workqueue, i) | |
ct.state = :runnable | |
end | |
rethrow(e) | |
end | |
process_events(false) | |
# return when we come out of the queue | |
return result | |
end | |
end | |
assert(false) | |
end | |
if is_windows() | |
pause() = ccall(:Sleep, stdcall, Void, (UInt32,), 0xffffffff) | |
else | |
pause() = ccall(:pause, Void, ()) | |
end | |
## async event notifications | |
""" | |
AsyncCondition() | |
Create a async condition that wakes up tasks waiting for it (by calling `wait` on the object) | |
when notified from C by a call to uv_async_send. | |
Waiting tasks are woken with an error when the object is closed (by `close`). | |
Use `isopen` to check whether it is still active. | |
""" | |
type AsyncCondition | |
handle::Ptr{Void} | |
cond::Condition | |
function AsyncCondition() | |
this = new(Libc.malloc(_sizeof_uv_async), Condition()) | |
associate_julia_struct(this.handle, this) | |
preserve_handle_new(this) | |
err = ccall(:uv_async_init, Cint, (Ptr{Void}, Ptr{Void}, Ptr{Void}), | |
eventloop(), this, uv_jl_asynccb::Ptr{Void}) | |
this | |
end | |
end | |
unsafe_convert(::Type{Ptr{Void}}, async::AsyncCondition) = async.handle | |
function wait(async::AsyncCondition) | |
isopen(async) || throw(EOFError()) | |
wait(async.cond) | |
end | |
isopen(t::AsyncCondition) = (t.handle != C_NULL) | |
close(t::AsyncCondition) = ccall(:jl_close_uv, Void, (Ptr{Void},), t) | |
function _uv_hook_close(async::AsyncCondition) | |
async.handle = C_NULL | |
unpreserve_handle(async) | |
notify_error(async.cond, EOFError()) | |
nothing | |
end | |
function uv_asynccb(handle::Ptr{Void}) | |
async = @handle_as handle AsyncCondition | |
notify(async.cond) | |
nothing | |
end | |
""" | |
AsyncCondition(callback::Function) | |
Create a async condition that calls the given `callback` function. The `callback` is passed one argument, | |
the async condition object itself. | |
""" | |
function AsyncCondition(cb::Function) | |
async = AsyncCondition() | |
waiter = Task(function() | |
while isopen(async) | |
success = try | |
wait(async) | |
true | |
catch # ignore possible exception on close() | |
false | |
end | |
success && cb(async) | |
end | |
end) | |
# must start the task right away so that it can wait for the AsyncCondition before | |
# we re-enter the event loop. this avoids a race condition. see issue #12719 | |
enq_work(current_task()) | |
yieldto(waiter) | |
return async | |
end | |
## timer-based notifications | |
""" | |
Timer(delay, repeat=0) | |
Create a timer that wakes up tasks waiting for it (by calling `wait` on the timer object) at | |
a specified interval. Times are in seconds. Waiting tasks are woken with an error when the | |
timer is closed (by `close`). Use `isopen` to check whether a timer is still active. | |
""" | |
type Timer | |
handle::Ptr{Void} | |
cond::Condition | |
isopen::Bool | |
function Timer(timeout::Real, repeat::Real=0.0) | |
timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds")) | |
repeat ≥ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $repeat seconds")) | |
this = new(Libc.malloc(_sizeof_uv_timer), Condition(), true) | |
err = ccall(:uv_timer_init, Cint, (Ptr{Void}, Ptr{Void}), eventloop(), this) | |
if err != 0 | |
#TODO: this codepath is currently not tested | |
Libc.free(this.handle) | |
this.handle = C_NULL | |
throw(UVError("uv_make_timer",err)) | |
end | |
associate_julia_struct(this.handle, this) | |
preserve_handle_new(this) | |
ccall(:uv_update_time, Void, (Ptr{Void},), eventloop()) | |
ccall(:uv_timer_start, Cint, (Ptr{Void}, Ptr{Void}, UInt64, UInt64), | |
this, uv_jl_timercb::Ptr{Void}, | |
UInt64(round(timeout * 1000)) + 1, UInt64(round(repeat * 1000))) | |
return this | |
end | |
end | |
unsafe_convert(::Type{Ptr{Void}}, t::Timer) = t.handle | |
function wait(t::Timer) | |
isopen(t) || throw(EOFError()) | |
wait(t.cond) | |
end | |
isopen(t::Timer) = t.isopen | |
function close(t::Timer) | |
if t.handle != C_NULL | |
t.isopen = false | |
ccall(:uv_timer_stop, Cint, (Ptr{Void},), t) | |
ccall(:jl_close_uv, Void, (Ptr{Void},), t) | |
end | |
nothing | |
end | |
function _uv_hook_close(t::Timer) | |
unpreserve_handle(t) | |
disassociate_julia_struct(t) | |
t.handle = C_NULL | |
t.isopen = false | |
notify_error(t.cond, EOFError()) | |
nothing | |
end | |
function uv_timercb(handle::Ptr{Void}) | |
t = @handle_as handle Timer | |
if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Void},), t) == 0 | |
# timer is stopped now | |
close(t) | |
end | |
notify(t.cond) | |
nothing | |
end | |
""" | |
sleep(seconds) | |
Block the current task for a specified number of seconds. The minimum sleep time is 1 | |
millisecond or input of `0.001`. | |
""" | |
function sleep(sec::Real) | |
sec ≥ 0 || throw(ArgumentError("cannot sleep for $sec seconds")) | |
wait(Timer(sec)) | |
nothing | |
end | |
# timer with repeated callback | |
""" | |
Timer(callback::Function, delay, repeat=0) | |
Create a timer to call the given `callback` function. The `callback` is passed one argument, | |
the timer object itself. The callback will be invoked after the specified initial `delay`, | |
and then repeating with the given `repeat` interval. If `repeat` is `0`, the timer is only | |
triggered once. Times are in seconds. A timer is stopped and has its resources freed by | |
calling `close` on it. | |
""" | |
function Timer(cb::Function, timeout::Real, repeat::Real=0.0) | |
t = Timer(timeout, repeat) | |
waiter = Task(function() | |
while isopen(t) | |
success = try | |
wait(t) | |
true | |
catch # ignore possible exception on close() | |
false | |
end | |
success && cb(t) | |
end | |
end) | |
# must start the task right away so that it can wait for the Timer before | |
# we re-enter the event loop. this avoids a race condition. see issue #12719 | |
enq_work(current_task()) | |
yieldto(waiter) | |
return t | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
export | |
# Modules | |
Collections, | |
FFTW, | |
Meta, | |
Operators, | |
Pkg, | |
LibGit2, | |
StackTraces, | |
Profile, | |
Dates, | |
Sys, | |
Test, | |
Libc, | |
Libdl, | |
Mmap, | |
LinAlg, | |
BLAS, | |
LAPACK, | |
Serializer, | |
Docs, | |
Markdown, | |
Threads, | |
Iterators, | |
# Types | |
AbstractChannel, | |
AbstractMatrix, | |
AbstractSet, | |
AbstractUnitRange, | |
AbstractVector, | |
AbstractVecOrMat, | |
Array, | |
Associative, | |
Bidiagonal, | |
BigFloat, | |
BigInt, | |
BitArray, | |
BitMatrix, | |
BitVector, | |
BufferStream, | |
CartesianIndex, | |
CartesianRange, | |
Channel, | |
Cmd, | |
Colon, | |
Complex, | |
Complex128, | |
Complex64, | |
Complex32, | |
DenseMatrix, | |
DenseVecOrMat, | |
DenseVector, | |
DevNull, | |
Diagonal, | |
Dict, | |
Dims, | |
EachLine, | |
Enum, | |
Enumerate, | |
Factorization, | |
FileMonitor, | |
FloatRange, | |
Future, | |
Hermitian, | |
UniformScaling, | |
InsertionSort, | |
IntSet, | |
IOBuffer, | |
IOStream, | |
LinSpace, | |
LowerTriangular, | |
Irrational, | |
Matrix, | |
MergeSort, | |
NTuple, | |
Nullable, | |
ObjectIdDict, | |
OrdinalRange, | |
Pair, | |
PartialQuickSort, | |
PollingFileWatcher, | |
QuickSort, | |
Range, | |
RangeIndex, | |
Rational, | |
Regex, | |
RegexMatch, | |
RemoteChannel, | |
RepString, | |
RevString, | |
RoundFromZero, | |
RoundDown, | |
RoundingMode, | |
RoundNearest, | |
RoundNearestTiesAway, | |
RoundNearestTiesUp, | |
RoundToZero, | |
RoundUp, | |
AbstractSerializer, | |
SerializationState, | |
Set, | |
SharedArray, | |
SharedMatrix, | |
SharedVector, | |
StepRange, | |
StridedArray, | |
StridedMatrix, | |
StridedVecOrMat, | |
StridedVector, | |
SubArray, | |
SubString, | |
Symmetric, | |
SymTridiagonal, | |
Timer, | |
Tridiagonal, | |
UnitRange, | |
UpperTriangular, | |
Val, | |
VecOrMat, | |
Vector, | |
VersionNumber, | |
WeakKeyDict, | |
WorkerConfig, | |
# Ccall types | |
Cchar, | |
Cdouble, | |
Cfloat, | |
Cint, | |
Cintmax_t, | |
Clong, | |
Clonglong, | |
Cptrdiff_t, | |
Cshort, | |
Csize_t, | |
Cssize_t, | |
Cuchar, | |
Cuint, | |
Cuintmax_t, | |
Culong, | |
Culonglong, | |
Cushort, | |
Cwchar_t, | |
Cstring, | |
Cwstring, | |
# Exceptions | |
ArgumentError, | |
DimensionMismatch, | |
CapturedException, | |
CompositeException, | |
EOFError, | |
ErrorException, | |
InvalidStateException, | |
KeyError, | |
LoadError, | |
InitError, | |
MethodError, | |
NullException, | |
ParseError, | |
ProcessExitedException, | |
RemoteException, | |
SystemError, | |
TypeError, | |
AssertionError, | |
UnicodeError, | |
# Global constants and variables | |
ARGS, | |
C_NULL, | |
ENDIAN_BOM, | |
ENV, | |
JULIA_HOME, | |
LOAD_PATH, | |
PROGRAM_FILE, | |
STDERR, | |
STDIN, | |
STDOUT, | |
VERSION, | |
# Mathematical constants | |
Inf, | |
Inf16, | |
Inf32, | |
Inf64, | |
NaN, | |
NaN16, | |
NaN32, | |
NaN64, | |
im, | |
π, pi, | |
e, eu, | |
γ, eulergamma, | |
catalan, | |
φ, golden, | |
I, | |
# Operators | |
!, | |
!=, | |
≠, | |
!==, | |
≡, | |
≢, | |
$, | |
%, | |
÷, | |
&, | |
*, | |
+, | |
-, | |
.!=, | |
.≠, | |
.+, | |
.-, | |
.*, | |
./, | |
.÷, | |
.%, | |
.<, | |
.<=, | |
.≤, | |
.==, | |
.>, | |
.>=, | |
.≥, | |
.\, | |
.^, | |
/, | |
//, | |
.//, | |
<, | |
<:, | |
<<, | |
<=, | |
≤, | |
==, | |
>, | |
>=, | |
≥, | |
>>, | |
.>>, | |
.<<, | |
>>>, | |
\, | |
^, | |
|, | |
|>, | |
~, | |
:, | |
=>, | |
A_ldiv_B!, | |
A_ldiv_Bc, | |
A_ldiv_Bt, | |
A_mul_B!, | |
A_mul_Bc, | |
A_mul_Bc!, | |
A_mul_Bt, | |
A_mul_Bt!, | |
A_rdiv_Bc, | |
A_rdiv_Bt, | |
Ac_ldiv_B, | |
Ac_ldiv_B!, | |
Ac_ldiv_Bc, | |
Ac_mul_B, | |
Ac_mul_B!, | |
Ac_mul_Bc, | |
Ac_mul_Bc!, | |
Ac_rdiv_B, | |
Ac_rdiv_Bc, | |
At_ldiv_B, | |
At_ldiv_B!, | |
At_ldiv_Bt, | |
At_mul_B, | |
At_mul_B!, | |
At_mul_Bt, | |
At_mul_Bt!, | |
At_rdiv_B, | |
At_rdiv_Bt, | |
# scalar math | |
@evalpoly, | |
abs, | |
abs2, | |
acos, | |
acosd, | |
acosh, | |
acot, | |
acotd, | |
acoth, | |
acsc, | |
acscd, | |
acsch, | |
angle, | |
asec, | |
asecd, | |
asech, | |
asin, | |
asind, | |
asinh, | |
atan, | |
atan2, | |
atand, | |
atanh, | |
big, | |
binomial, | |
bswap, | |
cbrt, | |
ceil, | |
cis, | |
clamp, | |
cld, | |
cmp, | |
complex, | |
conj, | |
copysign, | |
cos, | |
cosc, | |
cosd, | |
cosh, | |
cospi, | |
cot, | |
cotd, | |
coth, | |
count_ones, | |
count_zeros, | |
csc, | |
cscd, | |
csch, | |
dawson, | |
deg2rad, | |
den, | |
digamma, | |
div, | |
divrem, | |
eps, | |
erf, | |
erfc, | |
erfcinv, | |
erfcx, | |
erfi, | |
erfinv, | |
exp, | |
exp10, | |
exp2, | |
expm1, | |
exponent, | |
factorial, | |
fld, | |
fld1, | |
fldmod, | |
fldmod1, | |
flipsign, | |
float, | |
tryparse, | |
floor, | |
fma, | |
frexp, | |
gamma, | |
gcd, | |
gcdx, | |
hex2num, | |
hypot, | |
imag, | |
inv, | |
invdigamma, | |
invmod, | |
isapprox, | |
iseven, | |
isfinite, | |
isinf, | |
isinteger, | |
isnan, | |
isodd, | |
ispow2, | |
isqrt, | |
isreal, | |
isimag, | |
issubnormal, | |
lcm, | |
ldexp, | |
leading_ones, | |
leading_zeros, | |
lfact, | |
lgamma, | |
log, | |
log10, | |
log1p, | |
log2, | |
maxintfloat, | |
mod, | |
mod1, | |
modf, | |
mod2pi, | |
muladd, | |
nextfloat, | |
nextpow, | |
nextpow2, | |
nextprod, | |
num, | |
num2hex, | |
one, | |
powermod, | |
prevfloat, | |
prevpow, | |
prevpow2, | |
rad2deg, | |
rationalize, | |
real, | |
realmax, | |
realmin, | |
reim, | |
reinterpret, | |
rem, | |
round, | |
sec, | |
secd, | |
sech, | |
sign, | |
signbit, | |
signed, | |
signif, | |
significand, | |
sin, | |
sinc, | |
sind, | |
sinh, | |
sinpi, | |
sqrt, | |
tan, | |
tand, | |
tanh, | |
trailing_ones, | |
trailing_zeros, | |
trigamma, | |
trunc, | |
unsafe_trunc, | |
typemax, | |
typemin, | |
unsigned, | |
widemul, | |
zero, | |
√, | |
∛, | |
≈, | |
≉, | |
# specfun | |
airy, | |
airyai, | |
airyaiprime, | |
airybi, | |
airybiprime, | |
airyprime, | |
airyx, | |
besselh, | |
besselhx, | |
besseli, | |
besselix, | |
besselj, | |
besselj0, | |
besselj1, | |
besseljx, | |
besselk, | |
besselkx, | |
bessely, | |
bessely0, | |
bessely1, | |
besselyx, | |
beta, | |
eta, | |
hankelh1, | |
hankelh1x, | |
hankelh2, | |
hankelh2x, | |
lbeta, | |
polygamma, | |
zeta, | |
# arrays | |
bitbroadcast, | |
broadcast!, | |
broadcast, | |
broadcast_getindex, | |
broadcast_setindex!, | |
cat, | |
checkbounds, | |
checkindex, | |
circcopy!, | |
circshift, | |
circshift!, | |
clamp!, | |
colon, | |
conj!, | |
copy!, | |
cummax, | |
cummin, | |
cumprod, | |
cumprod!, | |
cumsum, | |
cumsum!, | |
cumsum_kbn, | |
eachindex, | |
extrema, | |
fill!, | |
fill, | |
find, | |
findfirst, | |
findlast, | |
findin, | |
findmax, | |
findmin, | |
findmin!, | |
findmax!, | |
findn, | |
findnext, | |
findprev, | |
findnz, | |
first, | |
flipdim, | |
gradient, | |
hcat, | |
hvcat, | |
ind2sub, | |
indexin, | |
indices, | |
indmax, | |
indmin, | |
invperm, | |
ipermute!, | |
isassigned, | |
isperm, | |
issorted, | |
last, | |
linearindices, | |
linspace, | |
logspace, | |
mapslices, | |
max, | |
maxabs, | |
maxabs!, | |
maximum!, | |
maximum, | |
min, | |
minabs, | |
minabs!, | |
minimum!, | |
minimum, | |
minmax, | |
ndims, | |
nonzeros, | |
countnz, | |
ones, | |
parent, | |
parentindexes, | |
permute, | |
permute!, | |
permutedims, | |
permutedims!, | |
prod!, | |
prod, | |
promote_shape, | |
randcycle, | |
randperm, | |
randsubseq!, | |
randsubseq, | |
range, | |
reducedim, | |
repmat, | |
reshape, | |
reverse!, | |
reverse, | |
rot180, | |
rotl90, | |
rotr90, | |
searchsorted, | |
searchsortedfirst, | |
searchsortedlast, | |
select!, | |
select, | |
shuffle, | |
shuffle!, | |
size, | |
slicedim, | |
sort!, | |
sort, | |
sortcols, | |
selectperm, | |
selectperm!, | |
sortperm, | |
sortperm!, | |
sortrows, | |
squeeze, | |
step, | |
stride, | |
strides, | |
sub2ind, | |
sum!, | |
sum, | |
sumabs!, | |
sumabs, | |
sumabs2!, | |
sumabs2, | |
sum_kbn, | |
vcat, | |
vec, | |
view, | |
zeros, | |
# linear algebra | |
bkfact!, | |
bkfact, | |
blkdiag, | |
chol, | |
cholfact!, | |
cholfact, | |
cond, | |
condskeel, | |
cross, | |
ctranspose!, | |
ctranspose, | |
det, | |
diag, | |
diagind, | |
diagm, | |
diff, | |
dot, | |
eig, | |
eigfact!, | |
eigfact, | |
eigmax, | |
eigmin, | |
eigs, | |
eigvals, | |
eigvals!, | |
eigvecs, | |
expm, | |
eye, | |
factorize, | |
givens, | |
hessfact!, | |
hessfact, | |
isdiag, | |
ishermitian, | |
isposdef!, | |
isposdef, | |
issymmetric, | |
istril, | |
istriu, | |
kron, | |
ldltfact, | |
ldltfact!, | |
linreg, | |
logabsdet, | |
logdet, | |
logm, | |
lu, | |
lufact!, | |
lufact, | |
lyap, | |
norm, | |
normalize, | |
normalize!, | |
nullspace, | |
ordschur!, | |
ordschur, | |
peakflops, | |
pinv, | |
qr, | |
qrfact!, | |
qrfact, | |
lq, | |
lqfact!, | |
lqfact, | |
rank, | |
scale!, | |
scale, | |
schur, | |
schurfact!, | |
schurfact, | |
sqrtm, | |
svd, | |
svdfact!, | |
svdfact, | |
svds, | |
svdvals!, | |
svdvals, | |
sylvester, | |
trace, | |
transpose!, | |
transpose, | |
tril!, | |
tril, | |
triu!, | |
triu, | |
vecdot, | |
vecnorm, | |
⋅, | |
×, | |
# sparse | |
full, | |
dropzeros, | |
dropzeros!, | |
# bitarrays | |
falses, | |
flipbits!, | |
rol, | |
rol!, | |
ror, | |
ror!, | |
trues, | |
# dequeues | |
append!, | |
insert!, | |
pop!, | |
prepend!, | |
push!, | |
resize!, | |
shift!, | |
unshift!, | |
# collections | |
all!, | |
all, | |
allunique, | |
any!, | |
any, | |
collect, | |
contains, | |
count, | |
delete!, | |
deleteat!, | |
eltype, | |
empty!, | |
endof, | |
filter!, | |
filter, | |
foldl, | |
foldr, | |
foreach, | |
get, | |
get!, | |
getindex, | |
getkey, | |
haskey, | |
in, | |
intersect!, | |
intersect, | |
isempty, | |
issubset, | |
keys, | |
keytype, | |
length, | |
map!, | |
map, | |
mapfoldl, | |
mapfoldr, | |
mapreduce, | |
mapreducedim, | |
merge!, | |
merge, | |
#pop!, | |
#push!, | |
reduce, | |
setdiff!, | |
setdiff, | |
setindex!, | |
similar, | |
sizehint!, | |
splice!, | |
symdiff!, | |
symdiff, | |
union!, | |
union, | |
unique, | |
values, | |
valtype, | |
∈, | |
∉, | |
∋, | |
∌, | |
⊆, | |
⊈, | |
⊊, | |
∩, | |
∪, | |
# strings and text output | |
ascii, | |
base, | |
base64encode, | |
base64decode, | |
Base64EncodePipe, | |
Base64DecodePipe, | |
startswith, | |
bin, | |
bits, | |
bytes2hex, | |
charwidth, | |
chomp, | |
chop, | |
chr2ind, | |
dec, | |
digits, | |
digits!, | |
dump, | |
eachmatch, | |
endswith, | |
escape_string, | |
graphemes, | |
hex, | |
hex2bytes, | |
ind2chr, | |
info, | |
is_assigned_char, | |
isalnum, | |
isalpha, | |
isascii, | |
iscntrl, | |
isdigit, | |
isgraph, | |
islower, | |
ismatch, | |
isnumber, | |
isprint, | |
ispunct, | |
isspace, | |
isupper, | |
isvalid, | |
isxdigit, | |
join, | |
lcfirst, | |
lowercase, | |
lpad, | |
lstrip, | |
match, | |
matchall, | |
ndigits, | |
nextind, | |
normalize_string, | |
oct, | |
prevind, | |
print, | |
print_shortest, | |
print_with_color, | |
println, | |
randstring, | |
repeat, | |
replace, | |
repr, | |
reverseind, | |
rpad, | |
rsearch, | |
rsearchindex, | |
rsplit, | |
rstrip, | |
search, | |
searchindex, | |
show, | |
showall, | |
showcompact, | |
showerror, | |
split, | |
sprint, | |
string, | |
strip, | |
strwidth, | |
summary, | |
transcode, | |
ucfirst, | |
unescape_string, | |
uppercase, | |
warn, | |
# random numbers | |
AbstractRNG, | |
MersenneTwister, | |
RandomDevice, | |
rand!, | |
rand, | |
randn!, | |
randn, | |
randexp!, | |
randexp, | |
srand, | |
bitrand, | |
randjump, | |
# bigfloat & precision | |
precision, | |
rounding, | |
setprecision, | |
setrounding, | |
get_zero_subnormals, | |
set_zero_subnormals, | |
# statistics | |
cor, | |
cov, | |
mean!, | |
mean, | |
median!, | |
median, | |
middle, | |
midpoints, | |
quantile!, | |
quantile, | |
std, | |
stdm, | |
var, | |
varm, | |
# signal processing | |
bfft!, | |
bfft, | |
brfft, | |
conv, | |
conv2, | |
dct!, | |
dct, | |
deconv, | |
fft!, | |
fft, | |
fftshift, | |
filt, | |
filt!, | |
idct!, | |
idct, | |
ifft!, | |
ifft, | |
ifftshift, | |
irfft, | |
plan_bfft!, | |
plan_bfft, | |
plan_brfft, | |
plan_dct!, | |
plan_dct, | |
plan_fft!, | |
plan_fft, | |
plan_idct!, | |
plan_idct, | |
plan_ifft!, | |
plan_ifft, | |
plan_irfft, | |
plan_rfft, | |
rfft, | |
xcorr, | |
# numerical integration | |
quadgk, | |
# iteration | |
done, | |
next, | |
start, | |
enumerate, # re-exported from Iterators | |
zip, | |
# object identity and equality | |
copy, | |
deepcopy, | |
hash, | |
identity, | |
isbits, | |
isequal, | |
isimmutable, | |
isless, | |
ifelse, | |
lexless, | |
lexcmp, | |
object_id, | |
sizeof, | |
# tasks and conditions | |
Condition, | |
consume, | |
current_task, | |
islocked, | |
istaskdone, | |
istaskstarted, | |
lock, | |
notify, | |
produce, | |
ReentrantLock, | |
schedule, | |
task_local_storage, | |
trylock, | |
unlock, | |
yield, | |
yieldto, | |
# time | |
sleep, | |
tic, | |
time, | |
time_ns, | |
toc, | |
toq, | |
# dates | |
Date, | |
DateTime, | |
now, | |
# errors | |
assert, | |
backtrace, | |
catch_backtrace, | |
error, | |
rethrow, | |
retry, | |
systemerror, | |
# stack traces | |
StackTrace, | |
StackFrame, | |
stacktrace, | |
catch_stacktrace, | |
# types | |
convert, | |
fieldoffset, | |
fieldname, | |
fieldnames, | |
isleaftype, | |
oftype, | |
promote, | |
promote_rule, | |
promote_type, | |
subtypes, | |
instances, | |
supertype, | |
typeintersect, | |
typejoin, | |
widen, | |
# syntax | |
esc, | |
expand, | |
gensym, | |
macroexpand, | |
@macroexpand, | |
parse, | |
# help and reflection | |
apropos, | |
current_module, | |
edit, | |
code_typed, | |
code_warntype, | |
code_lowered, | |
code_llvm, | |
code_native, | |
fullname, | |
functionloc, | |
isconst, | |
isinteractive, | |
less, | |
method_exists, | |
methods, | |
methodswith, | |
module_name, | |
module_parent, | |
names, | |
versioninfo, | |
which, | |
whos, | |
workspace, | |
# loading source files | |
__precompile__, | |
evalfile, | |
include, | |
include_string, | |
include_dependency, | |
reload, | |
# RTS internals | |
finalizer, | |
finalize, | |
gc, | |
gc_enable, | |
precompile, | |
# misc | |
atexit, | |
atreplinit, | |
clipboard, | |
exit, | |
ntuple, | |
quit, | |
# IP address stuff | |
@ip_str, | |
IPAddr, | |
IPv4, | |
IPv6, | |
# I/O and events | |
accept, | |
bind, | |
close, | |
connect, | |
countlines, | |
deserialize, | |
eachline, | |
eof, | |
fd, | |
fdio, | |
flush, | |
getaddrinfo, | |
gethostname, | |
getipaddr, | |
getsockname, | |
htol, | |
hton, | |
IOContext, | |
displaysize, | |
ismarked, | |
isopen, | |
isreadonly, | |
listen, | |
listenany, | |
ltoh, | |
mark, | |
nb_available, | |
ntoh, | |
open, | |
pipeline, | |
Pipe, | |
PipeBuffer, | |
poll_fd, | |
poll_file, | |
position, | |
RawFD, | |
read, | |
read!, | |
readavailable, | |
readbytes!, | |
readchomp, | |
readcsv, | |
readdir, | |
readdlm, | |
readline, | |
readlines, | |
readstring, | |
readuntil, | |
redirect_stderr, | |
redirect_stdin, | |
redirect_stdout, | |
recv, | |
recvfrom, | |
reset, | |
seek, | |
seekend, | |
seekstart, | |
send, | |
serialize, | |
skip, | |
skipchars, | |
takebuf_array, | |
takebuf_string, | |
truncate, | |
unmark, | |
watch_file, | |
write, | |
writecsv, | |
writedlm, | |
TCPSocket, | |
UDPSocket, | |
# multiprocessing | |
addprocs, | |
asyncmap, | |
CachingPool, | |
clear!, | |
ClusterManager, | |
default_worker_pool, | |
fetch, | |
init_worker, | |
interrupt, | |
isready, | |
launch, | |
manage, | |
myid, | |
nprocs, | |
nworkers, | |
pmap, | |
procs, | |
put!, | |
remote, | |
remotecall, | |
remotecall_fetch, | |
remotecall_wait, | |
remote_do, | |
rmprocs, | |
take!, | |
timedwait, | |
wait, | |
workers, | |
WorkerPool, | |
# multimedia I/O | |
Display, | |
display, | |
displayable, | |
TextDisplay, | |
istextmime, | |
MIME, | |
@MIME_str, | |
reprmime, | |
stringmime, | |
mimewritable, | |
popdisplay, | |
pushdisplay, | |
redisplay, | |
HTML, | |
Text, | |
# shared arrays | |
sdata, | |
indexpids, | |
localindexes, | |
# paths and file names | |
abspath, | |
basename, | |
dirname, | |
expanduser, | |
homedir, | |
isabspath, | |
isdirpath, | |
joinpath, | |
normpath, | |
realpath, | |
relpath, | |
splitdir, | |
splitdrive, | |
splitext, | |
# filesystem operations | |
cd, | |
chmod, | |
chown, | |
cp, | |
ctime, | |
download, | |
filemode, | |
filesize, | |
gperm, | |
isblockdev, | |
ischardev, | |
isdir, | |
isfifo, | |
isfile, | |
islink, | |
ismount, | |
ispath, | |
isreadable, | |
issetgid, | |
issetuid, | |
issocket, | |
issticky, | |
iswritable, | |
lstat, | |
mkdir, | |
mkpath, | |
mktemp, | |
mktempdir, | |
mtime, | |
mv, | |
operm, | |
pwd, | |
readlink, | |
rm, | |
stat, | |
symlink, | |
tempdir, | |
tempname, | |
touch, | |
uperm, | |
walkdir, | |
# external processes ## TODO: whittle down these exports. | |
detach, | |
getpid, | |
ignorestatus, | |
kill, | |
process_exited, | |
process_running, | |
readandwrite, | |
run, | |
setenv, | |
spawn, | |
success, | |
withenv, | |
# C interface | |
cfunction, | |
cglobal, | |
disable_sigint, | |
pointer, | |
pointer_from_objref, | |
unsafe_wrap, | |
unsafe_string, | |
reenable_sigint, | |
unsafe_copy!, | |
unsafe_load, | |
unsafe_pointer_to_objref, | |
unsafe_read, | |
unsafe_store!, | |
unsafe_write, | |
# nullable types | |
isnull, | |
unsafe_get, | |
# Macros | |
# parser internal | |
@__FILE__, | |
@__DIR__, | |
@int128_str, | |
@uint128_str, | |
@big_str, | |
@cmd, # `commands` | |
# notation for certain types | |
@b_str, # byte vector | |
@r_str, # regex | |
@s_str, # regex substitution string | |
@v_str, # version number | |
# documentation | |
@text_str, | |
@html_str, | |
@doc, | |
@doc_str, | |
# output | |
@show, | |
@printf, | |
@sprintf, | |
# profiling | |
@time, | |
@timed, | |
@timev, | |
@elapsed, | |
@allocated, | |
@profile, | |
# reflection | |
@which, | |
@edit, | |
@functionloc, | |
@less, | |
@code_typed, | |
@code_warntype, | |
@code_lowered, | |
@code_llvm, | |
@code_native, | |
# platform-conditional code | |
@static, | |
is_windows, | |
is_linux, | |
is_apple, | |
is_bsd, | |
is_unix, | |
# tasks | |
@schedule, | |
@sync, | |
@async, | |
@task, | |
@threadcall, | |
# multiprocessing | |
@spawn, | |
@spawnat, | |
@fetch, | |
@fetchfrom, | |
@everywhere, | |
@parallel, | |
# metaprogramming utilities | |
@generated, | |
@gensym, | |
@eval, | |
@deprecate, | |
# performance annotations | |
@boundscheck, | |
@inbounds, | |
@fastmath, | |
@simd, | |
@inline, | |
@noinline, | |
@polly, | |
@assert, | |
@enum, | |
@label, | |
@goto, | |
@view, | |
# SparseArrays module re-exports | |
SparseArrays, | |
AbstractSparseArray, | |
AbstractSparseMatrix, | |
AbstractSparseVector, | |
SparseMatrixCSC, | |
SparseVector, | |
issparse, | |
sparse, | |
sparsevec, | |
spdiagm, | |
speye, | |
spones, | |
sprand, | |
sprandn, | |
spzeros, | |
symperm, | |
rowvals, | |
nzrange, | |
nnz | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
## symbols ## | |
""" | |
gensym([tag]) | |
Generates a symbol which will not conflict with other variable names. | |
""" | |
gensym() = ccall(:jl_gensym, Ref{Symbol}, ()) | |
gensym(s::String) = gensym(s.data) | |
gensym(a::Array{UInt8,1}) = | |
ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Int32), a, length(a)) | |
gensym(ss::String...) = map(gensym, ss) | |
gensym(s::Symbol) = | |
ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Int32), s, ccall(:strlen, Csize_t, (Ptr{UInt8},), s)) | |
""" | |
@gensym | |
Generates a gensym symbol for a variable. For example, `@gensym x y` is transformed into | |
`x = gensym("x"); y = gensym("y")`. | |
""" | |
macro gensym(names...) | |
blk = Expr(:block) | |
for name in names | |
push!(blk.args, :($(esc(name)) = gensym($(string(name))))) | |
end | |
push!(blk.args, :nothing) | |
return blk | |
end | |
## expressions ## | |
copy(e::Expr) = (n = Expr(e.head); | |
n.args = copy_exprargs(e.args); | |
n.typ = e.typ; | |
n) | |
# copy parts of an AST that the compiler mutates | |
copy_exprs(x::Expr) = copy(x) | |
copy_exprs(x::ANY) = x | |
copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(a) for a in x] | |
==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args) | |
==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value) | |
""" | |
expand(x) | |
Takes the expression `x` and returns an equivalent expression in lowered form. | |
See also [`code_lowered`](:func:`code_lowered`). | |
""" | |
expand(x::ANY) = ccall(:jl_expand, Any, (Any,), x) | |
""" | |
macroexpand(x) | |
Takes the expression `x` and returns an equivalent expression with all macros removed (expanded). | |
""" | |
macroexpand(x::ANY) = ccall(:jl_macroexpand, Any, (Any,), x) | |
""" | |
@macroexpand | |
Return equivalent expression with all macros removed (expanded). | |
There is a subtle difference between `@macroexpand` and `macroexpand` in that expansion takes place in | |
different contexts. This is best seen in the following example: | |
```jldoctest | |
julia> module M | |
macro m() | |
1 | |
end | |
function f() | |
(@macroexpand(@m), macroexpand(:(@m))) | |
end | |
end | |
M | |
julia> macro m() | |
2 | |
end | |
@m (macro with 1 method) | |
julia> M.f() | |
(1,2) | |
``` | |
With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module | |
`M` in the example). With `macroexpand` the expression expands in the current module where | |
the code was finally called (REPL in the example). | |
Note that when calling `macroexpand` or `@macroexpand` directly from the REPL, both of these contexts coincide, hence there is no difference. | |
""" | |
macro macroexpand(code) | |
code_expanded = macroexpand(code) | |
QuoteNode(code_expanded) | |
end | |
## misc syntax ## | |
""" | |
eval([m::Module], expr::Expr) | |
Evaluate an expression in the given module and return the result. Every `Module` (except | |
those defined with `baremodule`) has its own 1-argument definition of `eval`, which | |
evaluates expressions in that module. | |
""" | |
Core.eval | |
""" | |
@eval | |
Evaluate an expression and return the value. | |
""" | |
macro eval(x) | |
:($(esc(:eval))($(Expr(:quote,x)))) | |
end | |
macro inline(ex) | |
esc(isa(ex, Expr) ? pushmeta!(ex, :inline) : ex) | |
end | |
macro noinline(ex) | |
esc(isa(ex, Expr) ? pushmeta!(ex, :noinline) : ex) | |
end | |
macro pure(ex) | |
esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex) | |
end | |
""" | |
@propagate_inbounds | |
Tells the compiler to inline a function while retaining the caller's inbounds context. | |
""" | |
macro propagate_inbounds(ex) | |
if isa(ex, Expr) | |
pushmeta!(ex, :inline) | |
pushmeta!(ex, :propagate_inbounds) | |
esc(ex) | |
else | |
esc(ex) | |
end | |
end | |
""" | |
@polly | |
Tells the compiler to apply the polyhedral optimizer Polly to a function. | |
""" | |
macro polly(ex) | |
esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex) | |
end | |
## some macro utilities ## | |
find_vars(e) = find_vars(e, []) | |
function find_vars(e, lst) | |
if isa(e,Symbol) | |
if current_module()===Main && isdefined(e) | |
# Main runs on process 1, so send globals from there, excluding | |
# things defined in Base. | |
if !isdefined(Base,e) || eval(Base,e)!==eval(current_module(),e) | |
push!(lst, e) | |
end | |
end | |
elseif isa(e,Expr) && e.head !== :quote && e.head !== :top && e.head !== :core | |
for x in e.args | |
find_vars(x,lst) | |
end | |
end | |
lst | |
end | |
# wrap an expression in "let a=a,b=b,..." for each var it references | |
localize_vars(expr) = localize_vars(expr, true) | |
function localize_vars(expr, esca) | |
v = find_vars(expr) | |
# requires a special feature of the front end that knows how to insert | |
# the correct variables. the list of free variables cannot be computed | |
# from a macro. | |
if esca | |
v = map(esc,v) | |
end | |
Expr(:localize, expr, v...) | |
end | |
function pushmeta!(ex::Expr, sym::Symbol, args::Any...) | |
if isempty(args) | |
tag = sym | |
else | |
tag = Expr(sym, args...) | |
end | |
inner = ex | |
while inner.head == :macrocall | |
inner = inner.args[end]::Expr | |
end | |
idx, exargs = findmeta(inner) | |
if idx != 0 | |
push!(exargs[idx].args, tag) | |
else | |
body::Expr = inner.args[2] | |
unshift!(body.args, Expr(:meta, tag)) | |
end | |
ex | |
end | |
function popmeta!(body::Expr, sym::Symbol) | |
body.head == :block || return false, [] | |
popmeta!(body.args, sym) | |
end | |
popmeta!(arg, sym) = (false, []) | |
function popmeta!(body::Array{Any,1}, sym::Symbol) | |
idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0) | |
if idx == 0 | |
return false, [] | |
end | |
metaargs = blockargs[idx].args | |
i = findmetaarg(blockargs[idx].args, sym) | |
if i == 0 | |
return false, [] | |
end | |
ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : [] | |
deleteat!(metaargs, i) | |
isempty(metaargs) && deleteat!(blockargs, idx) | |
true, ret | |
end | |
# Find index of `sym` in a meta expression argument list, or 0. | |
function findmetaarg(metaargs, sym) | |
for i = 1:length(metaargs) | |
arg = metaargs[i] | |
if (isa(arg, Symbol) && (arg::Symbol) == sym) || | |
(isa(arg, Expr) && (arg::Expr).head == sym) | |
return i | |
end | |
end | |
return 0 | |
end | |
function findmeta(ex::Expr) | |
if ex.head == :function || (ex.head == :(=) && typeof(ex.args[1]) == Expr && ex.args[1].head == :call) | |
body::Expr = ex.args[2] | |
body.head == :block || error(body, " is not a block expression") | |
return findmeta_block(ex.args) | |
end | |
error(ex, " is not a function expression") | |
end | |
findmeta(ex::Array{Any,1}) = findmeta_block(ex) | |
function findmeta_block(exargs, argsmatch=args->true) | |
for i = 1:length(exargs) | |
a = exargs[i] | |
if isa(a, Expr) | |
if (a::Expr).head == :meta && argsmatch((a::Expr).args) | |
return i, exargs | |
elseif (a::Expr).head == :block | |
idx, exa = findmeta_block(a.args, argsmatch) | |
if idx != 0 | |
return idx, exa | |
end | |
end | |
end | |
end | |
return 0, [] | |
end | |
remove_linenums!(ex) = ex | |
function remove_linenums!(ex::Expr) | |
filter!(x->!((isa(x,Expr) && x.head === :line) || isa(x,LineNumberNode)), ex.args) | |
for subex in ex.args | |
remove_linenums!(subex) | |
end | |
ex | |
end | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Support for @fastmath | |
# This module provides versions of math functions that may violate | |
# strict IEEE semantics. | |
# This allows the following transformations: | |
# nnan: No NaNs - Allow optimizations to assume the arguments and | |
# result are not NaN. Such optimizations are required to retain | |
# defined behavior over NaNs, but the value of the result is | |
# undefined. | |
# ninf: No Infs - Allow optimizations to assume the arguments and | |
# result are not +/-Inf. Such optimizations are required to | |
# retain defined behavior over +/-Inf, but the value of the | |
# result is undefined. | |
# nsz: No Signed Zeros - Allow optimizations to treat the sign of a | |
# zero argument or result as insignificant. | |
# arcp: Allow Reciprocal - Allow optimizations to use the reciprocal | |
# of an argument rather than perform division. | |
module FastMath | |
export @fastmath | |
import Core.Intrinsics: box, unbox, powi_llvm, sqrt_llvm_fast | |
const fast_op = | |
Dict(# basic arithmetic | |
:+ => :add_fast, | |
:- => :sub_fast, | |
:* => :mul_fast, | |
:/ => :div_fast, | |
:(==) => :eq_fast, | |
:!= => :ne_fast, | |
:< => :lt_fast, | |
:<= => :le_fast, | |
:abs => :abs_fast, | |
:abs2 => :abs2_fast, | |
:cmp => :cmp_fast, | |
:conj => :conj_fast, | |
:inv => :inv_fast, | |
:mod => :mod_fast, | |
:rem => :rem_fast, | |
:sign => :sign_fast, | |
:isfinite => :isfinite_fast, | |
:isinf => :isinf_fast, | |
:isnan => :isnan_fast, | |
:issubnormal => :issubnormal_fast, | |
# math functions | |
:^ => :pow_fast, | |
:acos => :acos_fast, | |
:acosh => :acosh_fast, | |
:angle => :angle_fast, | |
:asin => :asin_fast, | |
:asinh => :asinh_fast, | |
:atan => :atan_fast, | |
:atan2 => :atan2_fast, | |
:atanh => :atanh_fast, | |
:cbrt => :cbrt_fast, | |
:cis => :cis_fast, | |
:cos => :cos_fast, | |
:cosh => :cosh_fast, | |
:exp10 => :exp10_fast, | |
:exp2 => :exp2_fast, | |
:exp => :exp_fast, | |
:expm1 => :expm1_fast, | |
:hypot => :hypot_fast, | |
:lgamma => :lgamma_fast, | |
:log10 => :log10_fast, | |
:log1p => :log1p_fast, | |
:log2 => :log2_fast, | |
:log => :log_fast, | |
:max => :max_fast, | |
:min => :min_fast, | |
:minmax => :minmax_fast, | |
:sin => :sin_fast, | |
:sinh => :sinh_fast, | |
:sqrt => :sqrt_fast, | |
:tan => :tan_fast, | |
:tanh => :tanh_fast) | |
const rewrite_op = | |
Dict(:+= => :+, | |
:-= => :-, | |
:*= => :*, | |
:/= => :/, | |
:^= => :^) | |
function make_fastmath(expr::Expr) | |
if expr.head === :quote | |
return expr | |
end | |
op = get(rewrite_op, expr.head, :nothing) | |
if op !== :nothing | |
var = expr.args[1] | |
rhs = expr.args[2] | |
if isa(var, Symbol) | |
# simple assignment | |
expr = :($var = $op($var, $rhs)) | |
elseif isa(var, Expr) && var.head === :ref | |
# array reference | |
arr = var.args[1] | |
inds = tuple(var.args[2:end]...) | |
arrvar = gensym() | |
indvars = tuple([gensym() for i in inds]...) | |
expr = quote | |
$(Expr(:(=), arrvar, arr)) | |
$(Expr(:(=), Expr(:tuple, indvars...), Expr(:tuple, inds...))) | |
$arrvar[$(indvars...)] = $op($arrvar[$(indvars...)], $rhs) | |
end | |
end | |
end | |
Expr(make_fastmath(expr.head), map(make_fastmath, expr.args)...) | |
end | |
function make_fastmath(symb::Symbol) | |
fast_symb = get(fast_op, symb, :nothing) | |
if fast_symb === :nothing | |
return symb | |
end | |
:(Base.FastMath.$fast_symb) | |
end | |
make_fastmath(expr) = expr | |
macro fastmath(expr) | |
make_fastmath(esc(expr)) | |
end | |
# Basic arithmetic | |
FloatTypes = Union{Float32, Float64} | |
sub_fast{T<:FloatTypes}(x::T) = box(T,Base.neg_float_fast(unbox(T,x))) | |
add_fast{T<:FloatTypes}(x::T, y::T) = | |
box(T,Base.add_float_fast(unbox(T,x), unbox(T,y))) | |
sub_fast{T<:FloatTypes}(x::T, y::T) = | |
box(T,Base.sub_float_fast(unbox(T,x), unbox(T,y))) | |
mul_fast{T<:FloatTypes}(x::T, y::T) = | |
box(T,Base.mul_float_fast(unbox(T,x), unbox(T,y))) | |
div_fast{T<:FloatTypes}(x::T, y::T) = | |
box(T,Base.div_float_fast(unbox(T,x), unbox(T,y))) | |
rem_fast{T<:FloatTypes}(x::T, y::T) = | |
box(T,Base.rem_float_fast(unbox(T,x), unbox(T,y))) | |
add_fast{T<:FloatTypes}(x::T, y::T, zs::T...) = | |
add_fast(add_fast(x, y), zs...) | |
mul_fast{T<:FloatTypes}(x::T, y::T, zs::T...) = | |
mul_fast(mul_fast(x, y), zs...) | |
@fastmath begin | |
cmp_fast{T<:FloatTypes}(x::T, y::T) = ifelse(x==y, 0, ifelse(x<y, -1, +1)) | |
function mod_fast{T<:FloatTypes}(x::T, y::T) | |
r = rem(x,y) | |
ifelse((r > 0) $ (y > 0), r+y, r) | |
end | |
end | |
eq_fast{T<:FloatTypes}(x::T, y::T) = | |
Base.eq_float_fast(unbox(T,x),unbox(T,y)) | |
ne_fast{T<:FloatTypes}(x::T, y::T) = | |
Base.ne_float_fast(unbox(T,x),unbox(T,y)) | |
lt_fast{T<:FloatTypes}(x::T, y::T) = | |
Base.lt_float_fast(unbox(T,x),unbox(T,y)) | |
le_fast{T<:FloatTypes}(x::T, y::T) = | |
Base.le_float_fast(unbox(T,x),unbox(T,y)) | |
isinf_fast(x) = false | |
isfinite_fast(x) = true | |
isnan_fast(x) = false | |
issubnormal_fast(x) = false | |
# complex numbers | |
ComplexTypes = Union{Complex64, Complex128} | |
@fastmath begin | |
abs_fast{T<:ComplexTypes}(x::T) = hypot(real(x), imag(x)) | |
abs2_fast{T<:ComplexTypes}(x::T) = real(x)*real(x) + imag(x)*imag(x) | |
conj_fast{T<:ComplexTypes}(x::T) = T(real(x), -imag(x)) | |
inv_fast{T<:ComplexTypes}(x::T) = conj(x) / abs2(x) | |
sign_fast{T<:ComplexTypes}(x::T) = x == 0 ? float(zero(x)) : x/abs(x) | |
add_fast{T<:ComplexTypes}(x::T, y::T) = | |
T(real(x)+real(y), imag(x)+imag(y)) | |
add_fast{T<:FloatTypes}(x::Complex{T}, b::T) = | |
Complex{T}(real(x)+b, imag(x)) | |
add_fast{T<:FloatTypes}(a::T, y::Complex{T}) = | |
Complex{T}(a+real(y), imag(y)) | |
sub_fast{T<:ComplexTypes}(x::T, y::T) = | |
T(real(x)-real(y), imag(x)-imag(y)) | |
sub_fast{T<:FloatTypes}(x::Complex{T}, b::T) = | |
Complex{T}(real(x)-b, imag(x)) | |
sub_fast{T<:FloatTypes}(a::T, y::Complex{T}) = | |
Complex{T}(a-real(y), -imag(y)) | |
mul_fast{T<:ComplexTypes}(x::T, y::T) = | |
T(real(x)*real(y) - imag(x)*imag(y), | |
real(x)*imag(y) + imag(x)*real(y)) | |
mul_fast{T<:FloatTypes}(x::Complex{T}, b::T) = | |
Complex{T}(real(x)*b, imag(x)*b) | |
mul_fast{T<:FloatTypes}(a::T, y::Complex{T}) = | |
Complex{T}(a*real(y), a*imag(y)) | |
@inline div_fast{T<:ComplexTypes}(x::T, y::T) = | |
T(real(x)*real(y) + imag(x)*imag(y), | |
imag(x)*real(y) - real(x)*imag(y)) / abs2(y) | |
div_fast{T<:FloatTypes}(x::Complex{T}, b::T) = | |
Complex{T}(real(x)/b, imag(x)/b) | |
div_fast{T<:FloatTypes}(a::T, y::Complex{T}) = | |
Complex{T}(a*real(y), -a*imag(y)) / abs2(y) | |
eq_fast{T<:ComplexTypes}(x::T, y::T) = | |
(real(x)==real(y)) & (imag(x)==imag(y)) | |
eq_fast{T<:FloatTypes}(x::Complex{T}, b::T) = | |
(real(x)==b) & (imag(x)==T(0)) | |
eq_fast{T<:FloatTypes}(a::T, y::Complex{T}) = | |
(a==real(y)) & (T(0)==imag(y)) | |
ne_fast{T<:ComplexTypes}(x::T, y::T) = !(x==y) | |
end | |
# fall-back implementations and type promotion | |
for op in (:abs, :abs2, :conj, :inv, :sign) | |
op_fast = fast_op[op] | |
@eval begin | |
# fall-back implementation for non-numeric types | |
$op_fast(xs...) = $op(xs...) | |
end | |
end | |
for op in (:+, :-, :*, :/, :(==), :!=, :<, :<=, :cmp, :mod, :rem) | |
op_fast = fast_op[op] | |
@eval begin | |
# fall-back implementation for non-numeric types | |
$op_fast(xs...) = $op(xs...) | |
# type promotion | |
$op_fast(x::Number, y::Number, zs::Number...) = | |
$op_fast(promote(x,y,zs...)...) | |
# fall-back implementation that applies after promotion | |
$op_fast{T<:Number}(x::T,ys::T...) = $op(x,ys...) | |
end | |
end | |
# Math functions | |
# builtins | |
pow_fast{T<:FloatTypes}(x::T, y::Integer) = pow_fast(x, Int32(y)) | |
pow_fast{T<:FloatTypes}(x::T, y::Int32) = | |
box(T, Base.powi_llvm(unbox(T,x), unbox(Int32,y))) | |
# TODO: Change sqrt_llvm intrinsic to avoid nan checking; add nan | |
# checking to sqrt in math.jl; remove sqrt_llvm_fast intrinsic | |
sqrt_fast{T<:FloatTypes}(x::T) = box(T, Base.sqrt_llvm_fast(unbox(T,x))) | |
# libm | |
const libm = Base.libm_name | |
for f in (:acos, :acosh, :asin, :asinh, :atan, :atanh, :cbrt, :cos, | |
:cosh, :exp2, :exp, :expm1, :lgamma, :log10, :log1p, :log2, | |
:log, :sin, :sinh, :tan, :tanh) | |
f_fast = fast_op[f] | |
@eval begin | |
$f_fast(x::Float32) = | |
ccall(($(string(f,"f")),libm), Float32, (Float32,), x) | |
$f_fast(x::Float64) = | |
ccall(($(string(f)),libm), Float64, (Float64,), x) | |
end | |
end | |
pow_fast(x::Float32, y::Float32) = | |
ccall(("powf",libm), Float32, (Float32,Float32), x, y) | |
pow_fast(x::Float64, y::Float64) = | |
ccall(("pow",libm), Float64, (Float64,Float64), x, y) | |
atan2_fast(x::Float32, y::Float32) = | |
ccall(("atan2f",libm), Float32, (Float32,Float32), x, y) | |
atan2_fast(x::Float64, y::Float64) = | |
ccall(("atan2",libm), Float64, (Float64,Float64), x, y) | |
# explicit implementations | |
@fastmath begin | |
exp10_fast{T<:FloatTypes}(x::T) = exp2(log2(T(10))*x) | |
exp10_fast(x::Integer) = exp10(float(x)) | |
hypot_fast{T<:FloatTypes}(x::T, y::T) = sqrt(x*x + y*y) | |
# Note: we use the same comparison for min, max, and minmax, so | |
# that the compiler can convert between them | |
max_fast{T<:FloatTypes}(x::T, y::T) = ifelse(y > x, y, x) | |
min_fast{T<:FloatTypes}(x::T, y::T) = ifelse(y > x, x, y) | |
minmax_fast{T<:FloatTypes}(x::T, y::T) = ifelse(y > x, (x,y), (y,x)) | |
# complex numbers | |
cis_fast{T<:FloatTypes}(x::T) = Complex{T}(cos(x), sin(x)) | |
# See <http://en.cppreference.com/w/cpp/numeric/complex> | |
pow_fast{T<:ComplexTypes}(x::T, y::T) = exp(y*log(x)) | |
pow_fast{T<:FloatTypes}(x::T, y::Complex{T}) = exp(y*log(x)) | |
pow_fast{T<:FloatTypes}(x::Complex{T}, y::T) = exp(y*log(x)) | |
acos_fast{T<:ComplexTypes}(x::T) = | |
convert(T,π)/2 + im*log(im*x + sqrt(1-x*x)) | |
acosh_fast{T<:ComplexTypes}(x::T) = log(x + sqrt(x+1) * sqrt(x-1)) | |
angle_fast{T<:ComplexTypes}(x::T) = atan2(imag(x), real(x)) | |
asin_fast{T<:ComplexTypes}(x::T) = -im*asinh(im*x) | |
asinh_fast{T<:ComplexTypes}(x::T) = log(x + sqrt(1+x*x)) | |
atan_fast{T<:ComplexTypes}(x::T) = -im*atanh(im*x) | |
atanh_fast{T<:ComplexTypes}(x::T) = convert(T,1)/2*(log(1+x) - log(1-x)) | |
cis_fast{T<:ComplexTypes}(x::T) = exp(-imag(x)) * cis(real(x)) | |
cos_fast{T<:ComplexTypes}(x::T) = cosh(im*x) | |
cosh_fast{T<:ComplexTypes}(x::T) = convert(T,1)/2*(exp(x) + exp(-x)) | |
exp10_fast{T<:ComplexTypes}(x::T) = | |
exp10(real(x)) * cis(imag(x)*log(convert(T,10))) | |
exp2_fast{T<:ComplexTypes}(x::T) = | |
exp2(real(x)) * cis(imag(x)*log(convert(T,2))) | |
exp_fast{T<:ComplexTypes}(x::T) = exp(real(x)) * cis(imag(x)) | |
expm1_fast{T<:ComplexTypes}(x::T) = exp(x)-1 | |
log10_fast{T<:ComplexTypes}(x::T) = log(x) / log(convert(T,10)) | |
log1p_fast{T<:ComplexTypes}(x::T) = log(1+x) | |
log2_fast{T<:ComplexTypes}(x::T) = log(x) / log(convert(T,2)) | |
log_fast{T<:ComplexTypes}(x::T) = T(log(abs2(x))/2, angle(x)) | |
sin_fast{T<:ComplexTypes}(x::T) = -im*sinh(im*x) | |
sinh_fast{T<:ComplexTypes}(x::T) = convert(T,1)/2*(exp(x) - exp(-x)) | |
sqrt_fast{T<:ComplexTypes}(x::T) = sqrt(abs(x)) * cis(angle(x)/2) | |
tan_fast{T<:ComplexTypes}(x::T) = -im*tanh(im*x) | |
tanh_fast{T<:ComplexTypes}(x::T) = (a=exp(x); b=exp(-x); (a-b)/(a+b)) | |
end | |
# fall-back implementations and type promotion | |
for f in (:acos, :acosh, :angle, :asin, :asinh, :atan, :atanh, :cbrt, | |
:cis, :cos, :cosh, :exp10, :exp2, :exp, :expm1, :lgamma, | |
:log10, :log1p, :log2, :log, :sin, :sinh, :sqrt, :tan, | |
:tanh) | |
f_fast = fast_op[f] | |
@eval begin | |
$f_fast(x) = $f(x) | |
end | |
end | |
for f in (:^, :atan2, :hypot, :max, :min, :minmax) | |
f_fast = fast_op[f] | |
@eval begin | |
# fall-back implementation for non-numeric types | |
$f_fast(x, y) = $f(x, y) | |
# type promotion | |
$f_fast(x::Number, y::Number) = $f_fast(promote(x, y)...) | |
# fall-back implementation that applies after promotion | |
$f_fast{T<:Number}(x::T, y::T) = $f(x, y) | |
end | |
end | |
end | |
const JL_FE_UNDERFLOW = 0x0010 | |
const JL_FE_OVERFLOW = 0x0008 | |
const JL_FE_DIVBYZERO = 0x0004 | |
const JL_FE_INVALID = 0x0001 | |
const JL_FE_TONEAREST = 0x0000 | |
const JL_FE_UPWARD = 0x0800 | |
const JL_FE_DOWNWARD = 0x0400 | |
const JL_FE_TOWARDZERO = 0x0c00 | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# (This is part of the FFTW module.) | |
export dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct! | |
# Discrete cosine transforms (type II/III) via FFTW's r2r transforms; | |
# we follow the Matlab convention and adopt a unitary normalization here. | |
# Unlike Matlab we compute the multidimensional transform by default, | |
# similar to the Julia fft functions. | |
type DCTPlan{T<:fftwNumber,K,inplace} <: Plan{T} | |
plan::r2rFFTWPlan{T} | |
r::Array{UnitRange{Int}} # array of indices for rescaling | |
nrm::Float64 # normalization factor | |
region::Dims # dimensions being transformed | |
pinv::DCTPlan{T} | |
DCTPlan(plan,r,nrm,region) = new(plan,r,nrm,region) | |
end | |
size(p::DCTPlan) = size(p.plan) | |
function show{T,K,inplace}(io::IO, p::DCTPlan{T,K,inplace}) | |
print(io, inplace ? "FFTW in-place " : "FFTW ", | |
K == REDFT10 ? "DCT (DCT-II)" : "IDCT (DCT-III)", " plan for ") | |
showfftdims(io, p.plan.sz, p.plan.istride, eltype(p)) | |
end | |
for (pf, pfr, K, inplace) in ((:plan_dct, :plan_r2r, REDFT10, false), | |
(:plan_dct!, :plan_r2r!, REDFT10, true), | |
(:plan_idct, :plan_r2r, REDFT01, false), | |
(:plan_idct!, :plan_r2r!, REDFT01, true)) | |
@eval function $pf{T<:fftwNumber}(X::StridedArray{T}, region; kws...) | |
r = [1:n for n in size(X)] | |
nrm = sqrt(0.5^length(region) * normalization(X,region)) | |
DCTPlan{T,$K,$inplace}($pfr(X, $K, region; kws...), r, nrm, | |
ntuple(i -> Int(region[i]), length(region))) | |
end | |
end | |
""" | |
plan_dct!(A [, dims [, flags [, timelimit]]]) | |
Same as [`plan_dct`](:func:`plan_dct`), but operates in-place on `A`. | |
""" | |
plan_dct! | |
""" | |
plan_idct(A [, dims [, flags [, timelimit]]]) | |
Pre-plan an optimized inverse discrete cosine transform (DCT), similar to | |
[`plan_fft`](:func:`plan_fft`) except producing a function that computes | |
[`idct`](:func:`idct`). The first two arguments have the same meaning as for | |
[`idct`](:func:`idct`). | |
""" | |
plan_idct | |
""" | |
plan_dct(A [, dims [, flags [, timelimit]]]) | |
Pre-plan an optimized discrete cosine transform (DCT), similar to | |
[`plan_fft`](:func:`plan_fft`) except producing a function that computes | |
[`dct`](:func:`dct`). The first two arguments have the same meaning as for | |
[`dct`](:func:`dct`). | |
""" | |
plan_dct | |
""" | |
plan_idct!(A [, dims [, flags [, timelimit]]]) | |
Same as [`plan_idct`](:func:`plan_idct`), but operates in-place on `A`. | |
""" | |
plan_idct! | |
function plan_inv{T,K,inplace}(p::DCTPlan{T,K,inplace}) | |
X = Array{T}(p.plan.sz) | |
iK = inv_kind[K] | |
DCTPlan{T,iK,inplace}(inplace ? | |
plan_r2r!(X, iK, p.region, flags=p.plan.flags) : | |
plan_r2r(X, iK, p.region, flags=p.plan.flags), | |
p.r, p.nrm, p.region) | |
end | |
for f in (:dct, :dct!, :idct, :idct!) | |
pf = Symbol("plan_", f) | |
@eval begin | |
$f{T<:fftwNumber}(x::AbstractArray{T}) = $pf(x) * x | |
$f{T<:fftwNumber}(x::AbstractArray{T}, region) = $pf(x, region) * x | |
$pf(x::AbstractArray; kws...) = $pf(x, 1:ndims(x); kws...) | |
$f{T<:Real}(x::AbstractArray{T}, region=1:ndims(x)) = $f(fftwfloat(x), region) | |
$pf{T<:Real}(x::AbstractArray{T}, region; kws...) = $pf(fftwfloat(x), region; kws...) | |
$pf{T<:Complex}(x::AbstractArray{T}, region; kws...) = $pf(fftwcomplex(x), region; kws...) | |
end | |
end | |
""" | |
dct(A [, dims]) | |
Performs a multidimensional type-II discrete cosine transform (DCT) of the array `A`, using | |
the unitary normalization of the DCT. The optional `dims` argument specifies an iterable | |
subset of dimensions (e.g. an integer, range, tuple, or array) to transform along. Most | |
efficient if the size of `A` along the transformed dimensions is a product of small primes; | |
see [`nextprod`](:func:`nextprod`). See also [`plan_dct`](:func:`plan_dct`) for even greater | |
efficiency. | |
""" | |
dct | |
""" | |
idct(A [, dims]) | |
Computes the multidimensional inverse discrete cosine transform (DCT) of the array `A` | |
(technically, a type-III DCT with the unitary normalization). The optional `dims` argument | |
specifies an iterable subset of dimensions (e.g. an integer, range, tuple, or array) to | |
transform along. Most efficient if the size of `A` along the transformed dimensions is a | |
product of small primes; see [`nextprod`](:func:`nextprod`). See also | |
[`plan_idct`](:func:`plan_idct`) for even greater efficiency. | |
""" | |
idct | |
""" | |
dct!(A [, dims]) | |
Same as [`dct!`](:func:`dct!`), except that it operates in-place on `A`, which must be an | |
array of real or complex floating-point values. | |
""" | |
dct! | |
""" | |
idct!(A [, dims]) | |
Same as [`idct!`](:func:`idct!`), but operates in-place on `A`. | |
""" | |
idct! | |
const sqrthalf = sqrt(0.5) | |
const sqrt2 = sqrt(2.0) | |
const onerange = 1:1 | |
function A_mul_B!{T}(y::StridedArray{T}, p::DCTPlan{T,REDFT10}, | |
x::StridedArray{T}) | |
assert_applicable(p.plan, x, y) | |
unsafe_execute!(p.plan, x, y) | |
scale!(y, p.nrm) | |
r = p.r | |
for d in p.region | |
oldr = r[d] | |
r[d] = onerange | |
y[r...] *= sqrthalf | |
r[d] = oldr | |
end | |
return y | |
end | |
# note: idct changes input data | |
function A_mul_B!{T}(y::StridedArray{T}, p::DCTPlan{T,REDFT01}, | |
x::StridedArray{T}) | |
assert_applicable(p.plan, x, y) | |
scale!(x, p.nrm) | |
r = p.r | |
for d in p.region | |
oldr = r[d] | |
r[d] = onerange | |
x[r...] *= sqrt2 | |
r[d] = oldr | |
end | |
unsafe_execute!(p.plan, x, y) | |
return y | |
end | |
*{T}(p::DCTPlan{T,REDFT10,false}, x::StridedArray{T}) = | |
A_mul_B!(Array{T}(p.plan.osz), p, x) | |
*{T}(p::DCTPlan{T,REDFT01,false}, x::StridedArray{T}) = | |
A_mul_B!(Array{T}(p.plan.osz), p, copy(x)) # need copy to preserve input | |
*{T,K}(p::DCTPlan{T,K,true}, x::StridedArray{T}) = A_mul_B!(x, p, x) | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
module FFTW | |
import ..DFT: fft, bfft, ifft, rfft, brfft, irfft, plan_fft, plan_bfft, plan_ifft, plan_rfft, plan_brfft, plan_irfft, fft!, bfft!, ifft!, plan_fft!, plan_bfft!, plan_ifft!, Plan, rfft_output_size, brfft_output_size, plan_inv, normalization, ScaledPlan | |
import Base: show, *, convert, unsafe_convert, size, strides, ndims, pointer, A_mul_B! | |
export r2r, r2r!, plan_r2r, plan_r2r! | |
export export_wisdom, import_wisdom, import_system_wisdom, forget_wisdom, | |
MEASURE, DESTROY_INPUT, UNALIGNED, CONSERVE_MEMORY, EXHAUSTIVE, | |
PRESERVE_INPUT, PATIENT, ESTIMATE, WISDOM_ONLY, NO_TIMELIMIT, | |
R2HC, HC2R, DHT, REDFT00, REDFT01, REDFT10, REDFT11, | |
RODFT00, RODFT01, RODFT10, RODFT11, | |
fftwNumber, fftwReal, fftwComplex, flops | |
## FFT: Implement fft by calling fftw. | |
const libfftw = Base.libfftw_name | |
const libfftwf = Base.libfftwf_name | |
const version = convert(VersionNumber, split(unsafe_string(cglobal((:fftw_version,Base.DFT.FFTW.libfftw), UInt8)), ['-', ' '])[2]) | |
## Direction of FFT | |
const FORWARD = -1 | |
const BACKWARD = 1 | |
## FFTW Flags from fftw3.h | |
const MEASURE = UInt32(0) | |
const DESTROY_INPUT = UInt32(1 << 0) | |
const UNALIGNED = UInt32(1 << 1) | |
const CONSERVE_MEMORY = UInt32(1 << 2) | |
const EXHAUSTIVE = UInt32(1 << 3) # NO_EXHAUSTIVE is default | |
const PRESERVE_INPUT = UInt32(1 << 4) # cancels DESTROY_INPUT | |
const PATIENT = UInt32(1 << 5) # IMPATIENT is default | |
const ESTIMATE = UInt32(1 << 6) | |
const WISDOM_ONLY = UInt32(1 << 21) | |
const NO_SIMD = UInt32(1 << 17) # disable SIMD, useful for benchmarking | |
## R2R transform kinds | |
const R2HC = 0 | |
const HC2R = 1 | |
const DHT = 2 | |
const REDFT00 = 3 | |
const REDFT01 = 4 | |
const REDFT10 = 5 | |
const REDFT11 = 6 | |
const RODFT00 = 7 | |
const RODFT01 = 8 | |
const RODFT10 = 9 | |
const RODFT11 = 10 | |
let k2s = Dict(R2HC => "R2HC", HC2R => "HC2R", DHT => "DHT", REDFT00 => "REDFT00", REDFT01 => "REDFT01", REDFT10 => "REDFT10", REDFT11 => "REDFT11", RODFT00 => "RODFT00", RODFT01 => "RODFT01", RODFT10 => "RODFT10", RODFT11 => "RODFT11") | |
global kind2string | |
kind2string(k::Integer) = k2s[Int(k)] | |
end | |
# FFTW floating-point types: | |
typealias fftwNumber Union{Float64,Float32,Complex128,Complex64} | |
typealias fftwReal Union{Float64,Float32} | |
typealias fftwComplex Union{Complex128,Complex64} | |
typealias fftwDouble Union{Float64,Complex128} | |
typealias fftwSingle Union{Float32,Complex64} | |
typealias fftwTypeDouble Union{Type{Float64},Type{Complex128}} | |
typealias fftwTypeSingle Union{Type{Float32},Type{Complex64}} | |
# For ESTIMATE plans, FFTW allows one to pass NULL for the array pointer, | |
# since it is not written to. Hence, it is convenient to create an | |
# array-like type that carries a size and a stride like a "real" array | |
# but which is converted to C_NULL as a pointer. | |
immutable FakeArray{T, N} <: DenseArray{T, N} | |
sz::NTuple{N, Int} | |
st::NTuple{N, Int} | |
end | |
size(a::FakeArray) = a.sz | |
strides(a::FakeArray) = a.st | |
unsafe_convert{T}(::Type{Ptr{T}}, a::FakeArray{T}) = convert(Ptr{T}, C_NULL) | |
pointer{T}(a::FakeArray{T}) = convert(Ptr{T}, C_NULL) | |
FakeArray{T, N}(::Type{T}, sz::NTuple{N, Int}) = | |
FakeArray{T, N}(sz, colmajorstrides(sz)) | |
FakeArray{T}(::Type{T}, sz::Int...) = FakeArray(T, sz) | |
fakesimilar(flags, X, T) = flags & ESTIMATE != 0 ? FakeArray(T, size(X)) : Array{T}(size(X)) | |
alignment_of(A::FakeArray) = Int32(0) | |
## Julia wrappers around FFTW functions | |
# Wisdom | |
# Import and export wisdom to/from a single file for all precisions, | |
# which is more user-friendly than requiring the user to call a | |
# separate routine depending on the fp precision of the plans. This | |
# requires a bit of trickness since we have to (a) use the libc file | |
# I/O routines with fftw_export_wisdom_to_file/import_wisdom_from_file | |
# (b) we need 256 bytes of space padding between the wisdoms to work | |
# around FFTW's internal file i/o buffering [see the BUFSZ constant in | |
# FFTW's api/import-wisdom-from-file.c file]. | |
function export_wisdom(fname::AbstractString) | |
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :w) | |
systemerror("could not open wisdom file $fname for writing", f == C_NULL) | |
ccall((:fftw_export_wisdom_to_file,libfftw), Void, (Ptr{Void},), f) | |
ccall(:fputs, Int32, (Ptr{UInt8},Ptr{Void}), " "^256, f) # no NUL, hence no Cstring | |
ccall((:fftwf_export_wisdom_to_file,libfftwf), Void, (Ptr{Void},), f) | |
ccall(:fclose, Void, (Ptr{Void},), f) | |
end | |
function import_wisdom(fname::AbstractString) | |
f = ccall(:fopen, Ptr{Void}, (Cstring,Cstring), fname, :r) | |
systemerror("could not open wisdom file $fname for reading", f == C_NULL) | |
if ccall((:fftw_import_wisdom_from_file,libfftw),Int32,(Ptr{Void},),f)==0|| | |
ccall((:fftwf_import_wisdom_from_file,libfftwf),Int32,(Ptr{Void},),f)==0 | |
error("failed to import wisdom from $fname") | |
end | |
ccall(:fclose, Void, (Ptr{Void},), f) | |
end | |
function import_system_wisdom() | |
if ccall((:fftw_import_system_wisdom,libfftw), Int32, ()) == 0 || | |
ccall((:fftwf_import_system_wisdom,libfftwf), Int32, ()) == 0 | |
error("failed to import system wisdom") | |
end | |
end | |
function forget_wisdom() | |
ccall((:fftw_forget_wisdom,libfftw), Void, ()) | |
ccall((:fftwf_forget_wisdom,libfftwf), Void, ()) | |
end | |
# Threads | |
let initialized = false | |
global set_num_threads | |
function set_num_threads(nthreads::Integer) | |
if !initialized | |
# must re-initialize FFTW if any FFTW routines have been called | |
ccall((:fftw_cleanup,libfftw), Void, ()) | |
ccall((:fftwf_cleanup,libfftwf), Void, ()) | |
stat = ccall((:fftw_init_threads,libfftw), Int32, ()) | |
statf = ccall((:fftwf_init_threads,libfftwf), Int32, ()) | |
if stat == 0 || statf == 0 | |
error("could not initialize FFTW threads") | |
end | |
initialized = true | |
end | |
ccall((:fftw_plan_with_nthreads,libfftw), Void, (Int32,), nthreads) | |
ccall((:fftwf_plan_with_nthreads,libfftwf), Void, (Int32,), nthreads) | |
end | |
end | |
# pointer type for fftw_plan (opaque pointer) | |
immutable fftw_plan_struct end | |
typealias PlanPtr Ptr{fftw_plan_struct} | |
# Planner timelimits | |
const NO_TIMELIMIT = -1.0 # from fftw3.h | |
set_timelimit(precision::fftwTypeDouble,seconds) = | |
ccall((:fftw_set_timelimit,libfftw), Void, (Float64,), seconds) | |
set_timelimit(precision::fftwTypeSingle,seconds) = | |
ccall((:fftwf_set_timelimit,libfftwf), Void, (Float64,), seconds) | |
# Array alignment mod 16: | |
# FFTW plans may depend on the alignment of the array mod 16 bytes, | |
# i.e. the address mod 16 of the first element of the array, in order | |
# to exploit SIMD operations. Julia arrays are, by default, aligned | |
# to 16-byte boundaries (address mod 16 == 0), but this may not be | |
# true for data imported from external C code, or for SubArrays. | |
# Use the undocumented routine fftw_alignment_of to determine the | |
# alignment of a given pointer modulo whatever FFTW needs; this | |
# function will be documented in FFTW 3.3.4. | |
if Base.libfftw_name == "libmkl_rt" | |
alignment_of{T<:fftwDouble}(A::StridedArray{T}) = | |
convert(Int32, convert(Int64, pointer(A)) % 16) | |
alignment_of{T<:fftwSingle}(A::StridedArray{T}) = | |
convert(Int32, convert(Int64, pointer(A)) % 16) | |
else | |
alignment_of{T<:fftwDouble}(A::StridedArray{T}) = | |
ccall((:fftw_alignment_of, libfftw), Int32, (Ptr{T},), A) | |
alignment_of{T<:fftwSingle}(A::StridedArray{T}) = | |
ccall((:fftwf_alignment_of, libfftwf), Int32, (Ptr{T},), A) | |
end | |
# FFTWPlan (low-level) | |
# low-level storage of the FFTW plan, along with the information | |
# needed to determine whether it is applicable. We need to put | |
# this into a type to support a finalizer on the fftw_plan. | |
# K is FORWARD/BACKWARD for forward/backward or r2c/c2r plans, respectively. | |
# For r2r plans, K is a tuple of the transform kinds along each dimension. | |
abstract FFTWPlan{T<:fftwNumber,K,inplace} <: Plan{T} | |
for P in (:cFFTWPlan, :rFFTWPlan, :r2rFFTWPlan) # complex, r2c/c2r, and r2r | |
@eval begin | |
type $P{T<:fftwNumber,K,inplace,N} <: FFTWPlan{T,K,inplace} | |
plan::PlanPtr | |
sz::NTuple{N, Int} # size of array on which plan operates (Int tuple) | |
osz::NTuple{N, Int} # size of output array (Int tuple) | |
istride::NTuple{N, Int} # strides of input | |
ostride::NTuple{N, Int} # strides of output | |
ialign::Int32 # alignment mod 16 of input | |
oalign::Int32 # alignment mod 16 of input | |
flags::UInt32 # planner flags | |
region::Any # region (iterable) of dims that are transormed | |
pinv::ScaledPlan | |
function $P(plan::PlanPtr, flags::Integer, R::Any, | |
X::StridedArray{T, N}, Y::StridedArray) | |
p = new(plan, size(X), size(Y), strides(X), strides(Y), | |
alignment_of(X), alignment_of(Y), flags, R) | |
finalizer(p, destroy_plan) | |
p | |
end | |
end | |
end | |
end | |
size(p::FFTWPlan) = p.sz | |
unsafe_convert(::Type{PlanPtr}, p::FFTWPlan) = p.plan | |
destroy_plan{T<:fftwDouble}(plan::FFTWPlan{T}) = | |
ccall((:fftw_destroy_plan,libfftw), Void, (PlanPtr,), plan) | |
destroy_plan{T<:fftwSingle}(plan::FFTWPlan{T}) = | |
ccall((:fftwf_destroy_plan,libfftwf), Void, (PlanPtr,), plan) | |
cost{T<:fftwDouble}(plan::FFTWPlan{T}) = | |
ccall((:fftw_cost,libfftw), Float64, (PlanPtr,), plan) | |
cost{T<:fftwSingle}(plan::FFTWPlan{T}) = | |
ccall((:fftwf_cost,libfftwf), Float64, (PlanPtr,), plan) | |
function arithmetic_ops{T<:fftwDouble}(plan::FFTWPlan{T}) | |
# Change to individual Ref after we can allocate them on stack | |
ref = Ref{NTuple{3, Float64}}() | |
ptr = Ptr{Float64}(Base.unsafe_convert(Ptr{NTuple{3, Float64}}, ref)) | |
ccall((:fftw_flops,libfftw), Void, | |
(PlanPtr,Ptr{Float64},Ptr{Float64},Ptr{Float64}), | |
plan, ptr, ptr + 8, ptr + 16) | |
(round(Int64, ref[][1]), round(Int64, ref[][2]), round(Int64, ref[][3])) | |
end | |
function arithmetic_ops{T<:fftwSingle}(plan::FFTWPlan{T}) | |
# Change to individual Ref after we can allocate them on stack | |
ref = Ref{NTuple{3, Float64}}() | |
ptr = Ptr{Float64}(Base.unsafe_convert(Ptr{NTuple{3, Float64}}, ref)) | |
ccall((:fftwf_flops,libfftwf), Void, | |
(PlanPtr,Ptr{Float64},Ptr{Float64},Ptr{Float64}), | |
plan, ptr, ptr + 8, ptr + 16) | |
(round(Int64, ref[][1]), round(Int64, ref[][2]), round(Int64, ref[][3])) | |
end | |
flops(plan::FFTWPlan) = let ops = arithmetic_ops(plan) | |
ops[1] + ops[2] + 2 * ops[3] # add + mul + 2*fma | |
end | |
# Pretty-printing plans | |
function showfftdims(io, sz::Dims, istride::Dims, T) | |
if isempty(sz) | |
print(io, "0-dimensional") | |
elseif length(sz) == 1 | |
print(io, sz[1], "-element") | |
else | |
print(io, join(sz, "×")) | |
end | |
if istride == colmajorstrides(sz) | |
print(io, " array of ", T) | |
else | |
print(io, " $istride-strided array of ", T) | |
end | |
end | |
# The sprint_plan function was released in FFTW 3.3.4 | |
sprint_plan_{T<:fftwDouble}(plan::FFTWPlan{T}) = | |
ccall((:fftw_sprint_plan,libfftw), Ptr{UInt8}, (PlanPtr,), plan) | |
sprint_plan_{T<:fftwSingle}(plan::FFTWPlan{T}) = | |
ccall((:fftwf_sprint_plan,libfftwf), Ptr{UInt8}, (PlanPtr,), plan) | |
function sprint_plan(plan::FFTWPlan) | |
unsafe_wrap(String, sprint_plan_(plan), true) | |
end | |
function show{T,K,inplace}(io::IO, p::cFFTWPlan{T,K,inplace}) | |
print(io, inplace ? "FFTW in-place " : "FFTW ", | |
K < 0 ? "forward" : "backward", " plan for ") | |
showfftdims(io, p.sz, p.istride, T) | |
version >= v"3.3.4" && print(io, "\n", sprint_plan(p)) | |
end | |
function show{T,K,inplace}(io::IO, p::rFFTWPlan{T,K,inplace}) | |
print(io, inplace ? "FFTW in-place " : "FFTW ", | |
K < 0 ? "real-to-complex" : "complex-to-real", | |
" plan for ") | |
showfftdims(io, p.sz, p.istride, T) | |
version >= v"3.3.4" && print(io, "\n", sprint_plan(p)) | |
end | |
function show{T,K,inplace}(io::IO, p::r2rFFTWPlan{T,K,inplace}) | |
print(io, inplace ? "FFTW in-place r2r " : "FFTW r2r ") | |
if isempty(K) | |
print(io, "0-dimensional") | |
elseif K == ntuple(i -> K[1], length(K)) | |
print(io, kind2string(K[1])) | |
if length(K) > 1 | |
print(io, "^", length(K)) | |
end | |
else | |
print(io, join(map(kind2string, K), "×")) | |
end | |
print(io, " plan for ") | |
showfftdims(io, p.sz, p.istride, T) | |
version >= v"3.3.4" && print(io, "\n", sprint_plan(p)) | |
end | |
# Check whether a FFTWPlan is applicable to a given input array, and | |
# throw an informative error if not: | |
function assert_applicable{T}(p::FFTWPlan{T}, X::StridedArray{T}) | |
if size(X) != p.sz | |
throw(ArgumentError("FFTW plan applied to wrong-size array")) | |
elseif strides(X) != p.istride | |
throw(ArgumentError("FFTW plan applied to wrong-strides array")) | |
elseif alignment_of(X) != p.ialign || p.flags & UNALIGNED != 0 | |
throw(ArgumentError("FFTW plan applied to array with wrong memory alignment")) | |
end | |
end | |
function assert_applicable{T,K,inplace}(p::FFTWPlan{T,K,inplace}, X::StridedArray{T}, Y::StridedArray) | |
assert_applicable(p, X) | |
if size(Y) != p.osz | |
throw(ArgumentError("FFTW plan applied to wrong-size output")) | |
elseif strides(Y) != p.ostride | |
throw(ArgumentError("FFTW plan applied to wrong-strides output")) | |
elseif alignment_of(Y) != p.oalign || p.flags & UNALIGNED != 0 | |
throw(ArgumentError("FFTW plan applied to output with wrong memory alignment")) | |
elseif inplace != (pointer(X) == pointer(Y)) | |
throw(ArgumentError(string("FFTW ", | |
inplace ? "in-place" : "out-of-place", | |
" plan applied to ", | |
inplace ? "out-of-place" : "in-place", | |
" data"))) | |
end | |
end | |
# strides for a column-major (Julia-style) array of size == sz | |
colmajorstrides(sz) = isempty(sz) ? () : (1,cumprod(Int[sz[1:end-1]...])...) | |
# Execute | |
unsafe_execute!{T<:fftwDouble}(plan::FFTWPlan{T}) = | |
ccall((:fftw_execute,libfftw), Void, (PlanPtr,), plan) | |
unsafe_execute!{T<:fftwSingle}(plan::FFTWPlan{T}) = | |
ccall((:fftwf_execute,libfftwf), Void, (PlanPtr,), plan) | |
unsafe_execute!{T<:fftwDouble}(plan::cFFTWPlan{T}, | |
X::StridedArray{T}, Y::StridedArray{T}) = | |
ccall((:fftw_execute_dft,libfftw), Void, | |
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) | |
unsafe_execute!{T<:fftwSingle}(plan::cFFTWPlan{T}, | |
X::StridedArray{T}, Y::StridedArray{T}) = | |
ccall((:fftwf_execute_dft,libfftwf), Void, | |
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) | |
unsafe_execute!(plan::rFFTWPlan{Float64,FORWARD}, | |
X::StridedArray{Float64}, Y::StridedArray{Complex128}) = | |
ccall((:fftw_execute_dft_r2c,libfftw), Void, | |
(PlanPtr,Ptr{Float64},Ptr{Complex128}), plan, X, Y) | |
unsafe_execute!(plan::rFFTWPlan{Float32,FORWARD}, | |
X::StridedArray{Float32}, Y::StridedArray{Complex64}) = | |
ccall((:fftwf_execute_dft_r2c,libfftwf), Void, | |
(PlanPtr,Ptr{Float32},Ptr{Complex64}), plan, X, Y) | |
unsafe_execute!(plan::rFFTWPlan{Complex128,BACKWARD}, | |
X::StridedArray{Complex128}, Y::StridedArray{Float64}) = | |
ccall((:fftw_execute_dft_c2r,libfftw), Void, | |
(PlanPtr,Ptr{Complex128},Ptr{Float64}), plan, X, Y) | |
unsafe_execute!(plan::rFFTWPlan{Complex64,BACKWARD}, | |
X::StridedArray{Complex64}, Y::StridedArray{Float32}) = | |
ccall((:fftwf_execute_dft_c2r,libfftwf), Void, | |
(PlanPtr,Ptr{Complex64},Ptr{Float32}), plan, X, Y) | |
unsafe_execute!{T<:fftwDouble}(plan::r2rFFTWPlan{T}, | |
X::StridedArray{T}, Y::StridedArray{T}) = | |
ccall((:fftw_execute_r2r,libfftw), Void, | |
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) | |
unsafe_execute!{T<:fftwSingle}(plan::r2rFFTWPlan{T}, | |
X::StridedArray{T}, Y::StridedArray{T}) = | |
ccall((:fftwf_execute_r2r,libfftwf), Void, | |
(PlanPtr,Ptr{T},Ptr{T}), plan, X, Y) | |
# NOTE ON GC (garbage collection): | |
# The FFTWPlan has a finalizer so that gc will destroy the plan, | |
# which is necessary for gc to work with plan_fft. However, | |
# even when we are creating a single-use FFTWPlan [e.g. for fftn(x)], | |
# we intentionally do NOT call destroy_plan explicitly, and instead | |
# wait for garbage collection. The reason is that, in the common | |
# case where the user calls fft(x) a second time soon afterwards, | |
# if destroy_plan has not yet been called then FFTW will internally | |
# re-use the table of trigonometric constants from the first plan. | |
# Compute dims and howmany for FFTW guru planner | |
function dims_howmany(X::StridedArray, Y::StridedArray, | |
sz::Array{Int,1}, region) | |
reg = [region...] | |
if length(unique(reg)) < length(reg) | |
throw(ArgumentError("each dimension can be transformed at most once")) | |
end | |
ist = [strides(X)...] | |
ost = [strides(Y)...] | |
dims = [sz[reg] ist[reg] ost[reg]]' | |
oreg = [1:ndims(X);] | |
oreg[reg] = 0 | |
oreg = filter(d -> d > 0, oreg) | |
howmany = [sz[oreg] ist[oreg] ost[oreg]]' | |
return (dims, howmany) | |
end | |
# check & convert kinds into int32 array with same length as region | |
function fix_kinds(region, kinds) | |
if length(kinds) != length(region) | |
if length(kinds) > length(region) | |
throw(ArgumentError("too many transform kinds")) | |
else | |
if isempty(kinds) | |
throw(ArgumentError("must supply a transform kind")) | |
end | |
k = Array{Int32}(length(region)) | |
k[1:length(kinds)] = [kinds...] | |
k[length(kinds)+1:end] = kinds[end] | |
kinds = k | |
end | |
else | |
kinds = Int32[kinds...] | |
end | |
for i = 1:length(kinds) | |
if kinds[i] < 0 || kinds[i] > 10 | |
throw(ArgumentError("invalid transform kind")) | |
end | |
end | |
return kinds | |
end | |
# low-level FFTWPlan creation (for internal use in FFTW module) | |
for (Tr,Tc,fftw,lib) in ((:Float64,:Complex128,"fftw",libfftw), | |
(:Float32,:Complex64,"fftwf",libfftwf)) | |
@eval function (::Type{cFFTWPlan{$Tc,K,inplace,N}}){K,inplace,N}(X::StridedArray{$Tc,N}, | |
Y::StridedArray{$Tc,N}, | |
region, flags::Integer, timelimit::Real) | |
direction = K | |
set_timelimit($Tr, timelimit) | |
R = isa(region, Tuple) ? region : copy(region) | |
dims, howmany = dims_howmany(X, Y, [size(X)...], R) | |
plan = ccall(($(string(fftw,"_plan_guru64_dft")),$lib), | |
PlanPtr, | |
(Int32, Ptr{Int}, Int32, Ptr{Int}, | |
Ptr{$Tc}, Ptr{$Tc}, Int32, UInt32), | |
size(dims,2), dims, size(howmany,2), howmany, | |
X, Y, direction, flags) | |
set_timelimit($Tr, NO_TIMELIMIT) | |
if plan == C_NULL | |
error("FFTW could not create plan") # shouldn't normally happen | |
end | |
return cFFTWPlan{$Tc,K,inplace,N}(plan, flags, R, X, Y) | |
end | |
@eval function (::Type{rFFTWPlan{$Tr,$FORWARD,inplace,N}}){inplace,N}(X::StridedArray{$Tr,N}, | |
Y::StridedArray{$Tc,N}, | |
region, flags::Integer, timelimit::Real) | |
R = isa(region, Tuple) ? region : copy(region) | |
region = circshift([region...],-1) # FFTW halves last dim | |
set_timelimit($Tr, timelimit) | |
dims, howmany = dims_howmany(X, Y, [size(X)...], region) | |
plan = ccall(($(string(fftw,"_plan_guru64_dft_r2c")),$lib), | |
PlanPtr, | |
(Int32, Ptr{Int}, Int32, Ptr{Int}, | |
Ptr{$Tr}, Ptr{$Tc}, UInt32), | |
size(dims,2), dims, size(howmany,2), howmany, | |
X, Y, flags) | |
set_timelimit($Tr, NO_TIMELIMIT) | |
if plan == C_NULL | |
error("FFTW could not create plan") # shouldn't normally happen | |
end | |
return rFFTWPlan{$Tr,$FORWARD,inplace,N}(plan, flags, R, X, Y) | |
end | |
@eval function (::Type{rFFTWPlan{$Tc,$BACKWARD,inplace,N}}){inplace,N}(X::StridedArray{$Tc,N}, | |
Y::StridedArray{$Tr,N}, | |
region, flags::Integer, timelimit::Real) | |
R = isa(region, Tuple) ? region : copy(region) | |
region = circshift([region...],-1) # FFTW halves last dim | |
set_timelimit($Tr, timelimit) | |
dims, howmany = dims_howmany(X, Y, [size(Y)...], region) | |
plan = ccall(($(string(fftw,"_plan_guru64_dft_c2r")),$lib), | |
PlanPtr, | |
(Int32, Ptr{Int}, Int32, Ptr{Int}, | |
Ptr{$Tc}, Ptr{$Tr}, UInt32), | |
size(dims,2), dims, size(howmany,2), howmany, | |
X, Y, flags) | |
set_timelimit($Tr, NO_TIMELIMIT) | |
if plan == C_NULL | |
error("FFTW could not create plan") # shouldn't normally happen | |
end | |
return rFFTWPlan{$Tc,$BACKWARD,inplace,N}(plan, flags, R, X, Y) | |
end | |
@eval function (::Type{r2rFFTWPlan{$Tr,ANY,inplace,N}}){inplace,N}(X::StridedArray{$Tr,N}, | |
Y::StridedArray{$Tr,N}, | |
region, kinds, flags::Integer, | |
timelimit::Real) | |
R = isa(region, Tuple) ? region : copy(region) | |
knd = fix_kinds(region, kinds) | |
set_timelimit($Tr, timelimit) | |
dims, howmany = dims_howmany(X, Y, [size(X)...], region) | |
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib), | |
PlanPtr, | |
(Int32, Ptr{Int}, Int32, Ptr{Int}, | |
Ptr{$Tr}, Ptr{$Tr}, Ptr{Int32}, UInt32), | |
size(dims,2), dims, size(howmany,2), howmany, | |
X, Y, knd, flags) | |
set_timelimit($Tr, NO_TIMELIMIT) | |
if plan == C_NULL | |
error("FFTW could not create plan") # shouldn't normally happen | |
end | |
r2rFFTWPlan{$Tr,(map(Int,knd)...),inplace,N}(plan, flags, R, X, Y) | |
end | |
# support r2r transforms of complex = transforms of real & imag parts | |
@eval function (::Type{r2rFFTWPlan{$Tc,ANY,inplace,N}}){inplace,N}(X::StridedArray{$Tc,N}, | |
Y::StridedArray{$Tc,N}, | |
region, kinds, flags::Integer, | |
timelimit::Real) | |
R = isa(region, Tuple) ? region : copy(region) | |
knd = fix_kinds(region, kinds) | |
set_timelimit($Tr, timelimit) | |
dims, howmany = dims_howmany(X, Y, [size(X)...], region) | |
dims[2:3, 1:size(dims,2)] *= 2 | |
howmany[2:3, 1:size(howmany,2)] *= 2 | |
howmany = [howmany [2,1,1]] # append loop over real/imag parts | |
plan = ccall(($(string(fftw,"_plan_guru64_r2r")),$lib), | |
PlanPtr, | |
(Int32, Ptr{Int}, Int32, Ptr{Int}, | |
Ptr{$Tc}, Ptr{$Tc}, Ptr{Int32}, UInt32), | |
size(dims,2), dims, size(howmany,2), howmany, | |
X, Y, knd, flags) | |
set_timelimit($Tr, NO_TIMELIMIT) | |
if plan == C_NULL | |
error("FFTW could not create plan") # shouldn't normally happen | |
end | |
r2rFFTWPlan{$Tc,(map(Int,knd)...),inplace,N}(plan, flags, R, X, Y) | |
end | |
end | |
# Convert arrays of numeric types to FFTW-supported packed complex-float types | |
# (FIXME: is there a way to use the Julia promotion rules more cleverly here?) | |
fftwcomplex{T<:fftwComplex}(X::StridedArray{T}) = X | |
fftwcomplex{T<:fftwReal}(X::AbstractArray{T}) = | |
copy!(Array{typeof(complex(one(T)))}(size(X)), X) | |
fftwcomplex{T<:Real}(X::AbstractArray{T}) = copy!(Array{Complex128}(size(X)),X) | |
fftwcomplex{T<:Complex}(X::AbstractArray{T}) = | |
copy!(Array{Complex128}(size(X)), X) | |
fftwfloat{T<:fftwReal}(X::StridedArray{T}) = X | |
fftwfloat{T<:Real}(X::AbstractArray{T}) = copy!(Array{Float64}(size(X)), X) | |
fftwfloat{T<:Complex}(X::AbstractArray{T}) = fftwcomplex(X) | |
for (f,direction) in ((:fft,FORWARD), (:bfft,BACKWARD)) | |
plan_f = Symbol("plan_",f) | |
plan_f! = Symbol("plan_",f,"!") | |
idirection = -direction | |
@eval begin | |
function $plan_f{T<:fftwComplex,N}(X::StridedArray{T,N}, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
cFFTWPlan{T,$direction,false,N}(X, fakesimilar(flags, X, T), | |
region, flags, timelimit) | |
end | |
function $plan_f!{T<:fftwComplex,N}(X::StridedArray{T,N}, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
cFFTWPlan{T,$direction,true,N}(X, X, region, flags, timelimit) | |
end | |
$plan_f{T<:fftwComplex}(X::StridedArray{T}; kws...) = | |
$plan_f(X, 1:ndims(X); kws...) | |
$plan_f!{T<:fftwComplex}(X::StridedArray{T}; kws...) = | |
$plan_f!(X, 1:ndims(X); kws...) | |
function plan_inv{T<:fftwComplex,N,inplace}(p::cFFTWPlan{T,$direction,inplace,N}) | |
X = Array{T}(p.sz) | |
Y = inplace ? X : fakesimilar(p.flags, X, T) | |
ScaledPlan(cFFTWPlan{T,$idirection,inplace,N}(X, Y, p.region, | |
p.flags, NO_TIMELIMIT), | |
normalization(X, p.region)) | |
end | |
end | |
end | |
function A_mul_B!{T}(y::StridedArray{T}, p::cFFTWPlan{T}, x::StridedArray{T}) | |
assert_applicable(p, x, y) | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function *{T,K,N}(p::cFFTWPlan{T,K,false}, x::StridedArray{T,N}) | |
assert_applicable(p, x) | |
y = Array{T}(p.osz)::Array{T,N} | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function *{T,K}(p::cFFTWPlan{T,K,true}, x::StridedArray{T}) | |
assert_applicable(p, x) | |
unsafe_execute!(p, x, x) | |
return x | |
end | |
# rfft/brfft and planned variants. No in-place version for now. | |
for (Tr,Tc) in ((:Float32,:Complex64),(:Float64,:Complex128)) | |
# Note: use $FORWARD and $BACKWARD below because of issue #9775 | |
@eval begin | |
function plan_rfft{N}(X::StridedArray{$Tr,N}, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
osize = rfft_output_size(X, region) | |
Y = flags&ESTIMATE != 0 ? FakeArray($Tc,osize...) : Array{$Tc}(osize...) | |
rFFTWPlan{$Tr,$FORWARD,false,N}(X, Y, region, flags, timelimit) | |
end | |
function plan_brfft{N}(X::StridedArray{$Tc,N}, d::Integer, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
osize = brfft_output_size(X, d, region) | |
Y = flags&ESTIMATE != 0 ? FakeArray($Tr,osize...) : Array{$Tr}(osize...) | |
# FFTW currently doesn't support PRESERVE_INPUT for | |
# multidimensional out-of-place c2r transforms, so | |
# we have to handle 1d and >1d cases separately with a copy. Ugh. | |
if length(region) <= 1 | |
rFFTWPlan{$Tc,$BACKWARD,false,N}(X, Y, region, | |
flags | PRESERVE_INPUT, | |
timelimit) | |
else | |
rFFTWPlan{$Tc,$BACKWARD,false,N}(copy(X), Y, region, flags, | |
timelimit) | |
end | |
end | |
plan_rfft(X::StridedArray{$Tr};kws...)=plan_rfft(X,1:ndims(X);kws...) | |
plan_brfft(X::StridedArray{$Tr};kws...)=plan_brfft(X,1:ndims(X);kws...) | |
function plan_inv{N}(p::rFFTWPlan{$Tr,$FORWARD,false,N}) | |
X = Array{$Tr}(p.sz) | |
Y = p.flags&ESTIMATE != 0 ? FakeArray($Tc,p.osz) : Array{$Tc}(p.osz) | |
ScaledPlan(rFFTWPlan{$Tc,$BACKWARD,false,N}(Y, X, p.region, | |
length(p.region) <= 1 ? | |
p.flags | PRESERVE_INPUT : | |
p.flags, NO_TIMELIMIT), | |
normalization(X, p.region)) | |
end | |
function plan_inv{N}(p::rFFTWPlan{$Tc,$BACKWARD,false,N}) | |
X = Arra{$Tc}(p.sz) | |
Y = p.flags&ESTIMATE != 0 ? FakeArray($Tr,p.osz) : Array{$Tr}(p.osz) | |
ScaledPlan(rFFTWPlan{$Tr,$FORWARD,false,N}(Y, X, p.region, | |
p.flags, NO_TIMELIMIT), | |
normalization(Y, p.region)) | |
end | |
function A_mul_B!(y::StridedArray{$Tc}, p::rFFTWPlan{$Tr,$FORWARD}, x::StridedArray{$Tr}) | |
assert_applicable(p, x, y) | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function A_mul_B!(y::StridedArray{$Tr}, p::rFFTWPlan{$Tc,$BACKWARD}, x::StridedArray{$Tc}) | |
assert_applicable(p, x, y) | |
unsafe_execute!(p, x, y) # note: may overwrite x as well as y! | |
return y | |
end | |
function *{N}(p::rFFTWPlan{$Tr,$FORWARD,false}, x::StridedArray{$Tr,N}) | |
assert_applicable(p, x) | |
y = Array{$Tc}(p.osz)::Array{$Tc,N} | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function *{N}(p::rFFTWPlan{$Tc,$BACKWARD,false}, x::StridedArray{$Tc,N}) | |
if p.flags & PRESERVE_INPUT != 0 | |
assert_applicable(p, x) | |
y = Array{$Tr}(p.osz)::Array{$Tr,N} | |
unsafe_execute!(p, x, y) | |
else # need to make a copy to avoid overwriting x | |
xc = copy(x) | |
assert_applicable(p, xc) | |
y = Array{$Tr}(p.osz)::Array{$Tr,N} | |
unsafe_execute!(p, xc, y) | |
end | |
return y | |
end | |
end | |
end | |
""" | |
plan_rfft(A [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Pre-plan an optimized real-input FFT, similar to [`plan_fft`](:func:`plan_fft`) except for | |
[`rfft`](:func:`rfft`) instead of [`fft`](:func:`fft`). The first two arguments, and the | |
size of the transformed result, are the same as for [`rfft`](:func:`rfft`). | |
""" | |
plan_rfft | |
""" | |
plan_brfft(A, d [, dims]; flags=FFTW.ESTIMATE; timelimit=Inf) | |
Pre-plan an optimized real-input unnormalized transform, similar to | |
[`plan_rfft`](:func:`plan_rfft`) except for [`brfft`](:func:`brfft`) instead of | |
[`rfft`](:func:`rfft`). The first two arguments and the size of the transformed result, are | |
the same as for [`brfft`](:func:`brfft`). | |
""" | |
plan_brfft | |
# FFTW r2r transforms (low-level interface) | |
for f in (:r2r, :r2r!) | |
pf = Symbol("plan_", f) | |
@eval begin | |
$f{T<:fftwNumber}(x::AbstractArray{T}, kinds) = $pf(x, kinds) * x | |
$f{T<:fftwNumber}(x::AbstractArray{T}, kinds, region) = $pf(x, kinds, region) * x | |
$pf(x::AbstractArray, kinds; kws...) = $pf(x, kinds, 1:ndims(x); kws...) | |
$f{T<:Real}(x::AbstractArray{T}, kinds, region=1:ndims(x)) = $f(fftwfloat(x), kinds, region) | |
$pf{T<:Real}(x::AbstractArray{T}, kinds, region; kws...) = $pf(fftwfloat(x), kinds, region; kws...) | |
$f{T<:Complex}(x::AbstractArray{T}, kinds, region=1:ndims(x)) = $f(fftwcomplex(x), kinds, region) | |
$pf{T<:Complex}(x::AbstractArray{T}, kinds, region; kws...) = $pf(fftwcomplex(x), kinds, region; kws...) | |
end | |
end | |
function plan_r2r{T<:fftwNumber,N}(X::StridedArray{T,N}, kinds, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
r2rFFTWPlan{T,ANY,false,N}(X, fakesimilar(flags, X, T), region, kinds, | |
flags, timelimit) | |
end | |
function plan_r2r!{T<:fftwNumber,N}(X::StridedArray{T,N}, kinds, region; | |
flags::Integer=ESTIMATE, | |
timelimit::Real=NO_TIMELIMIT) | |
r2rFFTWPlan{T,ANY,true,N}(X, X, region, kinds, flags, timelimit) | |
end | |
""" | |
r2r(A, kind [, dims]) | |
Performs a multidimensional real-input/real-output (r2r) transform | |
of type `kind` of the array `A`, as defined in the FFTW manual. | |
`kind` specifies either a discrete cosine transform of various types | |
(`FFTW.REDFT00`, `FFTW.REDFT01`, `FFTW.REDFT10`, or | |
`FFTW.REDFT11`), a discrete sine transform of various types | |
(`FFTW.RODFT00`, `FFTW.RODFT01`, `FFTW.RODFT10`, or | |
`FFTW.RODFT11`), a real-input DFT with halfcomplex-format output | |
(`FFTW.R2HC` and its inverse `FFTW.HC2R`), or a discrete | |
Hartley transform (`FFTW.DHT`). The `kind` argument may be | |
an array or tuple in order to specify different transform types | |
along the different dimensions of `A`; `kind[end]` is used | |
for any unspecified dimensions. See the FFTW manual for precise | |
definitions of these transform types, at http://www.fftw.org/doc. | |
The optional `dims` argument specifies an iterable subset of | |
dimensions (e.g. an integer, range, tuple, or array) to transform | |
along. `kind[i]` is then the transform type for `dims[i]`, | |
with `kind[end]` being used for `i > length(kind)`. | |
See also [`plan_r2r`](:func:`plan_r2r`) to pre-plan optimized r2r transforms. | |
""" | |
FFTW.r2r | |
""" | |
r2r!(A, kind [, dims]) | |
Same as [`r2r`](:func:`r2r`), but operates in-place on `A`, which must be | |
an array of real or complex floating-point numbers. | |
""" | |
FFTW.r2r! | |
""" | |
plan_r2r!(A, kind [, dims [, flags [, timelimit]]]) | |
Similar to [`plan_fft`](:func:`Base.plan_fft`), but corresponds to [`r2r!`](:func:`r2r!`). | |
""" | |
FFTW.plan_r2r! | |
""" | |
plan_r2r(A, kind [, dims [, flags [, timelimit]]]) | |
Pre-plan an optimized r2r transform, similar to [`plan_fft`](:func:`Base.plan_fft`) | |
except that the transforms (and the first three arguments) | |
correspond to [`r2r`](:func:`r2r`) and [`r2r!`](:func:`r2r!`), respectively. | |
""" | |
FFTW.plan_r2r | |
# mapping from r2r kind to the corresponding inverse transform | |
const inv_kind = Dict{Int,Int}(R2HC => HC2R, HC2R => R2HC, DHT => DHT, | |
REDFT00 => REDFT00, | |
REDFT01 => REDFT10, REDFT10 => REDFT01, | |
REDFT11 => REDFT11, | |
RODFT00 => RODFT00, | |
RODFT01 => RODFT10, RODFT10 => RODFT01, | |
RODFT11 => RODFT11) | |
# r2r inverses are normalized to 1/N, where N is a "logical" size | |
# the transform with length n and kind k: | |
function logical_size(n::Integer, k::Integer) | |
k <= DHT && return n | |
k == REDFT00 && return 2(n-1) | |
k == RODFT00 && return 2(n+1) | |
return 2n | |
end | |
function plan_inv{T<:fftwNumber,K,inplace,N}(p::r2rFFTWPlan{T,K,inplace,N}) | |
X = Array{T}(p.sz) | |
iK = fix_kinds(p.region, [inv_kind[k] for k in K]) | |
Y = inplace ? X : fakesimilar(p.flags, X, T) | |
ScaledPlan(r2rFFTWPlan{T,ANY,inplace,N}(X, Y, p.region, iK, | |
p.flags, NO_TIMELIMIT), | |
normalization(real(T), | |
map(logical_size, [p.sz...][[p.region...]], iK), | |
1:length(iK))) | |
end | |
function A_mul_B!{T}(y::StridedArray{T}, p::r2rFFTWPlan{T}, x::StridedArray{T}) | |
assert_applicable(p, x, y) | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function *{T,K,N}(p::r2rFFTWPlan{T,K,false}, x::StridedArray{T,N}) | |
assert_applicable(p, x) | |
y = Array{T}(p.osz)::Array{T,N} | |
unsafe_execute!(p, x, y) | |
return y | |
end | |
function *{T,K}(p::r2rFFTWPlan{T,K,true}, x::StridedArray{T}) | |
assert_applicable(p, x) | |
unsafe_execute!(p, x, x) | |
return x | |
end | |
include("dct.jl") | |
end # module | |
# This file is a part of Julia. License is MIT: http://julialang.org/license | |
# Operations with the file system (paths) ## | |
export | |
cd, | |
chmod, | |
chown, | |
cp, | |
cptree, | |
mkdir, | |
mkpath, | |
mktemp, | |
mktempdir, | |
mv, | |
pwd, | |
rename, | |
readlink, | |
readdir, | |
rm, | |
samefile, | |
sendfile, | |
symlink, | |
tempdir, | |
tempname, | |
touch, | |
walkdir | |
# get and set current directory | |
""" | |
pwd() -> AbstractString | |
Get the current working directory. | |
""" | |
function pwd() | |
b = Array{UInt8}(1024) | |
len = Ref{Csize_t}(length(b)) | |
uv_error(:getcwd, ccall(:uv_cwd, Cint, (Ptr{UInt8}, Ptr{Csize_t}), b, len)) | |
String(b[1:len[]]) | |
end | |
""" | |
cd(dir::AbstractString=homedir()) | |
Set the current working directory. | |
""" | |
function cd(dir::AbstractString) | |
uv_error("chdir $dir", ccall(:uv_chdir, Cint, (Cstring,), dir)) | |
end | |
cd() = cd(homedir()) | |
if is_windows() | |
function cd(f::Function, dir::AbstractString) | |
old = pwd() | |
try | |
cd(dir) | |
f() | |
finally | |
cd(old) | |
end | |
end | |
else | |
function cd(f::Function, dir::AbstractString) | |
fd = ccall(:open, Int32, (Cstring, Int32), :., 0) | |
systemerror(:open, fd == -1) | |
try | |
cd(dir) | |
f() | |
finally | |
systemerror(:fchdir, ccall(:fchdir, Int32, (Int32,), fd) != 0) | |
systemerror(:close, ccall(:close, Int32, (Int32,), fd) != 0) | |
end | |
end | |
end | |
""" | |
cd(f::Function, dir::AbstractString=homedir()) | |
Temporarily changes the current working directory and applies function `f` before returning. | |
""" | |
cd(f::Function) = cd(f, homedir()) | |
""" | |
mkdir(path::AbstractString, mode::Unsigned=0o777) | |
Make a new directory with name `path` and permissions `mode`. `mode` defaults to `0o777`, | |
modified by the current file creation mask. | |
""" | |
function mkdir(path::AbstractString, mode::Unsigned=0o777) | |
@static if is_windows() | |
ret = ccall(:_wmkdir, Int32, (Cwstring,), path) | |
else | |
ret = ccall(:mkdir, Int32, (Cstring, UInt32), path, mode) | |
end | |
systemerror(:mkdir, ret != 0; extrainfo=path) | |
end | |
""" | |
mkpath(path::AbstractString, mode::Unsigned=0o777) | |
Create all directories in the given `path`, with permissions `mode`. `mode` defaults to | |
`0o777`, modified by the current file creation mask. | |
""" | |
function mkpath(path::AbstractString, mode::Unsigned=0o777) | |
isdirpath(path) && (path = dirname(path)) | |
dir = dirname(path) | |
(path == dir || isdir(path)) && return | |
mkpath(dir, mode) | |
try | |
mkdir(path, mode) | |
# If there is a problem with making the directory, but the directory | |
# does in fact exist, then ignore the error. Else re-throw it. | |
catch err | |
if isa(err, SystemError) && isdir(path) | |
return | |
else | |
rethrow() | |
end | |
end | |
end | |
mkdir(path::AbstractString, mode::Signed) = throw(ArgumentError("mode must be an unsigned integer; try 0o$mode")) | |
mkpath(path::AbstractString, mode::Signed) = throw(ArgumentError("mode must be an unsigned integer; try 0o$mode")) | |
""" | |
rm(path::AbstractString; force::Bool=false, recursive::Bool=false) | |
Delete the file, link, or empty directory at the given path. If `force=true` is passed, a | |
non-existing path is not treated as error. If `recursive=true` is passed and the path is a | |
directory, then all contents are removed recursively. | |
""" | |
function rm(path::AbstractString; force::Bool=false, recursive::Bool=false) | |
if islink(path) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment