Packages
fixpoint
0.8.13
0.22.1
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.6
0.20.5
0.20.4
0.20.3
0.20.2
0.20.1
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.18.2
0.18.1
0.17.6
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.13.5
0.13.4
0.13.2
0.13.1
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.2
0.12.1
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.46
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.28
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.10
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.1.3
0.1.2
0.1.1
0.1.0
Constraint Programming Solver
Current section
Files
Jump to
Current section
Files
lib/solver/domain/bitvector_domain_v2.ex
defmodule CPSolver.BitVectorDomain.V2 do
import Bitwise
def new([]) do
throw(:empty_domain)
end
def new(value) when is_integer(value) do
new([value])
end
def new(domain) when is_integer(domain) do
new([domain])
end
def new({{:bit_vector, _size, _ref} = _bitmap, _offset} = domain) do
domain
end
def new(domain) do
offset = -Enum.min(domain)
domain_size = Enum.max(domain) + offset + 1
bv = :bit_vector.new(domain_size)
Enum.each(domain, fn idx -> :bit_vector.set(bv, idx + offset) end)
set_min(bv, 0)
set_max(bv, Enum.max(domain) + offset)
{bv, offset}
end
def map(domain, mapper_fun) when is_function(mapper_fun) do
to_list(domain, mapper_fun)
end
def to_list(domain, mapper_fun \\ &Function.identity/1) do
Enum.reduce(min(domain)..max(domain), [], fn i, acc ->
(contains?(domain, i) && [mapper_fun.(i) | acc]) || acc
end)
end
def fixed?(domain) do
min(domain) == max(domain)
end
def min({bit_vector, offset} = _domain) do
get_min(bit_vector) - offset
end
def max({bit_vector, offset} = _domain) do
get_max(bit_vector) - offset
end
def size({{:bit_vector, _size, ref} = bit_vector, _offset}) do
%{
min_addr: %{block: current_min_block},
max_addr: %{block: current_max_block}
} = get_bound_addrs(bit_vector)
Enum.reduce(current_min_block..current_max_block, 0, fn idx, acc ->
n = :atomics.get(ref, idx)
(n == 0 && acc) ||
acc + (for(<<bit::1 <- :binary.encode_unsigned(n)>>, do: bit) |> Enum.sum())
end)
end
def contains?({{:bit_vector, _zero_based_max, _ref} = bit_vector, offset}, value) do
vector_value = value + offset
vector_value >= get_min(bit_vector) && vector_value <= get_max(bit_vector) &&
:bit_vector.get(bit_vector, vector_value) == 1
end
def fix({bit_vector, offset} = domain, value) do
if contains?(domain, value) do
update_min(bit_vector, value + offset)
update_max(bit_vector, value + offset)
## TODO: do we need it?
{:fixed, domain}
else
:fail
end
end
def remove({bit_vector, offset} = domain, value) do
cond do
## No value in the domain, do nothing
!contains?(domain, value) ->
:no_change
## The domain is fixed
fixed?(domain) ->
## Fail on attempt to remove fixed value, otherwise do nothing
(min(domain) == value && :fail) || :no_change
true ->
## Value is there, and it's safe to remove
domain_change =
cond do
min(domain) == value ->
if tighten_min(bit_vector) == :fail do
:fail
else
(fixed?(domain) && :fixed) || :min_change
end
max(domain) == value ->
if tighten_max(bit_vector) == :fail do
:fail
else
(fixed?(domain) && :fixed) || :max_change
end
true ->
vector_value = value + offset
:bit_vector.clear(bit_vector, vector_value)
:domain_change
end
{domain_change, domain}
end
end
def removeAbove({bit_vector, offset} = domain, value) do
cond do
value >= max(domain) ->
:no_change
value < min(domain) ->
:fail
true ->
## The value is strictly less than max
domain_change =
cond do
tighten_max(bit_vector, value + offset + 1) == :fail -> :fail
fixed?(domain) -> :fixed
true -> :max_change
end
{domain_change, domain}
end
end
def removeBelow({bit_vector, offset} = domain, value) do
cond do
value <= min(domain) ->
:no_change
value > max(domain) ->
:fail
true ->
## The value is strictly greater than min
domain_change =
cond do
tighten_min(bit_vector, value + offset - 1) == :fail -> :fail
fixed?(domain) -> :fixed
true -> :min_change
end
{domain_change, domain}
end
end
## Last 2 bytes of bit_vector are min and max
def last_index({:bit_vector, _zero_based_max, ref} = _bit_vector) do
:atomics.info(ref).size - 2
end
defp get_min({:bit_vector, _zero_based_max, ref} = bit_vector) do
:atomics.get(ref, last_index(bit_vector) + 1)
end
defp set_min({:bit_vector, _zero_based_max, ref} = bit_vector, value) do
min_idx = last_index(bit_vector) + 1
# :atomics.put(ref, min_idx, value)
case :atomics.exchange(ref, min_idx, value) do
prev_value when prev_value > value ->
## Do not update if current min is greater than the proposed min value
set_min(bit_vector, prev_value)
prev_value ->
(prev_value == value && :no_change) || :min_change
end
end
defp update_min(bit_vector, new_min_value) do
cond do
new_min_value > get_max(bit_vector) ->
:fail
get_min(bit_vector) >= new_min_value ->
:no_change
true ->
set_min(bit_vector, new_min_value)
:min_change
end
end
defp get_max({:bit_vector, _zero_based_max, ref} = bit_vector) do
:atomics.get(ref, last_index(bit_vector) + 2)
end
defp set_max({:bit_vector, _zero_based_max, ref} = bit_vector, value) do
max_idx = last_index(bit_vector) + 2
# :atomics.put(ref, last_index(bit_vector) + 2, value)
case :atomics.exchange(ref, max_idx, value) do
prev_value when prev_value < value ->
## Do not update if current max is lesser than the proposed max value
set_max(bit_vector, prev_value)
prev_value ->
(prev_value == value && :no_change) || :max_change
end
# :atomics.put(ref, last_index(bit_vector) + 2, value)
end
defp update_max(bit_vector, new_max_value) do
cond do
new_max_value < get_min(bit_vector) ->
:fail
get_max(bit_vector) <= new_max_value ->
:no_change
true ->
set_max(bit_vector, new_max_value)
:max_change
end
# :atomics.put(ref, last_index(bit_vector) + 2, new_max_value)
end
## Update (cached) min, if necessary
def tighten_min({:bit_vector, _zero_based_max, atomics_ref} = bit_vector, starting_at \\ nil) do
starting_position = (starting_at && starting_at) || get_min(bit_vector)
%{
max_addr: %{block: current_max_block}
} = get_bound_addrs(bit_vector)
{rightmost_block, position_in_block} = vector_address(starting_position + 1)
## Find a new min (on the right of the current one)
min_value =
Enum.reduce_while(rightmost_block..current_max_block, nil, fn idx, _acc ->
case :atomics.get(atomics_ref, idx) do
0 ->
{:cont, nil}
non_zero_block ->
## Because the position in the block is 0-based
shift = position_in_block
{:halt, (idx - 1) * 64 + lsb(non_zero_block >>> shift <<< shift)}
end
end)
(min_value && update_min(bit_vector, min_value)) || :fail
end
## Update (cached) max
defp tighten_max({:bit_vector, _zero_based_max, atomics_ref} = bit_vector, starting_at \\ nil) do
starting_position = (starting_at && starting_at) || get_max(bit_vector)
%{
min_addr: %{block: current_min_block}
} = get_bound_addrs(bit_vector)
{leftmost_block, position_in_block} = vector_address(starting_position - 1)
## Find a new max (on the left of the current one)
max_value =
Enum.reduce_while(current_min_block..leftmost_block |> Enum.reverse(), nil, fn idx, _acc ->
case :atomics.get(atomics_ref, idx) do
0 ->
{:cont, nil}
non_zero_block ->
## Reset all bits above the position
mask = (1 <<< (position_in_block + 1)) - 1
{:halt, (idx - 1) * 64 + msb(non_zero_block &&& mask)}
end
end)
(max_value && update_max(bit_vector, max_value)) || :fail
end
def get_bound_addrs(bit_vector) do
current_min = get_min(bit_vector)
current_max = get_max(bit_vector)
{current_min_block, current_min_offset} = vector_address(current_min)
{current_max_block, current_max_offset} = vector_address(current_max)
%{
min_addr: %{block: current_min_block, offset: current_min_offset},
max_addr: %{block: current_max_block, offset: current_max_offset}
}
end
## Find the index of atomics where the n-value resides
def block_index(n) do
div(n, 64) + 1
end
def vector_address(n) do
{block_index(n), rem(n, 64)}
end
## Find least significant bit
def lsb(0) do
0
end
def lsb(n) do
lsb(n, 0)
end
defp lsb(1, idx) do
idx
end
defp lsb(n, idx) do
((n &&& 1) == 1 && idx) ||
lsb(n >>> 1, idx + 1)
end
def msb(0) do
0
end
def msb(n) do
msb = floor(:math.log2(n))
## Check if there is no precision loss.
## We really want to throw away the fraction part even if it may
## get very close to 1.
if floor(:math.pow(2, msb)) > n do
msb - 1
else
msb
end
end
end