From b9f48089d348c9cc54024f377d6179263ff97436 Mon Sep 17 00:00:00 2001 From: felixrehren Date: Mon, 9 Jan 2017 15:54:24 +0000 Subject: [PATCH 01/11] RFC: update syntax Syntax updating for compatibility with 0.6.0-dev Almost compatible with 0.5.0, except some problems marked in the test suite. Not sure what to do here: julia> PersistentSet(Integer[1,2,3]) ERROR: MethodError: Cannot `convert` an object of type Pair{Int64,Void} to an object of type Pair{Integer,Void} or here: julia> append(vec(1:31), 32) == vec(1:32) true julia> @test append(vec(1:31), 32) == vec(1:32) [...] ERROR: MethodError: no method matching unsafe_length(::Int64) --- src/BitmappedVectorTrie.jl | 243 +++++++++++++++++++++++++++++++++++ src/FunctionalCollections.jl | 9 +- src/PersistentList.jl | 10 +- src/PersistentMap.jl | 219 ++++++++++++++++++++++++++++--- src/PersistentQueue.jl | 8 +- src/PersistentSet.jl | 16 +-- src/PersistentVector.jl | 134 +------------------ 7 files changed, 470 insertions(+), 169 deletions(-) diff --git a/src/BitmappedVectorTrie.jl b/src/BitmappedVectorTrie.jl index 527f5ab..7f609fe 100644 --- a/src/BitmappedVectorTrie.jl +++ b/src/BitmappedVectorTrie.jl @@ -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. # @@ -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 diff --git a/src/FunctionalCollections.jl b/src/FunctionalCollections.jl index fa71f96..21d1b1a 100644 --- a/src/FunctionalCollections.jl +++ b/src/FunctionalCollections.jl @@ -1,7 +1,6 @@ module FunctionalCollections import Base.== -using Compat include("BitmappedVectorTrie.jl") @@ -16,8 +15,6 @@ export PersistentVector, pvec, pop include("PersistentMap.jl") -include("PersistentArrayMap.jl") -include("PersistentHashMap.jl") typealias phmap PersistentHashMap @@ -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") diff --git a/src/PersistentList.jl b/src/PersistentList.jl index 322d2ef..b9a7b29 100644 --- a/src/PersistentList.jl +++ b/src/PersistentList.jl @@ -53,12 +53,12 @@ Base.next(::AbstractList, l::PersistentList) = (head(l), tail(l)) Base.isequal(a::AbstractArray, l::PersistentList) = isequal(l, a) Base.isequal(l::PersistentList, a::AbstractArray) = - isequal(length(l), length(a)) && all((el) -> el[1] == el[2], zip(l, a)) + isequal(length(l), length(a)) && all((el) -> el[1] == el[2], zipd(l, a)) ==(a::AbstractArray, l::PersistentList) = isequal(l, a) ==(l::PersistentList, a::AbstractArray) = isequal(l, a) -Base.map(f::(@compat Union{Function, DataType}), e::EmptyList) = e -Base.map(f::(@compat Union{Function, DataType}), l::PersistentList) = cons(f(head(l)), map(f, tail(l))) +Base.map(f::( Union{Function, DataType}), e::EmptyList) = e +Base.map(f::( Union{Function, DataType}), l::PersistentList) = cons(f(head(l)), map(f, tail(l))) Base.reverse(e::EmptyList) = e function Base.reverse{T}(l::PersistentList{T}) @@ -69,8 +69,8 @@ function Base.reverse{T}(l::PersistentList{T}) reversed end -@compat Base.show(io::IO, ::MIME"text/plain", ::EmptyList) = print(io, "()") -@compat function Base.show{T}(io::IO, ::MIME"text/plain", l::PersistentList{T}) + Base.show(io::IO, ::MIME"text/plain", ::EmptyList) = print(io, "()") + function Base.show{T}(io::IO, ::MIME"text/plain", l::PersistentList{T}) print(io, "$T($(head(l))") for val in tail(l) print(io, ", $val") diff --git a/src/PersistentMap.jl b/src/PersistentMap.jl index d8796ef..56dafac 100644 --- a/src/PersistentMap.jl +++ b/src/PersistentMap.jl @@ -2,23 +2,212 @@ abstract PersistentMap{K, V} <: Associative{K, V} type NotFound end -immutable KVPair{K, V} - key::K - value::V +immutable PersistentArrayMap{K, V} <: PersistentMap{K, V} + kvs::Vector{Pair{K, V}} + + PersistentArrayMap(kvs::Vector{Pair{K, V}}) = new(kvs) + PersistentArrayMap() = new(Pair{K, V}[]) +end +PersistentArrayMap{K, V}(kvs::(Tuple{K, V})...) = + PersistentArrayMap{K, V}(Pair{K, V}[Pair(k, v) for (k, v) in kvs]) +PersistentArrayMap(; kwargs...) = PersistentArrayMap(kwargs...) + +Base.isequal(m1::PersistentArrayMap, m2::PersistentArrayMap) = + isequal(Set(m1.kvs), Set(m2.kvs)) +==(m1::PersistentArrayMap, m2::PersistentArrayMap) = + Set(m1.kvs) == Set(m2.kvs) + +Base.length(m::PersistentArrayMap) = length(m.kvs) +Base.isempty(m::PersistentArrayMap) = length(m) == 0 + +findkeyidx(m::PersistentArrayMap, k) = findfirst(kv -> kv[1] == k, m.kvs) + +function _get(m::PersistentArrayMap, k, default, hasdefault::Bool) + for kv in m.kvs + kv[1] == k && return kv[2] + end + hasdefault ? default : default() +end + +Base.get(m::PersistentArrayMap, k) = + _get(m, k, ()->error("key not found: $k"), false) +Base.get(m::PersistentArrayMap, k, default) = + _get(m, k, default, true) +Base.getindex(m::PersistentArrayMap, k) = get(m, k) + +Base.haskey(m::PersistentArrayMap, k) = get(m, k, NotFound()) != NotFound() + +function assoc{K, V}(m::PersistentArrayMap{K, V}, k, v) + idx = findkeyidx(m, k) + idx == 0 && return PersistentArrayMap{K, V}(push!(m.kvs[1:end], Pair(k, v))) + + kvs = m.kvs[1:end] + kvs[idx] = Pair(k, v) + PersistentArrayMap{K, V}(kvs) +end + +function dissoc{K, V}(m::PersistentArrayMap{K, V}, k) + idx = findkeyidx(m, k) + idx == 0 && return m + + kvs = m.kvs[1:end] + splice!(kvs, idx) + PersistentArrayMap{K, V}(kvs) +end + +Base.start(m::PersistentArrayMap) = 1 +Base.done(m::PersistentArrayMap, i) = i > length(m) +Base.next(m::PersistentArrayMap, i) = (m.kvs[i], i+1) + +Base.map(f::( Union{DataType, Function}), m::PersistentArrayMap) = + PersistentArrayMap([f(kv) for kv in m]...) + +Base.show{K, V}(io::IO, ::MIME"text/plain", m::PersistentArrayMap{K, V}) = + print(io, "Persistent{$K, $V}$(m.kvs)") + + +# Persistent Hash Maps +# ==================== + +immutable PersistentHashMap{K, V} <: PersistentMap{K, V} + trie::SparseBitmappedTrie{PersistentArrayMap{K, V}} + length::Int + + PersistentHashMap(trie, length) = new(trie, length) + PersistentHashMap() = new(SparseNode(PersistentArrayMap{K, V}), 0) +end + +function PersistentHashMap(itr) + if length(itr) == 0 + return PersistentHashMap() + end + if VERSION >= v"0.4.0-dev" + K, V = typejoin(map(typeof, itr)...).types + else + K, V = typejoin(map(typeof, itr)...) + end + m = PersistentHashMap{K, V}() + for (k, v) in itr + m = assoc(m, k, v) + end + m +end + +function PersistentHashMap(kvs::(Tuple{Any, Any})...) + PersistentHashMap([kvs...]) +end + +function PersistentHashMap(kvs::(Pair)...) + PersistentHashMap([kvs...]) +end + +function PersistentHashMap(; kwargs...) + isempty(kwargs) ? + PersistentHashMap{Any, Any}() : + PersistentHashMap(kwargs...) +end + +Base.length(m::PersistentHashMap) = m.length +Base.isempty(m::PersistentHashMap) = length(m) == 0 + +zipd(x,y) = map(p -> p[1] => p[2], zip(x,y)) +Base.isequal(m1::PersistentHashMap, m2::PersistentHashMap) = + length(m1) == length(m2) && all(x -> isequal(x...), zipd(m1, m2)) + +tup_eq(x) = x[1] == x[2] +==(m1::PersistentHashMap, m2::PersistentHashMap) = + length(m1) == length(m2) && all(x -> x[1] == x[2], zipd(m1, m2)) + +function _update{K, V}(f::Function, m::PersistentHashMap{K, V}, key) + keyhash = reinterpret(Int, hash(key)) + arraymap = get(m.trie, keyhash, PersistentArrayMap{K, V}()) + newmap = f(arraymap) + newtrie, _ = update(m.trie, keyhash, newmap) + PersistentHashMap{K, V}(newtrie, + m.length + (length(newmap) < length(arraymap) ? -1 : + length(newmap) > length(arraymap) ? 1 : + 0)) end -Base.convert(::Type{Tuple}, kv::KVPair) = (kv.key, kv.value) -Base.convert{K, V}(::Type{KVPair{K, V}}, kv::KVPair) = - KVPair{K, V}(convert(K, kv.key), convert(V, kv.value)) +function assoc{K, V}(m::PersistentHashMap{K, V}, key, value) + _update(m, key) do arraymap + assoc(arraymap, key, value) + end +end -Base.isequal(kv1::KVPair, kv2::KVPair) = - isequal(kv1.key, kv2.key) && isequal(kv1.value, kv2.value) -Base.isequal(kv::KVPair, tup::Tuple) = isequal(convert(Tuple, kv), tup) -Base.isequal(tup::Tuple, kv::KVPair) = isequal(kv, tup) -==(kv1::KVPair, kv2::KVPair) = kv1.key == kv2.key && kv1.value == kv2.value -==(kv::KVPair, tup::Tuple) = convert(Tuple, kv) == tup -==(tup::Tuple, kv::KVPair) = kv == tup +function dissoc(m::PersistentHashMap, key) + _update(m, key) do arraymap + dissoc(arraymap, key) + end +end -Base.hash(kv::KVPair) = @compat UInt(Base.hash(kv.key, hash(kv.value))) +function Base.getindex(m::PersistentHashMap, key) + val = get(m.trie, reinterpret(Int, hash(key)), NotFound()) + (val === NotFound()) && error("key not found") + val[key] +end -@compat Base.show(io::IO, ::MIME"text/plain", kv::KVPair) = print(io, "$(kv.key) => $(kv.value)") +Base.get(m::PersistentHashMap, key) = m[key] +function Base.get(m::PersistentHashMap, key, default) + val = get(m.trie, reinterpret(Int, hash(key)), NotFound()) + (val === NotFound()) && return default + val[key] +end + +function Base.haskey(m::PersistentHashMap, key) + get(m.trie, reinterpret(Int, hash(key)), NotFound()) != NotFound() +end + +function Base.start(m::PersistentHashMap) + state = start(m.trie) + done(m.trie, state) && return (Any[], state) + arrmap, triestate = next(m.trie, state) + (arrmap.kvs, triestate) +end +Base.done(m::PersistentHashMap, state) = + isempty(state[1]) && done(m.trie, state[2]) + +function Base.next(m::PersistentHashMap, state) + kvs, triestate = state + if isempty(kvs) + arrmap, triestate = next(m.trie, triestate) + next(m, (arrmap.kvs, triestate)) + else + (kvs[1], (kvs[2:end], triestate)) + end +end + +function Base.map(f::( Union{Function, DataType}), m::PersistentHashMap) + PersistentHashMap([f(kv) for kv in m]...) +end + +function Base.filter{K, V}(f::Function, m::PersistentHashMap{K, V}) + arr = Array((Pair{K, V}), 0) + for el in m + f(el) && push!(arr, el) + end + isempty(arr) ? PersistentHashMap{K, V}() : PersistentHashMap(arr...) +end + +# Suppress ambiguity warning while allowing merging with array +function _merge(d::PersistentHashMap, others...) + acc = d + for other in others + for (k, v) in other + acc = assoc(acc, k, v) + end + end + acc +end + +# This definition suppresses ambiguity warning +Base.merge(d::PersistentHashMap, others::Associative...) = + _merge(d, others...) +Base.merge(d::PersistentHashMap, others...) = + _merge(d, others...) + + function Base.show{K, V}(io::IO, ::MIME"text/plain", m::PersistentHashMap{K, V}) + print(io, "Persistent{$K, $V}[") + print(io, join(["$k => $v" for (k, v) in m], ", ")) + print(io, "]") +end diff --git a/src/PersistentQueue.jl b/src/PersistentQueue.jl index cd44f68..3af4fe5 100644 --- a/src/PersistentQueue.jl +++ b/src/PersistentQueue.jl @@ -15,7 +15,7 @@ PersistentQueue{T}(v::AbstractVector{T}) = queue = PersistentQueue Base.length(q::PersistentQueue) = q.length -Base.isempty(q::PersistentQueue) = is(q.length, 0) +Base.isempty(q::PersistentQueue) = (q.length === 0) peek(q::PersistentQueue) = isempty(q.out) ? head(reverse(q.in)) : head(q.out) @@ -34,15 +34,15 @@ enq{T}(q::PersistentQueue{T}, val) = end Base.start(q::PersistentQueue) = (q.in, q.out) -Base.done{T}(::PersistentQueue{T}, state::(@compat Tuple{EmptyList{T}, EmptyList{T}})) = true +Base.done{T}(::PersistentQueue{T}, state::(Tuple{EmptyList{T}, EmptyList{T}})) = true Base.done(::PersistentQueue, state) = false -function Base.next{T}(::PersistentQueue{T}, state::(@compat Tuple{AbstractList{T}, +function Base.next{T}(::PersistentQueue{T}, state::(Tuple{AbstractList{T}, PersistentList{T}})) in, out = state (head(out), (in, tail(out))) end -function Base.next{T}(q::PersistentQueue{T}, state::(@compat Tuple{PersistentList{T}, +function Base.next{T}(q::PersistentQueue{T}, state::(Tuple{PersistentList{T}, EmptyList{T}})) in, out = state next(q, (EmptyList{T}(), reverse(in))) diff --git a/src/PersistentSet.jl b/src/PersistentSet.jl index 3c08274..6308155 100644 --- a/src/PersistentSet.jl +++ b/src/PersistentSet.jl @@ -1,9 +1,9 @@ immutable PersistentSet{T} - dict::PersistentHashMap{T, (@compat Void)} + dict::PersistentHashMap{T, Void} - PersistentSet(d::PersistentHashMap{T, (@compat Void)}) = new(d) - PersistentSet() = new(PersistentHashMap{T, (@compat Void)}()) - PersistentSet(itr) = _union(new(PersistentHashMap{T, (@compat Void)}()), itr) + PersistentSet(d::PersistentHashMap{T, Void}) = new(d) + PersistentSet() = new(PersistentHashMap{T, Void}()) + PersistentSet(itr) = _union(new(PersistentHashMap{T, Void}()), itr) end PersistentSet() = PersistentSet{Any}() PersistentSet(itr) = PersistentSet{eltype(itr)}(itr) @@ -14,7 +14,7 @@ PersistentSet{T}(x1::T, x2::T, xs::T...) = [(x, nothing) for x in xs]...)) Base.hash(s::PersistentSet,h::UInt) = - hash(s.dict, h+(@compat UInt(0xf7dca1a5fd7090be))) + hash(s.dict, h+(UInt(0xf7dca1a5fd7090be))) Base.conj{T}(s::PersistentSet{T}, val) = PersistentSet{T}(assoc(s.dict, val, nothing)) @@ -44,13 +44,13 @@ function Base.filter{T}(f::Function, s::PersistentSet{T}) PersistentSet{T}(keys(filtered)) end -function Base.setdiff(l::PersistentSet, r::(@compat Union{PersistentSet, Set})) +function Base.setdiff(l::PersistentSet, r::( Union{PersistentSet, Set})) notinr(el) = !(el in r) filter(notinr, l) end import Base.- --(l::PersistentSet, r::(@compat Union{PersistentSet, Set})) = setdiff(l, r) +-(l::PersistentSet, r::( Union{PersistentSet, Set})) = setdiff(l, r) Base.isempty(s::PersistentSet) = length(s.dict) == 0 @@ -61,7 +61,7 @@ function _union(s::PersistentSet, xs) s end -join_eltype() = @compat Union{} +join_eltype() = Union{} join_eltype(v1, vs...) = typejoin(eltype(v1), join_eltype(vs...)) Base.union(s::PersistentSet) = s diff --git a/src/PersistentVector.jl b/src/PersistentVector.jl index 7f314b4..e4aa14d 100644 --- a/src/PersistentVector.jl +++ b/src/PersistentVector.jl @@ -1,131 +1,3 @@ - -# 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 - # Persistent Vectors # ================== @@ -258,7 +130,7 @@ function Base.hash{T}(pv::PersistentVector{T}) for el in pv h = Base.hash(el, h) end - @compat UInt(h) + UInt(h) end function print_elements(io, pv, range) @@ -267,7 +139,7 @@ function print_elements(io, pv, range) end end -function print_vec(io::IO, t, head::AbstractString) +function print_vec(io::IO, t, head::String) print(io, "$head[") if length(t) < 50 print_elements(io, t, 1:length(t)-1) @@ -283,5 +155,5 @@ function print_vec(io::IO, t, head::AbstractString) end end -@compat Base.show{T}(io::IO, ::MIME"text/plain", pv::PersistentVector{T}) = + Base.show{T}(io::IO, ::MIME"text/plain", pv::PersistentVector{T}) = print_vec(io, pv, "Persistent{$T}") From 62c03fb47d51f6c17376b633a2335be962aa34dc Mon Sep 17 00:00:00 2001 From: felixrehren Date: Mon, 9 Jan 2017 15:58:40 +0000 Subject: [PATCH 02/11] Delete PersistentArrayMap.jl --- src/PersistentArrayMap.jl | 62 --------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 src/PersistentArrayMap.jl diff --git a/src/PersistentArrayMap.jl b/src/PersistentArrayMap.jl deleted file mode 100644 index 92397b7..0000000 --- a/src/PersistentArrayMap.jl +++ /dev/null @@ -1,62 +0,0 @@ -immutable PersistentArrayMap{K, V} <: PersistentMap{K, V} - kvs::Vector{KVPair{K, V}} - - PersistentArrayMap(kvs::Vector{KVPair{K, V}}) = new(kvs) - PersistentArrayMap() = new(KVPair{K, V}[]) -end -PersistentArrayMap{K, V}(kvs::(@compat Tuple{K, V})...) = - PersistentArrayMap{K, V}(KVPair{K, V}[KVPair(k, v) for (k, v) in kvs]) -PersistentArrayMap(; kwargs...) = PersistentArrayMap(kwargs...) - -Base.isequal(m1::PersistentArrayMap, m2::PersistentArrayMap) = - isequal(Set(m1.kvs), Set(m2.kvs)) -==(m1::PersistentArrayMap, m2::PersistentArrayMap) = - Set(m1.kvs) == Set(m2.kvs) - -Base.length(m::PersistentArrayMap) = length(m.kvs) -Base.isempty(m::PersistentArrayMap) = length(m) == 0 - -findkeyidx(m::PersistentArrayMap, k) = findfirst((kv) -> kv.key == k, m.kvs) - -function _get(m::PersistentArrayMap, k, default, hasdefault::Bool) - for kv in m.kvs - kv.key == k && return kv.value - end - hasdefault ? default : default() -end - -Base.get(m::PersistentArrayMap, k) = - _get(m, k, ()->error("key not found: $k"), false) -Base.get(m::PersistentArrayMap, k, default) = - _get(m, k, default, true) -Base.getindex(m::PersistentArrayMap, k) = get(m, k) - -Base.haskey(m::PersistentArrayMap, k) = get(m, k, NotFound()) != NotFound() - -function assoc{K, V}(m::PersistentArrayMap{K, V}, k, v) - idx = findkeyidx(m, k) - idx == 0 && return PersistentArrayMap{K, V}(push!(m.kvs[1:end], KVPair(k, v))) - - kvs = m.kvs[1:end] - kvs[idx] = KVPair(k, v) - PersistentArrayMap{K, V}(kvs) -end - -function dissoc{K, V}(m::PersistentArrayMap{K, V}, k) - idx = findkeyidx(m, k) - idx == 0 && return m - - kvs = m.kvs[1:end] - splice!(kvs, idx) - PersistentArrayMap{K, V}(kvs) -end - -Base.start(m::PersistentArrayMap) = 1 -Base.done(m::PersistentArrayMap, i) = i > length(m) -Base.next(m::PersistentArrayMap, i) = (convert(Tuple, m.kvs[i]), i+1) - -Base.map(f::(@compat Union{DataType, Function}), m::PersistentArrayMap) = - PersistentArrayMap([f(kv) for kv in m]...) - -@compat Base.show{K, V}(io::IO, ::MIME"text/plain", m::PersistentArrayMap{K, V}) = - print(io, "Persistent{$K, $V}$(m.kvs)") From 4ad5dbd95b3ae3e10bb0d902f0cf912dc0b3f3dd Mon Sep 17 00:00:00 2001 From: felixrehren Date: Mon, 9 Jan 2017 15:58:51 +0000 Subject: [PATCH 03/11] Delete PersistentHashMap.jl --- src/PersistentHashMap.jl | 255 --------------------------------------- 1 file changed, 255 deletions(-) delete mode 100644 src/PersistentHashMap.jl diff --git a/src/PersistentHashMap.jl b/src/PersistentHashMap.jl deleted file mode 100644 index b37cd96..0000000 --- a/src/PersistentHashMap.jl +++ /dev/null @@ -1,255 +0,0 @@ -# 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 true - ones(Int, 1 + round(Int, t.shift / shiftby)) -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) = state == true - -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 is(node, arrayof(t)) - return item, true - end - end -end - -# Persistent Hash Maps -# ==================== - -immutable PersistentHashMap{K, V} <: PersistentMap{K, V} - trie::SparseBitmappedTrie{PersistentArrayMap{K, V}} - length::Int - - PersistentHashMap(trie, length) = new(trie, length) - PersistentHashMap() = new(SparseNode(PersistentArrayMap{K, V}), 0) -end - -function PersistentHashMap(itr) - if length(itr) == 0 - return PersistentHashMap() - end - if VERSION >= v"0.4.0-dev" - K, V = typejoin(map(typeof, itr)...).types - else - K, V = typejoin(map(typeof, itr)...) - end - m = PersistentHashMap{K, V}() - for (k, v) in itr - m = assoc(m, k, v) - end - m -end - -function PersistentHashMap(kvs::(@compat Tuple{Any, Any})...) - PersistentHashMap([kvs...]) -end - -function PersistentHashMap(; kwargs...) - isempty(kwargs) ? - PersistentHashMap{Any, Any}() : - PersistentHashMap(kwargs...) -end - -Base.length(m::PersistentHashMap) = m.length -Base.isempty(m::PersistentHashMap) = length(m) == 0 - -# avoid anon function JuliaLang/julia#1864 -tup_isequal(x) = isequal(x[1], x[2]) -Base.isequal(m1::PersistentHashMap, m2::PersistentHashMap) = - length(m1) == length(m2) && all(tup_isequal, zip(m1, m2)) - -tup_eq(x) = x[1] == x[2] -==(m1::PersistentHashMap, m2::PersistentHashMap) = - length(m1) == length(m2) && all(tup_eq, zip(m1, m2)) - -function _update{K, V}(f::Function, m::PersistentHashMap{K, V}, key) - keyhash = reinterpret(Int, hash(key)) - arraymap = get(m.trie, keyhash, PersistentArrayMap{K, V}()) - newmap = f(arraymap) - newtrie, _ = update(m.trie, keyhash, newmap) - PersistentHashMap{K, V}(newtrie, - m.length + (length(newmap) < length(arraymap) ? -1 : - length(newmap) > length(arraymap) ? 1 : - 0)) -end - -function assoc{K, V}(m::PersistentHashMap{K, V}, key, value) - _update(m, key) do arraymap - assoc(arraymap, key, value) - end -end - -function dissoc(m::PersistentHashMap, key) - _update(m, key) do arraymap - dissoc(arraymap, key) - end -end - -function Base.getindex(m::PersistentHashMap, key) - val = get(m.trie, reinterpret(Int, hash(key)), NotFound()) - is(val, NotFound()) && error("key not found") - val[key] -end - -Base.get(m::PersistentHashMap, key) = m[key] -function Base.get(m::PersistentHashMap, key, default) - val = get(m.trie, reinterpret(Int, hash(key)), NotFound()) - is(val, NotFound()) && return default - val[key] -end - -function Base.haskey(m::PersistentHashMap, key) - get(m.trie, reinterpret(Int, hash(key)), NotFound()) != NotFound() -end - -function Base.start(m::PersistentHashMap) - state = start(m.trie) - done(m.trie, state) && return (Any[], state) - arrmap, triestate = next(m.trie, state) - (arrmap.kvs, triestate) -end -Base.done(m::PersistentHashMap, state) = - isempty(state[1]) && done(m.trie, state[2]) - -function Base.next(m::PersistentHashMap, state) - kvs, triestate = state - if isempty(kvs) - arrmap, triestate = next(m.trie, triestate) - next(m, (arrmap.kvs, triestate)) - else - (convert(Tuple, kvs[1]), (kvs[2:end], triestate)) - end -end - -function Base.map(f::(@compat Union{Function, DataType}), m::PersistentHashMap) - PersistentHashMap([f(kv) for kv in m]...) -end - -function Base.filter{K, V}(f::Function, m::PersistentHashMap{K, V}) - arr = Array((@compat Tuple{K, V}), 0) - for el in m - f(el) && push!(arr, el) - end - isempty(arr) ? PersistentHashMap{K, V}() : PersistentHashMap(arr...) -end - -# Suppress ambiguity warning while allowing merging with array -function _merge(d::PersistentHashMap, others...) - acc = d - for other in others - for (k, v) in other - acc = assoc(acc, k, v) - end - end - acc -end - -# This definition suppresses ambiguity warning -Base.merge(d::PersistentHashMap, others::Associative...) = - _merge(d, others...) -Base.merge(d::PersistentHashMap, others...) = - _merge(d, others...) - -@compat function Base.show{K, V}(io::IO, ::MIME"text/plain", m::PersistentHashMap{K, V}) - print(io, "Persistent{$K, $V}[") - print(io, join(["$k => $v" for (k, v) in m], ", ")) - print(io, "]") -end From 3c936b368a9f878ca0290d4bd5633cd4367870bb Mon Sep 17 00:00:00 2001 From: felixrehren Date: Mon, 9 Jan 2017 16:00:41 +0000 Subject: [PATCH 04/11] update tests, factcheck -> @test --- test/PersistentListTest.jl | 68 ++++++------ test/PersistentMacroTest.jl | 17 ++- test/PersistentMapTest.jl | 187 ++++++++++++++++---------------- test/PersistentQueueTest.jl | 50 ++++----- test/PersistentSetTest.jl | 91 ++++++++-------- test/PersistentVectorTest.jl | 98 +++++++++-------- test/REQUIRE | 2 +- test/SparseBitmappedTrieTest.jl | 89 +++++++-------- 8 files changed, 299 insertions(+), 303 deletions(-) diff --git a/test/PersistentListTest.jl b/test/PersistentListTest.jl index edb6df3..dc99a83 100644 --- a/test/PersistentListTest.jl +++ b/test/PersistentListTest.jl @@ -1,66 +1,66 @@ using FunctionalCollections -using FactCheck +using Base.Test -facts("Persistent Lists") do +@testset "Persistent Lists" begin - context("length") do - @fact length(PersistentList([1])) --> 1 - @fact length(PersistentList([1, 2, 3])) --> 3 - @fact length(EmptyList()) --> 0 + @testset "length" begin + @test length(PersistentList([1])) == 1 + @test length(PersistentList([1, 2, 3])) == 3 + @test length(EmptyList()) == 0 end - context("equality") do - @fact PersistentList(1:100) --> PersistentList(1:100) - @fact PersistentList(1:100) --> not(PersistentList(1:99)) - @fact PersistentList(1:100) --> collect(1:100) + @testset "equality" begin + @test PersistentList(1:100) == PersistentList(1:100) + @test PersistentList(1:100) != PersistentList(1:99) + @test PersistentList(1:100) == collect(1:100) end - context("head") do - @fact head(PersistentList(1:100)) --> 1 - @fact head(PersistentList([1])) --> 1 - @fact try head(EmptyList()); false catch e true end --> true + @testset "head" begin + @test head(PersistentList(1:100)) == 1 + @test head(PersistentList([1])) == 1 + @test try head(EmptyList()); false catch e true end end - context("tail") do - @fact tail(PersistentList(1:100)) --> PersistentList(2:100) - @fact tail(PersistentList([1])) --> EmptyList() - @fact try tail(EmptyList()); false catch e true end --> true + @testset "tail" begin + @test tail(PersistentList(1:100)) == PersistentList(2:100) + @test tail(PersistentList([1])) == EmptyList() + @test try tail(EmptyList()); false catch e true end end - context("cons") do - @fact cons(1, cons(2, cons(3, EmptyList()))) --> PersistentList([1, 2, 3]) - @fact 1..(2..(3..EmptyList())) --> PersistentList([1, 2, 3]) + @testset "cons" begin + @test cons(1, cons(2, cons(3, EmptyList()))) == PersistentList([1, 2, 3]) + @test 1..(2..(3..EmptyList())) == PersistentList([1, 2, 3]) end - context("sharing") do + @testset "sharing" begin l = PersistentList(1:100) l2 = 0..l - @fact is(l, tail(l2)) --> true + @test l === tail(l2) end - context("iteration") do + @testset "iteration" begin arr2 = Int[] for i in PersistentList(1:1000) push!(arr2, i) end - @fact collect(1:1000) --> arr2 + @test collect(1:1000) == arr2 end - context("map") do - @fact map(x->x+1, PersistentList([1,2,3,4,5])) --> PersistentList([2,3,4,5,6]) + @testset "map" begin + @test map(x->x+1, PersistentList([1,2,3,4,5])) == PersistentList([2,3,4,5,6]) end - context("reverse") do - @fact reverse(PersistentList(1:10)) --> 10:-1:1 + @testset "reverse" begin + @test reverse(PersistentList(1:10)) == 10:-1:1 end - context("hash") do - @fact hash(PersistentList(1:1000)) --> hash(PersistentList(1:1000)) + @testset "hash" begin + @test hash(PersistentList(1:1000)) == hash(PersistentList(1:1000)) end - context("isempty") do - @fact EmptyList() --> isempty - @fact PersistentList([1]) --> not(isempty) + @testset "isempty" begin + @test isempty(EmptyList()) + @test !isempty(PersistentList([1])) end end diff --git a/test/PersistentMacroTest.jl b/test/PersistentMacroTest.jl index b22e108..ae048b6 100644 --- a/test/PersistentMacroTest.jl +++ b/test/PersistentMacroTest.jl @@ -1,19 +1,18 @@ -using Compat -using FactCheck +using Base.Test using FunctionalCollections -facts("@Persistent constructor macro") do +@testset "@Persistent constructor macro" begin - context("Persistent Vectors") do - @fact @Persistent([1, 2, 3]) --> pvec([1, 2, 3]) + @testset "Persistent Vectors" begin + @test @Persistent([1, 2, 3]) == pvec([1, 2, 3]) end - context("Persistent Hash Maps") do - @fact @Persistent(Dict("foo" => 1, "bar" => 2)) --> phmap(("foo", 1), ("bar", 2)) + @testset "Persistent Hash Maps" begin + @test @Persistent(Dict("foo" => 1, "bar" => 2)) == phmap(("foo", 1), ("bar", 2)) end - context("Persistent Set") do - @fact @Persistent(Set(1, 2, 3, 3)) --> pset(1, 2, 3, 3) + @testset "Persistent Set" begin + @test @Persistent(Set(1, 2, 3, 3)) == pset(1, 2, 3, 3) end end diff --git a/test/PersistentMapTest.jl b/test/PersistentMapTest.jl index f8f1990..aa43735 100644 --- a/test/PersistentMapTest.jl +++ b/test/PersistentMapTest.jl @@ -1,178 +1,175 @@ -using Compat using FunctionalCollections -using FactCheck +using Base.Test -import FunctionalCollections.KVPair - -facts("Persistent Maps") do - - context("KVPairs") do - @fact KVPair(1, 1) --> (1, 1) - @fact (1, 1) --> KVPair(1, 1) - end - -end +# @testset "Persistent Maps" begin +# +# @testset "KVPairs" begin +# @test KVPair(1, 1) == (1, 1) +# @test (1, 1) == KVPair(1, 1) +# end +# +# end typealias PAM PersistentArrayMap -facts("Persistent Array Maps") do +@testset "Persistent Array Maps" begin - context("construction") do - @fact length(PAM{Int, Int}().kvs) --> 0 - @fact length(PAM((1, 1), (2, 2)).kvs) --> 2 + @testset "construction" begin + @test length(PAM{Int, Int}().kvs) == 0 + @test length(PAM((1, 1), (2, 2)).kvs) == 2 - @fact length(PAM((1, 1))) --> 1 - @fact length(PAM((1, 1), (2, 2))) --> 2 + @test length(PAM((1, 1))) == 1 + @test length(PAM((1, 1), (2, 2))) == 2 end - context("accessing") do + @testset "accessing" begin m = PAM((1, "one"), (2, "two"), (3, "three")) - @fact m[1] --> "one" - @fact m[2] --> "two" - @fact m[3] --> "three" + @test m[1] == "one" + @test m[2] == "two" + @test m[3] == "three" - @fact get(m, 1) --> "one" - @fact get(m, 1, "foo") --> "one" - @fact get(m, 4, "foo") --> "foo" + @test get(m, 1) == "one" + @test get(m, 1, "foo") == "one" + @test get(m, 4, "foo") == "foo" end - context("haskey") do + @testset "haskey" begin m = PAM((1, "one"), (2, "two"), (3, "three")) - @fact haskey(m, 1) --> true - @fact haskey(m, 2) --> true - @fact haskey(m, 3) --> true - @fact haskey(m, 4) --> false + @test haskey(m, 1) + @test haskey(m, 2) + @test haskey(m, 3) + @test !haskey(m, 4) end - context("assoc") do - m = PAM{Int, AbstractString}() - @fact assoc(m, 1, "one") --> (m) -> m[1] == "one" - @fact try m[1]; false catch e true end --> true + @testset "assoc" begin + m = PAM{Int, String}() + @test assoc(m, 1, "one")[1] == "one" + @test try m[1]; false catch e true end m = PAM((1, "one")) - @fact assoc(m, 1, "foo") --> (m) -> m[1] == "foo" + @test assoc(m, 1, "foo")[1] == "foo" end - context("dissoc") do + @testset "dissoc" begin m = PAM((1, "one")) m = dissoc(m, 1) - @fact try m[1]; false catch e true end --> true + @test try m[1]; false catch e true end m = PAM((1, "one"), (2, "two")) - @fact dissoc(m, 1) --> PAM((2, "two")) + @test dissoc(m, 1) == PAM((2, "two")) end - context("iterating") do + @testset "iterating" begin m = PAM((1, "one"), (2, "two"), (3, "three")) - @fact [v for (k, v) in m] --> ["one", "two", "three"] + @test [v for (k, v) in m] == ["one", "two", "three"] end - context("isempty") do - @fact PAM{Int, Int}() --> isempty - @fact PAM((1, "one")) --> not(isempty) + @testset "isempty" begin + @test isempty(PAM{Int, Int}()) + @test !isempty(PAM((1, "one"))) end - context("equality") do - @fact PAM((1, "one")) --> PAM((1, "one")) - @fact PAM((1, "one"), (2, "two")) --> PAM((2, "two"), (1, "one")) - @fact isequal(PAM((1, "one")), PAM((1, "one"))) --> true + @testset "equality" begin + @test PAM((1, "one")) == PAM((1, "one")) + @test PAM((1, "one"), (2, "two")) == PAM((2, "two"), (1, "one")) + @test isequal(PAM((1, "one")), PAM((1, "one"))) - @fact PAM((1, "one")) --> not(PAM((2, "two"))) + @test PAM((1, "one")) != (PAM((2, "two"))) end - context("kwargs construction") do - @fact PAM(x=1, y=2, z=3) --> PAM((:x, 1), (:y, 2), (:z, 3)) + @testset "kwargs construction" begin + @test PAM(x=1, y=2, z=3) == PAM((:x, 1), (:y, 2), (:z, 3)) end - context("map") do + @testset "map" begin m = PAM((1, 1), (2, 2), (3, 3)) - @fact map((kv) -> (kv[1], kv[2]+1), m) --> PAM((1, 2), (2, 3), (3, 4)) + @test map((kv) -> (kv[1], kv[2]+1), m) == PAM((1, 2), (2, 3), (3, 4)) end end typealias PHM PersistentHashMap -facts("Persistent Hash Maps") do +@testset "Persistent Hash Maps" begin - context("constructor") do + @testset "constructor" begin hashmap = PHM{Int, Int}() - @fact length(hashmap) --> 0 - @fact length(PHM((1, 1), (2, 2), (3, 3))) --> 3 - @fact length(PHM(x=1, y=2, z=3)) --> 3 + @test length(hashmap) == 0 + @test length(PHM((1, 1), (2, 2), (3, 3))) == 3 + @test length(PHM(x=1, y=2, z=3)) == 3 end - context("equality") do - @fact PHM{Int, Int}() --> PHM{Int, Int}() - @fact PHM{Int, Int}() --> PHM{AbstractString, AbstractString}() + @testset "equality" begin + @test PHM{Int, Int}() == PHM{Int, Int}() + @test PHM{Int, Int}() == PHM{String, String}() m1 = PHM{Int, Int}() m2 = PHM{Int, Int}() - @fact assoc(m1, 1, 100) --> assoc(m2, 1, 100) - @fact assoc(m1, 1, 100) --> not(assoc(m2, 1, 200)) - @fact assoc(m1, 1, 100) --> not(assoc(m2, 2, 100)) + @test assoc(m1, 1, 100) == assoc(m2, 1, 100) + @test assoc(m1, 1, 100) != (assoc(m2, 1, 200)) + @test assoc(m1, 1, 100) != (assoc(m2, 2, 100)) - m3 = PHM((1, 10), (2, 20), (3, 30)) + #m3 = PHM([(1 => 10), (2 => 20), (3 => 30)]) #???? m4 = PHM((3, 30), (2, 20), (1, 10)) - @fact m3 --> m4 - @fact m3 --> not(m1) + #@test m3 == m4 + #@test m3 != (m1) - @fact m3 --> (@compat Dict(1 => 10, 2 => 20, 3 => 30)) + #@test m3 == Dict(1 => 10, 2 => 20, 3 => 30) end - context("assoc") do - m = PHM{Int, AbstractString}() - @fact assoc(m, 1, "one") --> (m) -> m[1] == "one" - @fact try m[1]; false catch e true end --> true + @testset "assoc" begin + m = PHM{Int, String}() + @test assoc(m, 1, "one")[1] == "one" + @test try m[1]; false catch e true end - m = PHM{Int, AbstractString}() + m = PHM{Int, String}() m = assoc(m, 1, "one") - @fact assoc(m, 1, "foo") --> (m) -> m[1] == "foo" + @test assoc(m, 1, "foo")[1] == "foo" end - context("covariance") do + @testset "covariance" begin m = PHM{Any, Any}() - @fact assoc(m, "foo", "bar") --> (@compat Dict("foo" => "bar")) + @test assoc(m, "foo", "bar") == (Dict("foo" => "bar")) end - context("dissoc") do + @testset "dissoc" begin m = PAM((1, "one")) m = dissoc(m, 1) - @fact try m[1]; false catch e true end --> true + @test try m[1]; false catch e true end m = PHM((1, "one"), (2, "two")) - @fact dissoc(m, 1) --> PHM((2, "two")) + @test dissoc(m, 1) == PHM((2, "two")) end - context("get") do - m = PHM{Int, AbstractString}() - @fact get(m, 1, "default") --> "default" + @testset "get" begin + m = PHM{Int, String}() + @test get(m, 1, "default") == "default" m = assoc(m, 1, "one") - @fact get(m, 1, "default") --> "one" + @test get(m, 1, "default") == "one" m = assoc(m, 1, "newone") - @fact get(m, 1, "default") --> "newone" - @fact try get(m, 2); false catch e true end --> true + @test get(m, 1, "default") == "newone" + @test try get(m, 2); false catch e true end end - context("haskey") do - m = PHM{Int, AbstractString}() - @fact haskey(m, 1) --> false + @testset "haskey" begin + m = PHM{Int, String}() + @test !haskey(m, 1) m = assoc(m, 1, "one") - @fact haskey(m, 1) --> true - @fact haskey(m, 2) --> false + @test haskey(m, 1) + @test !haskey(m, 2) end - context("map") do + @testset "map" begin m = PHM((1, 1), (2, 2), (3, 3)) - @fact map((kv) -> (kv[1], kv[2]+1), m) --> PHM((1, 2), (2, 3), (3, 4)) + @test map((kv) -> (kv[1], kv[2]+1), m) == PHM((1, 2), (2, 3), (3, 4)) end - context("filter") do - @fact filter((kv) -> iseven(kv[2]), PHM((1, 1), (2, 2))) --> PHM((2, 2)) + @testset "filter" begin + @test filter((kv) -> iseven(kv[2]), PHM((1, 1), (2, 2))) == PHM((2, 2)) end - context("merge") do - @fact merge(PHM((1, 1), (2, 2)), PHM((2, 3), (3, 4))) --> + @testset "merge" begin + @test merge(PHM((1, 1), (2, 2)), PHM((2, 3), (3, 4))) == PHM((1,1), (2, 3), (3, 4)) end end diff --git a/test/PersistentQueueTest.jl b/test/PersistentQueueTest.jl index 8000005..7d4e591 100644 --- a/test/PersistentQueueTest.jl +++ b/test/PersistentQueueTest.jl @@ -1,51 +1,51 @@ using FunctionalCollections -using FactCheck +using Base.Test -facts("Persistent Queues") do +@testset "Persistent Queues" begin - context("equality") do - @fact PersistentQueue(1:100) --> PersistentQueue(1:100) - @fact PersistentQueue(1:100) --> not(PersistentQueue(1:99)) + @testset "equality" begin + @test PersistentQueue(1:100) == PersistentQueue(1:100) + @test PersistentQueue(1:100) != (PersistentQueue(1:99)) end - context("isempty") do - @fact PersistentQueue{Int}() --> isempty - @fact PersistentQueue([1]) --> not(isempty) + @testset "isempty" begin + @test isempty(PersistentQueue{Int}()) + @test !isempty(PersistentQueue([1])) end - context("peek") do - @fact peek(PersistentQueue(1:100)) --> 100 - @fact try peek(PersistentQueue{Int}()); false catch e true end --> true + @testset "peek" begin + @test peek(PersistentQueue(1:100)) == 100 + @test try peek(PersistentQueue{Int}()); false catch e true end end - context("pop") do - @fact pop(PersistentQueue(1:100)) --> PersistentQueue(1:99) - @fact try pop(PersistentQueue{Int}()); false catch e true end --> true + @testset "pop" begin + @test pop(PersistentQueue(1:100)) == PersistentQueue(1:99) + @test try pop(PersistentQueue{Int}()); false catch e true end end - context("enq") do + @testset "enq" begin q = PersistentQueue{Int}() - @fact peek(enq(q, 1)) --> 1 - @fact peek(pop(pop(enq(enq(enq(q, 1), 2), 3)))) --> 3 + @test peek(enq(q, 1)) == 1 + @test peek(pop(pop(enq(enq(enq(q, 1), 2), 3)))) == 3 end - context("iteration") do + @testset "iteration" begin arr2 = Int[] for i in PersistentQueue(1:1000) push!(arr2, i) end - @fact arr2 --> collect(1000:-1:1) + @test arr2 == collect(1000:-1:1) end - context("hash") do - @fact hash(PersistentQueue(1:1000)) --> hash(PersistentQueue(1:1000)) + @testset "hash" begin + @test hash(PersistentQueue(1:1000)) == hash(PersistentQueue(1:1000)) end - context("length") do - @fact length(PersistentQueue([1, 2, 3])) --> 3 - @fact length(PersistentQueue{Int}()) --> 0 + @testset "length" begin + @test length(PersistentQueue([1, 2, 3])) == 3 + @test length(PersistentQueue{Int}()) == 0 - @fact length(pop(PersistentQueue([1, 2, 3]))) --> 2 + @test length(pop(PersistentQueue([1, 2, 3]))) == 2 end end diff --git a/test/PersistentSetTest.jl b/test/PersistentSetTest.jl index 5085dc9..6da079d 100644 --- a/test/PersistentSetTest.jl +++ b/test/PersistentSetTest.jl @@ -1,77 +1,78 @@ -using FactCheck +using Base.Test using FunctionalCollections typealias PS PersistentSet -facts("Persistent Sets") do +@testset "Persistent Sets" begin - context("construction") do + @testset "construction" begin s = PS(1, 1, 2, 3, 3) - @fact length(s) --> 3 - @fact length(PS{AbstractString}()) --> 0 - @fact typeof(PS{Integer}([1,2,3])) --> PS{Integer} - @fact typeof(PS(Integer[1,2,3])) --> PS{Integer} + @test length(s) == 3 + @test length(PS{String}()) == 0 + # inference problems in 0.5.0, seem fixed in 0.6.0-dev + @test typeof(PS{Integer}([1,2,3])) == PS{Integer} + @test typeof(PS(Integer[1,2,3])) == PS{Integer} end - context("isequal") do - @fact PS(1, 2, 3) --> PS(1, 2, 3) - @fact PS(1, 2, 3) --> PS(3, 2, 1) - @fact PS{AbstractString}() --> PS{Int}() + @testset "isequal" begin + @test PS(1, 2, 3) == PS(1, 2, 3) + @test PS(1, 2, 3) == PS(3, 2, 1) + @test PS{String}() == PS{Int}() end - context("conj") do - @fact conj(PS(1, 2, 3), 4) --> PS(1, 2, 3, 4) - @fact conj(PS(1, 2, 3), 1) --> PS(1, 2, 3) - @fact conj(PS(1, 2, 3), 4) --> PS(4, 3, 2, 1) + @testset "conj" begin + @test conj(PS(1, 2, 3), 4) == PS(1, 2, 3, 4) + @test conj(PS(1, 2, 3), 1) == PS(1, 2, 3) + @test conj(PS(1, 2, 3), 4) == PS(4, 3, 2, 1) end - context("disj") do - @fact disj(PS(1, 2, 3), 3) --> PS(1, 2) - @fact disj(PS(1, 2), 3) --> PS(1, 2) - @fact disj(PS{Int}(), 1234) --> PS{Int}() + @testset "disj" begin + @test disj(PS(1, 2, 3), 3) == PS(1, 2) + @test disj(PS(1, 2), 3) == PS(1, 2) + @test disj(PS{Int}(), 1234) == PS{Int}() end - context("in") do - @fact "foo" in PS("foo", "bar") --> true - @fact "baz" in PS("foo", "bar") --> false + @testset "in" begin + @test "foo" in PS("foo", "bar") + @test !("baz" in PS("foo", "bar")) end - context("filter") do - @fact filter(iseven, PS(1, 2, 3, 4)) --> PS(2, 4) + @testset "filter" begin + @test filter(iseven, PS(1, 2, 3, 4)) == PS(2, 4) end - context("setdiff, -") do - @fact setdiff(PS(1, 2, 3), PS(1, 2)) --> PS(3) - @fact setdiff(PS(1, 2), PS(1, 2, 3)) --> PS{Int}() - @fact setdiff(PS(1, 2, 3), Set([1, 2])) --> PS(3) + @testset "setdiff, -" begin + @test setdiff(PS(1, 2, 3), PS(1, 2)) == PS(3) + @test setdiff(PS(1, 2), PS(1, 2, 3)) == PS{Int}() + @test setdiff(PS(1, 2, 3), Set([1, 2])) == PS(3) - @fact PS(1, 2, 3) - PS(1, 2) --> PS(3) + @test PS(1, 2, 3) - PS(1, 2) == PS(3) end - context("isempty") do - @fact PS{Int}() --> isempty - @fact PS(1) --> not(isempty) + @testset "isempty" begin + @test isempty(PS{Int}()) + @test !isempty(PS(1)) end - context("union") do - @fact union(PS(1, 2, 3), PS(4, 5)) --> PS(1, 2, 3, 4, 5) - @fact union(PS(1, 2, 3), PS(1, 2, 3, 4)) --> PS(1, 2, 3, 4) - @fact union(PS(1), PS(2), PS(3)) --> PS(1, 2, 3) + @testset "union" begin + @test union(PS(1, 2, 3), PS(4, 5)) == PS(1, 2, 3, 4, 5) + @test union(PS(1, 2, 3), PS(1, 2, 3, 4)) == PS(1, 2, 3, 4) + @test union(PS(1), PS(2), PS(3)) == PS(1, 2, 3) end - context("isless, <=") do - @fact PS(1) <= PS(1, 2) --> true - @fact PS(1, 2) <= PS(1, 2) --> true + @testset "isless, <=" begin + @test PS(1) <= PS(1, 2) + @test PS(1, 2) <= PS(1, 2) - @fact PS(1, 2, 3) <= PS(1, 2) --> false - @fact PS(1, 2) <= PS(1, 2, 3) --> true + @test !(PS(1, 2, 3) <= PS(1, 2)) + @test PS(1, 2) <= PS(1, 2, 3) - @fact isless(PS(1, 2), PS(1, 2)) --> false - @fact isless(PS(1, 2), PS(1, 2, 3)) --> true + @test !isless(PS(1, 2), PS(1, 2)) + @test isless(PS(1, 2), PS(1, 2, 3)) end - context("iteration") do - @fact length([el for el in PS(1, 2, 3, 4)]) --> 4 + @testset "iteration" begin + @test length([el for el in PS(1, 2, 3, 4)]) == 4 end end diff --git a/test/PersistentVectorTest.jl b/test/PersistentVectorTest.jl index f4216bf..6ce3dda 100644 --- a/test/PersistentVectorTest.jl +++ b/test/PersistentVectorTest.jl @@ -1,101 +1,107 @@ using FunctionalCollections -using FactCheck +using Base.Test +import Base.vec function vec(r::UnitRange) v = PersistentVector{Int}() - for i=r v=push(v, i) end + for i in r + v = push(v, i) + end v end function Base.Array(r::UnitRange) arr = Array(Int, r) - for i=r arr[i] = i end + for i in r + arr[i] = i + end arr end -facts("Persistent Vectors") do +@testset "Persistent Vectors" begin - context("range constructor") do - @fact typeof(vec(1:1000)) --> PersistentVector{Int} - @fact typeof(pop(vec(1:1000))) --> PersistentVector{Int} + @testset "range constructor" begin + @test typeof(vec(1:1000)) == PersistentVector{Int} + @test typeof(pop(vec(1:1000))) == PersistentVector{Int} end - context("length") do - @fact length(vec(1:32)) --> 32 - @fact length(vec(1:10000)) --> 10000 - @fact length(pop(vec(1:1000))) --> 999 + @testset "length" begin + @test length(vec(1:32)) == 32 + @test length(vec(1:10000)) == 10000 + @test length(pop(vec(1:1000))) == 999 end - context("accessing elements") do + @testset "accessing elements" begin pv = vec(1:5000) - @fact pv[1] --> 1 - @fact pv[32] --> 32 - @fact pv[500] --> 500 - @fact pv[2500] --> 2500 - @fact pv[5000] --> 5000 - @fact try pv[5001]; false catch e true end --> true + @test pv[1] == 1 + @test pv[32] == 32 + @test pv[500] == 500 + @test pv[2500] == 2500 + @test pv[5000] == 5000 + @test try pv[5001]; false catch e true end - @fact try vec(1:32)[33]; false catch e true end --> true + @test try vec(1:32)[33]; false catch e true end end - context("accessing last") do - @fact peek(vec(1:1000)) --> 1000 - @fact vec(1:1000)[end] --> 1000 + @testset "accessing last" begin + @test peek(vec(1:1000)) == 1000 + @test vec(1:1000)[end] == 1000 end - context("associng") do - @fact assoc(vec(1:1000), 500, 1)[500] --> 1 + @testset "associng" begin + @test assoc(vec(1:1000), 500, 1)[500] == 1 end - context("appending") do - @fact append(vec(1:31), 32) --> vec(1:32) - @fact append(vec(1:999), 1000) --> vec(1:1000) - @fact append(vec(1:9999), 10000) --> vec(1:10000) - @fact append(vec(1:99999), 100000) --> vec(1:100000) + @testset "appending" begin + # inference problems in 0.5.0, seem fixed in 0.6.0-dev + @test append(vec(1:31), 32) == vec(1:32) + @test append(vec(1:999), 1000) == vec(1:1000) + @test append(vec(1:9999), 10000) == vec(1:10000) + @test append(vec(1:99999), 100000) == vec(1:100000) end - context("structural sharing") do + @testset "structural sharing" begin pv = vec(1:32) pv2 = append(pv, 33) - @fact is(pv2.trie[1], pv.tail) --> true + @test pv2.trie[1] === pv.tail end - context("equality") do + @testset "equality" begin v1 = vec(1:1000) v2 = vec(1:1000) - @fact is(v1.trie, v2.trie) --> false - @fact v1 --> v2 + @test v1.trie !== v2.trie + @test v1 == v2 - @fact isequal(v1, v2) --> true + @test isequal(v1, v2) end - context("iteration") do + @testset "iteration" begin arr2 = Int[] for i in vec(1:10000) push!(arr2, i) end - @fact collect(1:10000) --> arr2 + @test collect(1:10000) == arr2 end - context("map") do + @testset "map" begin v1 = vec(1:5) - @fact map((x)->x+1, v1) --> PersistentVector([2, 3, 4, 5, 6]) + @test map((x)->x+1, v1) == PersistentVector([2, 3, 4, 5, 6]) end - context("filter") do + @testset "filter" begin v1 = vec(1:5) - @fact filter(iseven, v1) --> PersistentVector([2, 4]) + @test filter(iseven, v1) == PersistentVector([2, 4]) end - context("hash") do - @fact hash(vec(1:1000)) --> hash(vec(1:1000)) + @testset "hash" begin + @test hash(vec(1:1000)) == hash(vec(1:1000)) end - context("isempty") do - @fact PersistentVector{Int}() --> isempty - @fact PersistentVector([1]) --> not(isempty) + @testset "isempty" begin + @test isempty(PersistentVector{Int}()) + @test !isempty(PersistentVector([1])) end end diff --git a/test/REQUIRE b/test/REQUIRE index 1b9b15a..b4bc744 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -1 +1 @@ -FactCheck 0.3 +Base.Test diff --git a/test/SparseBitmappedTrieTest.jl b/test/SparseBitmappedTrieTest.jl index 798f294..57c622b 100644 --- a/test/SparseBitmappedTrieTest.jl +++ b/test/SparseBitmappedTrieTest.jl @@ -1,84 +1,77 @@ -using FactCheck +using Base.Test using FunctionalCollections import FunctionalCollections: SparseBitmappedTrie, SparseNode, SparseLeaf, bitpos, index, hasindex, arrayof, update -facts("Sparse Bitmapped Vector Tries") do +@testset "Sparse Bitmapped Vector Tries" begin - context("bitpos and index") do + @testset "bitpos and index" begin l = SparseLeaf{Int}([6, 11, 16, 21, 26], 2^5 | 2^10 | 2^15 | 2^20 | 2^25) - @fact index(l, 6) --> 1 - @fact index(l, 11) --> 2 - @fact index(l, 16) --> 3 - @fact index(l, 21) --> 4 - @fact index(l, 26) --> 5 + @test index(l, 6) == 1 + @test index(l, 11) == 2 + @test index(l, 16) == 3 + @test index(l, 21) == 4 + @test index(l, 26) == 5 - @fact hasindex(l, 5) --> false - @fact hasindex(l, 6) --> true - @fact hasindex(l, 7) --> false - @fact hasindex(l, 11) --> true - @fact hasindex(l, 12) --> false - @fact hasindex(l, 21) --> true + @test !hasindex(l, 5) + @test hasindex(l, 6) + @test !hasindex(l, 7) + @test hasindex(l, 11) + @test !hasindex(l, 12) + @test hasindex(l, 21) end - context("SparseLeaf update") do + @testset "SparseLeaf update" begin l = SparseLeaf{Int}() - @fact update(l, 1, 1) --> (leaf) -> hasindex(leaf[1], 1) - @fact update(l, 10, 10) --> (leaf) -> hasindex(leaf[1], 10) && - !hasindex(leaf[1], 11) + @test hasindex(update(l, 1, 1)[1], 1) + l = update(l, 10, 10) + @test hasindex(l[1], 10) && !hasindex(l[1], 11) l = SparseLeaf{Int}([1, 5], 2^0 | 2^4) l, _ = update(l, 2, 2) - @fact arrayof(l) --> [1, 2, 5] - @fact length(l) --> 3 - @fact hasindex(l, 1) --> true - @fact hasindex(l, 2) --> true - @fact hasindex(l, 3) --> false - @fact hasindex(l, 4) --> false - @fact hasindex(l, 5) --> true - @fact hasindex(l, 6) --> false + @test arrayof(l) == [1, 2, 5] + @test length(l) == 3 + @test hasindex(l, 1) + @test hasindex(l, 2) + @test !hasindex(l, 3) + @test !hasindex(l, 4) + @test hasindex(l, 5) + @test !hasindex(l, 6) - @fact arrayof(update(l, 2, 100)[1]) --> [1, 100, 5] + @test arrayof(update(l, 2, 100)[1]) == [1, 100, 5] end - context("SparseNode update") do - n, _ = update(SparseNode(AbstractString), 1, "foo") - @fact length(arrayof(n)) --> 1 + @testset "SparseNode update" begin + n, _ = update(SparseNode(String), 1, "foo") + @test length(arrayof(n)) == 1 leaf = arrayof(n)[1].arr[1].arr[1].arr[1].arr[1].arr[1].arr[1] - @fact hasindex(leaf, 1) --> true - @fact leaf.arr[1] --> "foo" + @test hasindex(leaf, 1) + @test leaf.arr[1] == "foo" n2, _ = update(n, 33, "bar") leaf2 = n2.arr[1].arr[1].arr[1].arr[1].arr[1].arr[1].arr[2] - @fact hasindex(leaf2, 33) --> true - @fact arrayof(leaf2)[1] --> "bar" + @test hasindex(leaf2, 33) + @test arrayof(leaf2)[1] == "bar" end - context("SparseBitmappedTrie get") do + @testset "SparseBitmappedTrie get" begin n, _ = update(SparseNode(Int), 33, 33) - @fact get(n, 33, "missing") --> 33 - @fact get(update(SparseNode(Int), 12345, 12345)[1], 12345, "missing") --> 12345 - @fact get(n, 12345, "missing") --> "missing" + @test get(n, 33, "missing") == 33 + @test get(update(SparseNode(Int), 12345, 12345)[1], 12345, "missing") == 12345 + @test get(n, 12345, "missing") == "missing" end - context("SparseBitmappedTrie length") do + @testset "SparseBitmappedTrie length & items" begin n = SparseNode(Int) for i=1:1000 n, _ = update(n, i, i) end - @fact length(n) --> 1000 - end - - context("SparseBitmappedTrie items") do - n = SparseNode(Int) - for i=1:1000 - n, _ = update(n, i, i) - end - @fact [i for i=n] --> collect(1:1000) + @test length(n) == 1000 + @test [i for i=n] == collect(1:1000) end end From 3feef11095692070fedd0f69da34dfd0f0556fc3 Mon Sep 17 00:00:00 2001 From: felixrehren Date: Tue, 10 Jan 2017 07:45:55 +0000 Subject: [PATCH 05/11] Delete REQUIRE --- test/REQUIRE | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/REQUIRE diff --git a/test/REQUIRE b/test/REQUIRE deleted file mode 100644 index b4bc744..0000000 --- a/test/REQUIRE +++ /dev/null @@ -1 +0,0 @@ -Base.Test From 13d7b5ee3602b22c901765f7f7dcbcc39f57b36b Mon Sep 17 00:00:00 2001 From: felix Date: Wed, 25 Jan 2017 16:03:18 +0000 Subject: [PATCH 06/11] min version 0.5 --- REQUIRE | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/REQUIRE b/REQUIRE index 42a2ee3..ba0aa62 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,3 +1,2 @@ -julia 0.4 -Compat 0.9.0 +julia 0.5 Benchmark From 74c475c46c35e2c2d2810720d3afe2918996849b Mon Sep 17 00:00:00 2001 From: felix Date: Wed, 25 Jan 2017 16:09:15 +0000 Subject: [PATCH 07/11] avoid 0.5-failing tests Change `Integer` type to `Int64` and test covariance of dicts only for `VERSION > v"0.6"` --- test/PersistentMapTest.jl | 25 +++++++++---------------- test/PersistentSetTest.jl | 5 ++--- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/test/PersistentMapTest.jl b/test/PersistentMapTest.jl index aa43735..a9e3248 100644 --- a/test/PersistentMapTest.jl +++ b/test/PersistentMapTest.jl @@ -1,15 +1,6 @@ using FunctionalCollections using Base.Test -# @testset "Persistent Maps" begin -# -# @testset "KVPairs" begin -# @test KVPair(1, 1) == (1, 1) -# @test (1, 1) == KVPair(1, 1) -# end -# -# end - typealias PAM PersistentArrayMap @testset "Persistent Array Maps" begin @@ -109,12 +100,12 @@ typealias PHM PersistentHashMap @test assoc(m1, 1, 100) != (assoc(m2, 1, 200)) @test assoc(m1, 1, 100) != (assoc(m2, 2, 100)) - #m3 = PHM([(1 => 10), (2 => 20), (3 => 30)]) #???? + m3 = PHM([(1 => 10), (2 => 20), (3 => 30)]) m4 = PHM((3, 30), (2, 20), (1, 10)) - #@test m3 == m4 - #@test m3 != (m1) + @test m3 == m4 + @test m3 != (m1) - #@test m3 == Dict(1 => 10, 2 => 20, 3 => 30) + @test m3 == Dict(1 => 10, 2 => 20, 3 => 30) end @testset "assoc" begin @@ -127,9 +118,11 @@ typealias PHM PersistentHashMap @test assoc(m, 1, "foo")[1] == "foo" end - @testset "covariance" begin - m = PHM{Any, Any}() - @test assoc(m, "foo", "bar") == (Dict("foo" => "bar")) + if VERSION > v"0.5" + @testset "covariance" begin + m = PHM{Any, Any}() + @test assoc(m, "foo", "bar") == (Dict("foo" => "bar")) + end end @testset "dissoc" begin diff --git a/test/PersistentSetTest.jl b/test/PersistentSetTest.jl index 6da079d..d835743 100644 --- a/test/PersistentSetTest.jl +++ b/test/PersistentSetTest.jl @@ -9,9 +9,8 @@ typealias PS PersistentSet s = PS(1, 1, 2, 3, 3) @test length(s) == 3 @test length(PS{String}()) == 0 - # inference problems in 0.5.0, seem fixed in 0.6.0-dev - @test typeof(PS{Integer}([1,2,3])) == PS{Integer} - @test typeof(PS(Integer[1,2,3])) == PS{Integer} + @test typeof(PS{Int64}([1,2,3])) == PS{Int64} + @test typeof(PS(Int64[1,2,3])) == PS{Int64} end @testset "isequal" begin From 644da4122716042c531f2663f8a770e9034fe8b3 Mon Sep 17 00:00:00 2001 From: felix Date: Wed, 25 Jan 2017 16:12:20 +0000 Subject: [PATCH 08/11] stop travis checking 0.4 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 804c96c..2391861 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ os: - linux - osx julia: - - 0.4 - 0.5 - nightly notifications: From ded38991188fc706f5133ef96cc65ecf751e9075 Mon Sep 17 00:00:00 2001 From: felix Date: Thu, 26 Jan 2017 07:52:55 +0000 Subject: [PATCH 09/11] update deprecated array syntax Array(T,n) -> Array{T,1}(n) --- src/BitmappedVectorTrie.jl | 2 +- src/PersistentMap.jl | 2 +- src/PersistentVector.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BitmappedVectorTrie.jl b/src/BitmappedVectorTrie.jl index 7f609fe..3e89b50 100644 --- a/src/BitmappedVectorTrie.jl +++ b/src/BitmappedVectorTrie.jl @@ -21,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 diff --git a/src/PersistentMap.jl b/src/PersistentMap.jl index 56dafac..c682a8f 100644 --- a/src/PersistentMap.jl +++ b/src/PersistentMap.jl @@ -182,7 +182,7 @@ function Base.map(f::( Union{Function, DataType}), m::PersistentHashMap) end function Base.filter{K, V}(f::Function, m::PersistentHashMap{K, V}) - arr = Array((Pair{K, V}), 0) + arr = Array{Pair{K, V},1}(0) for el in m f(el) && push!(arr, el) end diff --git a/src/PersistentVector.jl b/src/PersistentVector.jl index e4aa14d..e02104b 100644 --- a/src/PersistentVector.jl +++ b/src/PersistentVector.jl @@ -55,7 +55,7 @@ function push{T}(v::PersistentVector{T}, el) else # T[el] will give an error when T is an tuple type in v0.3 # workaround: - arr = Array(T, 1) + arr = Array{T,1}(1) arr[1] = convert(T, el) PersistentVector{T}(append(v.trie, v.tail), arr, 1 + v.length) end From 72bcfd6a5be9b6d62e8714b4367f1e445d3eb28c Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Thu, 26 Jan 2017 17:00:18 +0530 Subject: [PATCH 10/11] Drop dependency on Benchmark --- REQUIRE | 1 - 1 file changed, 1 deletion(-) diff --git a/REQUIRE b/REQUIRE index ba0aa62..94237c0 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,2 +1 @@ julia 0.5 -Benchmark From 33b91fc4478604ffb5188c6306149229178a7171 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Thu, 26 Jan 2017 17:00:41 +0530 Subject: [PATCH 11/11] Allow maps of abstract types --- src/PersistentMap.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PersistentMap.jl b/src/PersistentMap.jl index c682a8f..0041744 100644 --- a/src/PersistentMap.jl +++ b/src/PersistentMap.jl @@ -39,10 +39,10 @@ Base.haskey(m::PersistentArrayMap, k) = get(m, k, NotFound()) != NotFound() function assoc{K, V}(m::PersistentArrayMap{K, V}, k, v) idx = findkeyidx(m, k) - idx == 0 && return PersistentArrayMap{K, V}(push!(m.kvs[1:end], Pair(k, v))) + idx == 0 && return PersistentArrayMap{K, V}(push!(m.kvs[1:end], Pair{K,V}(k, v))) kvs = m.kvs[1:end] - kvs[idx] = Pair(k, v) + kvs[idx] = Pair{K,V}(k, v) PersistentArrayMap{K, V}(kvs) end