Skip to content

Commit

Permalink
Merge pull request #29 from felixrehren/master
Browse files Browse the repository at this point in the history
RFC: syntax update
  • Loading branch information
shashi authored Jan 26, 2017
2 parents 4feaf2d + 33b91fc commit 6a6b6cd
Show file tree
Hide file tree
Showing 19 changed files with 765 additions and 797 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ os:
- linux
- osx
julia:
- 0.4
- 0.5
- nightly
notifications:
Expand Down
4 changes: 1 addition & 3 deletions REQUIRE
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
julia 0.4
Compat 0.9.0
Benchmark
julia 0.5
245 changes: 244 additions & 1 deletion src/BitmappedVectorTrie.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# implements Tries

# `shiftby` is equal to the number of bits required to represent index information
# for one level of the BitmappedTrie.
#
Expand All @@ -19,7 +21,7 @@ end
# Copies elements from one Array to another of size `len`.
#
copy_to_len{T}(from::Array{T}, len::Int) =
copy_to(from, Array(T, len), min(len, length(from)))
copy_to(from, Array{T,1}(len), min(len, length(from)))

mask(t::BitmappedTrie, i::Int) = (((i - 1) >>> shift(t)) & (trielen - 1)) + 1

Expand All @@ -42,3 +44,244 @@ function Base.isequal(t1::BitmappedTrie, t2::BitmappedTrie)
end

==(t1::BitmappedTrie, t2::BitmappedTrie) = isequal(t1, t2)


# Dense Bitmapped Tries
# =====================

abstract DenseBitmappedTrie{T} <: BitmappedTrie{T}

# Why is the shift value of a DenseLeaf 5 instead of 0, and why does
# the shift value of a DenseNode start at 10?
#
# The PersistentVector implements a "tail" optimization, where it
# inserts appended elements into a tail array until that array is 32
# elements long, only then inserting it into the actual bitmapped
# vector trie. This significantly increases the performance of
# operations that touch the very end of the vector (last, append, pop,
# etc.) because you don't have to traverse the trie.
#
# However, it adds a small amount of complexity to the implementation;
# when you query the trie, you now get back a length-32 array instead
# of the actual element. This is why the DenseLeaf has a shift value
# of 5: it leaves an "extra" 5 bits for the PersistentVector to use to
# index into the array returned from the trie. (This also means that a
# DenseNode has to start at shiftby*2.)

immutable DenseNode{T} <: DenseBitmappedTrie{T}
arr::Vector{DenseBitmappedTrie{T}}
shift::Int
length::Int
maxlength::Int
end

immutable DenseLeaf{T} <: DenseBitmappedTrie{T}
arr::Vector{T}

DenseLeaf(arr::Vector) = new(arr)
DenseLeaf() = new(T[])
end

arrayof( node::DenseNode) = node.arr
shift( node::DenseNode) = node.shift
maxlength( node::DenseNode) = node.maxlength
Base.length(node::DenseNode) = node.length

arrayof( leaf::DenseLeaf) = leaf.arr
shift( ::DenseLeaf) = shiftby
maxlength( leaf::DenseLeaf) = trielen
Base.length(leaf::DenseLeaf) = length(arrayof(leaf))

function promoted{T}(node::DenseBitmappedTrie{T})
DenseNode{T}(DenseBitmappedTrie{T}[node],
shift(node) + shiftby,
length(node),
maxlength(node) * trielen)
end

function demoted{T}(node::DenseNode{T})
if shift(node) == shiftby * 2
DenseLeaf{T}(T[])
else
DenseNode{T}(DenseBitmappedTrie{T}[],
shift(node) - shiftby,
0,
round(Int, maxlength(node) / trielen))
end
end

function witharr{T}(node::DenseNode{T}, arr::Array, lenshift::Int=0)
DenseNode{T}(arr, shift(node), length(node) + lenshift, maxlength(node))
end
witharr{T}(leaf::DenseLeaf{T}, arr::Array) = DenseLeaf{T}(arr)

function append(leaf::DenseLeaf, el)
if length(leaf) < maxlength(leaf)
newarr = copy_to_len(arrayof(leaf), 1 + length(leaf))
newarr[end] = el
witharr(leaf, newarr)
else
append(promoted(leaf), el)
end
end
function append{T}(node::DenseNode{T}, el)
if length(node) == 0
child = append(demoted(node), el)
witharr(node, DenseBitmappedTrie{T}[child], 1)
elseif length(node) < maxlength(node)
if length(arrayof(node)[end]) == maxlength(arrayof(node)[end])
newarr = copy_to_len(arrayof(node), 1 + length(arrayof(node)))
newarr[end] = append(demoted(node), el)
witharr(node, newarr, 1)
else
newarr = arrayof(node)[:]
newarr[end] = append(newarr[end], el)
witharr(node, newarr, 1)
end
else
append(promoted(node), el)
end
end
push(leaf::DenseLeaf, el) = append(leaf, el)
push(node::DenseNode, el) = append(node, el)

Base.getindex(leaf::DenseLeaf, i::Int) = arrayof(leaf)[mask(leaf, i)]
Base.getindex(node::DenseNode, i::Int) = arrayof(node)[mask(node, i)][i]

function assoc{T}(leaf::DenseLeaf{T}, i::Int, el)
newarr = arrayof(leaf)[:]
newarr[mask(leaf, i)] = el
DenseLeaf{T}(newarr)
end
function assoc(node::DenseNode, i::Int, el)
newarr = arrayof(node)[:]
idx = mask(node, i)
newarr[idx] = assoc(newarr[idx], i, el)
witharr(node, newarr)
end

peek(bt::DenseBitmappedTrie) = bt[end]

# Pop is usually destructive, but that doesn't make sense for an immutable
# structure, so `pop` is defined to return a Trie without its last
# element. Use `peek` to access the last element.
#
pop(leaf::DenseLeaf) = witharr(leaf, arrayof(leaf)[1:end-1])
function pop(node::DenseNode)
newarr = arrayof(node)[:]
newarr[end] = pop(newarr[end])
witharr(node, newarr, -1)
end

# Sparse Bitmapped Tries
# ======================

abstract SparseBitmappedTrie{T} <: BitmappedTrie{T}

immutable SparseNode{T} <: SparseBitmappedTrie{T}
arr::Vector{SparseBitmappedTrie{T}}
shift::Int
length::Int
maxlength::Int
bitmap::Int
end
SparseNode(T::Type) = SparseNode{T}(SparseBitmappedTrie{T}[], shiftby*7, 0, trielen^7, 0)

immutable SparseLeaf{T} <: SparseBitmappedTrie{T}
arr::Vector{T}
bitmap::Int

SparseLeaf(arr::Vector, bitmap::Int) = new(arr, bitmap)
SparseLeaf() = new(T[], 0)
end

arrayof( n::SparseNode) = n.arr
shift( n::SparseNode) = n.shift
maxlength( n::SparseNode) = n.maxlength
Base.length(n::SparseNode) = n.length

arrayof( l::SparseLeaf) = l.arr
shift( ::SparseLeaf) = 0
maxlength( l::SparseLeaf) = trielen
Base.length(l::SparseLeaf) = length(arrayof(l))

function demoted{T}(n::SparseNode{T})
shift(n) == shiftby ?
SparseLeaf{T}(T[], 0) :
SparseNode{T}(SparseBitmappedTrie{T}[],
shift(n) - shiftby,
0,
round(Int, maxlength(n) / trielen), 0)
end

bitpos( t::SparseBitmappedTrie, i::Int) = 1 << (mask(t, i) - 1)
hasindex(t::SparseBitmappedTrie, i::Int) = t.bitmap & bitpos(t, i) != 0
index( t::SparseBitmappedTrie, i::Int) =
1 + count_ones(t.bitmap & (bitpos(t, i) - 1))

function update{T}(l::SparseLeaf{T}, i::Int, el::T)
hasi = hasindex(l, i)
bitmap = bitpos(l, i) | l.bitmap
idx = index(l, i)
if hasi
newarr = arrayof(l)[:]
newarr[idx] = el
else
newarr = vcat(arrayof(l)[1:idx-1], [el], arrayof(l)[idx:end])
end
(SparseLeaf{T}(newarr, bitmap), !hasi)
end
function update{T}(n::SparseNode{T}, i::Int, el::T)
bitmap = bitpos(n, i) | n.bitmap
idx = index(n, i)
if hasindex(n, i)
newarr = arrayof(n)[:]
updated, inc = update(newarr[idx], i, el)
newarr[idx] = updated
else
child, inc = update(demoted(n), i, el)
newarr = vcat(arrayof(n)[1:idx-1], [child], arrayof(n)[idx:end])
end
(SparseNode{T}(newarr,
n.shift,
inc ? n.length + 1 : n.length,
n.maxlength, bitmap),
inc)
end

Base.get(n::SparseLeaf, i::Int, default) =
hasindex(n, i) ? arrayof(n)[index(n, i)] : default
Base.get(n::SparseNode, i::Int, default) =
hasindex(n, i) ? get(arrayof(n)[index(n, i)], i, default) : default

function Base.start(t::SparseBitmappedTrie)
t.length == 0 && return []
ones(Int, 1 + round(Int, t.shift / shiftby)) # state
end

function directindex(t::SparseBitmappedTrie, v::Vector{Int})
isempty(v) && return arrayof(t)
local node = arrayof(t)
for i=v
node = node[i]
node = isa(node, SparseBitmappedTrie) ? arrayof(node) : node
end
node
end

Base.done(t::SparseBitmappedTrie, state) = isempty(state)

function Base.next(t::SparseBitmappedTrie, state::Vector{Int})
item = directindex(t, state)
while true
index = pop!(state)
node = directindex(t, state)
if length(node) > index
push!(state, index + 1)
return item, vcat(state, ones(Int, 1 + round(Int, t.shift / shiftby) -
length(state)))
elseif node === arrayof(t)
return item, []
end
end
end
9 changes: 3 additions & 6 deletions src/FunctionalCollections.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module FunctionalCollections

import Base.==
using Compat

include("BitmappedVectorTrie.jl")

Expand All @@ -16,8 +15,6 @@ export PersistentVector, pvec,
pop

include("PersistentMap.jl")
include("PersistentArrayMap.jl")
include("PersistentHashMap.jl")

typealias phmap PersistentHashMap

Expand Down Expand Up @@ -61,11 +58,11 @@ end
macro Persistent(ex)
hd = ex.head

if is(hd, :vcat) || is(hd, :cell1d) || is(hd, :vect)
if (hd === :vcat) || (hd === :cell1d) || (hd === :vect)
fromexpr(ex, pvec)
elseif is(hd, :call) && is(ex.args[1], :Set)
elseif (hd === :call) && (ex.args[1] === :Set)
fromexpr(ex, pset)
elseif is(hd, :dict) || (is(hd, :call) && is(ex.args[1], :Dict))
elseif (hd === :dict) || ((hd === :call) && (ex.args[1] === :Dict))
fromexpr(ex, phmap)
else
error("Unsupported @Persistent syntax")
Expand Down
62 changes: 0 additions & 62 deletions src/PersistentArrayMap.jl

This file was deleted.

Loading

0 comments on commit 6a6b6cd

Please sign in to comment.