Skip to content

Commit

Permalink
Merge bc6e315 into a298505
Browse files Browse the repository at this point in the history
  • Loading branch information
pguillory committed Aug 28, 2020
2 parents a298505 + bc6e315 commit d6f1a8a
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 60 deletions.
33 changes: 11 additions & 22 deletions lib/hypex.ex
Expand Up @@ -122,27 +122,11 @@ defmodule Hypex do
"""
@spec merge([ hypex :: Hypex.t ]) :: hypex :: Hypex.t
def merge([ { _mod, _width, _registers } = hypex ]),
do: hypex
def merge([ { mod, width, _registers } | _ ] = hypices) do
unless Enum.all?(hypices, &(match?({ ^mod, ^width, _ }, &1))) do
raise ArgumentError, message: @merge_err
end

registers = Enum.map(hypices, fn({ mod, _width, registers }) ->
mod.to_list(registers)
end)

m_reg =
registers
|> Util.ziplist
|> Enum.reduce([], &([ :lists.max(&1) | &2 ]))
|> Enum.reverse
|> mod.from_list

{ mod, width, m_reg }
def merge(hypices) when is_list(hypices) do
Enum.reduce(hypices, &merge/2)
end
def merge(_hypices) do

def merge(_hypex) do
raise ArgumentError, message: @merge_err
end

Expand All @@ -153,8 +137,13 @@ defmodule Hypex do
throguh to `merge/1`.
"""
@spec merge(hypex :: Hypex.t, hypex :: Hypex.t) :: hypex :: Hypex.t
def merge(h1, h2),
do: merge([ h1, h2 ])
def merge({mod, width, registers1}, {mod, width, registers2}) do
{mod, width, mod.merge(registers1, registers2)}
end

def merge(_hypex1, _hypex2) do
raise ArgumentError, message: @merge_err
end

@doc """
Updates a Hypex instance with a value.
Expand Down
15 changes: 15 additions & 0 deletions lib/hypex/array.ex
Expand Up @@ -78,4 +78,19 @@ defmodule Hypex.Array do
end, acc, registers)
end

@doc false
@spec merge(array, array) :: array
def merge(registers1, registers2) do
list1 = to_list(registers1)
list2 = to_list(registers2)
merge2(list1, list2) |> from_list()
end

defp merge2([value1 | list1], [value2 | list2]) do
[max(value1, value2) | merge2(list1, list2)]
end

defp merge2([], []) do
[]
end
end
13 changes: 13 additions & 0 deletions lib/hypex/bitstring.ex
Expand Up @@ -70,4 +70,17 @@ defmodule Hypex.Bitstring do
@spec reduce(bitstring, width :: number, accumulator :: any, (number, any -> any)) :: accumulator :: any
defdelegate reduce(registers, width, acc, fun), to: Hypex.Util, as: :binary_reduce

@doc false
@spec merge(bitstring, bitstring) :: bitstring
def merge(registers1, registers2) do
merge2(registers1, registers2) |> from_list()
end

defp merge2(<<value1, registers1::bitstring>>, <<value2, registers2::bitstring>>) do
[max(value1, value2) | merge2(registers1, registers2)]
end

defp merge2(<<>>, <<>>) do
[]
end
end
32 changes: 0 additions & 32 deletions lib/hypex/util.ex
Expand Up @@ -94,36 +94,4 @@ defmodule Hypex.Util do
def normalize_module(mod) when mod in [ Bitstring, Hypex.Bitstring ],
do: Hypex.Bitstring
def normalize_module(mod) when is_atom(mod), do: mod

@doc """
Zips corresponding elements from each list in list_of_lists.
This function acts in an identical way to `List.zip/1` except that the zipped
values are lists rather than tuples. This is because Hypex merge performance
can be improved without the jumps to/from Tuple structures.
"""
@spec ziplist(lists :: [ ]) :: zipped_list :: []
def ziplist(list_of_lists) when is_list(list_of_lists),
do: zip(list_of_lists, [])

# The internal zip of `ziplist/1`, accepting a list and an accumulator. This
# function will move through each list and blend each index into a single list
# in which each index is grouped as a list.
#
# This implementation contains slight optimizations for the Hypex use case vs
# the implementation inside the `List` module.
defp zip(list, acc) do
case :lists.mapfoldl(&zip_each/2, [], list) do
{ _, nil } ->
:lists.reverse(acc)
{ mlist, heads } ->
zip(mlist, [heads | acc])
end
end

# The handlers for the `:lists.mapfoldl/3` call inside `zip/2`. If we reach
# the end of a list, we pass back a set of `nil` tuples to avoid continuing.
defp zip_each([h | t], acc), do: { t, [h | acc] }
defp zip_each(_lists, _acc), do: { nil, nil }

end
12 changes: 9 additions & 3 deletions test/hypex/array_test.exs
Expand Up @@ -13,12 +13,18 @@ defmodule Hypex.ArrayTest do
test "updating a Hypex with a duplicate value will short-circuit" do
hypex = Hypex.new(16, Hypex.Array)

{ time1, hypex } = :timer.tc(fn ->
Hypex.update(hypex, "Hypex")
{ time1, _hypex } = :timer.tc(fn ->
for _ <- 1..1000 do
Hypex.update(hypex, "Hypex")
end
end)

hypex = Hypex.update(hypex, "Hypex")

{ time2, _hypex } = :timer.tc(fn ->
Hypex.update(hypex, "Hypex")
for _ <- 1..1000 do
Hypex.update(hypex, "Hypex")
end
end)

assert(time2 < time1 * 0.75)
Expand Down
12 changes: 9 additions & 3 deletions test/hypex/bitstring_text.exs
Expand Up @@ -13,12 +13,18 @@ defmodule Hypex.BitstringTest do
test "updating a Hypex with a duplicate value will short-circuit" do
hypex = Hypex.new(16)

{ time1, hypex } = :timer.tc(fn ->
Hypex.update(hypex, "Hypex")
{ time1, _hypex } = :timer.tc(fn ->
for _ <- 1..1000 do
Hypex.update(hypex, "Hypex")
end
end)

hypex = Hypex.update(hypex, "Hypex")

{ time2, _hypex } = :timer.tc(fn ->
Hypex.update(hypex, "Hypex")
for _ <- 1..1000 do
Hypex.update(hypex, "Hypex")
end
end)

assert(time2 < time1 * 0.75)
Expand Down

0 comments on commit d6f1a8a

Please sign in to comment.