Packages
fixpoint
0.12.7
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.ex
defmodule CPSolver.BitVectorDomain do
import Bitwise
@failure_value (1 <<< 64) - 1
def new([]) do
fail()
end
def new(domain) when is_integer(domain) do
new([domain])
end
def new(domain) when is_list(domain) or is_struct(domain, Range) or is_struct(domain, MapSet) 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)
PackedMinMax.set_min(0, 0)
|> PackedMinMax.set_max(Enum.max(domain) + offset)
|> then(fn min_max -> set_min_max(bv, min_max) end)
{bv, offset}
end
def copy({{:bit_vector, ref} = bit_vector, offset} = _domain) do
%{
min_addr: %{block: current_min_block},
max_addr: %{block: current_max_block}
} = get_bound_addrs(bit_vector)
new_atomics_size = current_max_block + 1
new_atomics_ref = :atomics.new(new_atomics_size, [{:signed, false}])
Enum.each(
current_min_block..current_max_block,
fn block_idx ->
block_val = :atomics.get(ref, block_idx)
:atomics.put(new_atomics_ref, block_idx, block_val)
end
)
new_bit_vector = {:bit_vector, new_atomics_ref}
set_min_max(new_bit_vector, get_min_max_impl(bit_vector) |> elem(1))
{new_bit_vector, offset}
end
def map(domain, mapper_fun) when is_function(mapper_fun) do
to_list(domain, mapper_fun)
end
## Reduce over domain values
def reduce(
{{:bit_vector, ref} = bit_vector, offset} = domain,
value_mapper_fun,
acc_init \\ MapSet.new(),
reduce_fun \\ &MapSet.union/2
) do
%{
min_addr: %{block: current_min_block, offset: _min_offset},
max_addr: %{block: current_max_block, offset: _max_offset}
} = get_bound_addrs(bit_vector)
mapped_lb = value_mapper_fun.(min(domain))
mapped_ub = value_mapper_fun.(max(domain))
{lb, ub} = (mapped_lb <= mapped_ub && {mapped_lb, mapped_ub}) || {mapped_ub, mapped_lb}
Enum.reduce(current_min_block..current_max_block, acc_init, fn block_idx, acc ->
block = :atomics.get(ref, block_idx)
if block == 0 do
acc
else
reduce_fun.(
acc,
bit_positions(block, fn val ->
case value_mapper_fun.(val + 64 * (block_idx - 1) - offset) do
value when value >= lb and value <= ub ->
value
_out_of_bounds ->
nil
end
end)
)
end
end)
end
def to_list(
domain,
value_mapper_fun \\ &Function.identity/1
) do
(fixed?(domain) && MapSet.new([value_mapper_fun.(min(domain))])) ||
reduce(domain, value_mapper_fun, MapSet.new(), &MapSet.union/2)
end
def fixed?({bit_vector, _offset} = _domain) do
{current_min_max, _min_max_idx, current_min, current_max} = get_min_max(bit_vector)
current_max == current_min && current_min_max != @failure_value
end
def failed?({:bit_vector, _ref} = bit_vector) do
failed?(elem(get_min_max_impl(bit_vector), 1))
end
def failed?({bit_vector, _offset} = _domain) do
failed?(bit_vector)
end
def failed?(min_max_value) when is_integer(min_max_value) do
min_max_value == @failure_value
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, ref} = bit_vector, _offset}) do
%{
min_addr: %{block: current_min_block, offset: min_offset},
max_addr: %{block: current_max_block, offset: max_offset}
} = get_bound_addrs(bit_vector)
Enum.reduce(current_min_block..current_max_block, 0, fn idx, acc ->
n = :atomics.get(ref, idx)
if n == 0 do
acc
else
n1 = (idx == current_min_block && n >>> min_offset) || n
n2 = (idx == current_max_block && ((1 <<< (max_offset + 1)) - 1 &&& n1)) || n1
acc + bit_count(n2)
end
end)
end
def contains?({{:bit_vector, _ref} = bit_vector, offset}, value) do
{_current_min_max, _min_max_idx, min_value, max_value} = get_min_max(bit_vector)
vector_value = value + offset
contains?(bit_vector, vector_value, min_value, max_value)
end
def contains?(bit_vector, vector_value, min_value, max_value) do
vector_value >= min_value && vector_value <= max_value &&
:bit_vector.get(bit_vector, vector_value) == 1
end
def fix({bit_vector, offset} = _domain, value) do
min_max_info =
{_current_min_max, _min_max_idx, min_value, max_value} = get_min_max(bit_vector)
vector_value = value + offset
if contains?(bit_vector, vector_value, min_value, max_value) do
set_fixed(bit_vector, value + offset, min_max_info)
else
fail(bit_vector)
end
end
def remove({bit_vector, offset} = domain, value) do
{_current_min_max, _min_max_idx, min_value, max_value} = get_min_max(bit_vector)
vector_value = value + offset
cond do
## No value in the domain, do nothing
contains?(bit_vector, vector_value, min_value, max_value) ->
domain_change =
cond do
min_value == max_value && vector_value == min_value ->
## Fixed value: fail on removing attempt
fail(bit_vector)
min_value == vector_value ->
tighten_min(bit_vector, min_value, max_value)
max_value == vector_value ->
tighten_max(bit_vector, max_value, min_value)
true ->
:domain_change
end
{domain_change, domain}
|> tap(fn _ -> :bit_vector.clear(bit_vector, vector_value) end)
true ->
:no_change
end
end
def removeAbove({bit_vector, offset} = domain, value) do
{_current_min_max, _min_max_idx, min_value, max_value} = get_min_max(bit_vector)
vector_value = value + offset
cond do
vector_value >= max_value ->
:no_change
vector_value < min_value ->
fail(bit_vector)
true ->
## The value is strictly less than max
domain_change = tighten_max(bit_vector, vector_value + 1, min_value)
{domain_change, domain}
end
end
def removeBelow({bit_vector, offset} = domain, value) do
{_current_min_max, _min_max_idx, min_value, max_value} = get_min_max(bit_vector)
vector_value = value + offset
cond do
vector_value <= min_value ->
:no_change
vector_value > max_value ->
fail(bit_vector)
true ->
## The value is strictly greater than min
domain_change = tighten_min(bit_vector, vector_value - 1, max_value)
{domain_change, domain}
end
end
def raw({{:bit_vector, ref} = _bit_vector, offset} = _domain) do
%{
offset: offset,
content: Enum.map(1..:atomics.info(ref).size, fn i -> :atomics.get(ref, i) end)
}
end
## Last byte of bit_vector contains (packed) min and max
def last_index({:bit_vector, ref} = _bit_vector) do
:atomics.info(ref).size - 1
end
defp set_min_max({:bit_vector, ref} = bit_vector, min_max) do
bit_vector
|> min_max_index()
|> tap(fn idx ->
:atomics.put(ref, idx, min_max)
end)
end
def get_min(bit_vector) do
get_min_max(bit_vector) |> elem(2)
end
def get_max(bit_vector) do
get_min_max(bit_vector) |> elem(3)
end
defp min_max_index(bit_vector) do
last_index(bit_vector) + 1
end
def get_min_max(bit_vector) do
get_min_max_impl(bit_vector)
|> then(fn {min_max_index, min_max} ->
min_max == @failure_value && fail(bit_vector)
{min_max, min_max_index, PackedMinMax.get_min(min_max), PackedMinMax.get_max(min_max)}
end)
end
defp get_min_max_impl({:bit_vector, ref} = bit_vector) do
min_max_index = min_max_index(bit_vector)
{min_max_index, :atomics.get(ref, min_max_index)}
end
def set_min(bit_vector, new_min) do
set_min(bit_vector, new_min, get_min_max(bit_vector))
end
def set_min({:bit_vector, ref} = bit_vector, new_min, min_max_info) do
{current_min_max, min_max_idx, current_min, current_max} = min_max_info
cond do
new_min > current_max ->
## Inconsistency
fail(bit_vector)
new_min != current_min && current_min == current_max ->
## Attempt to re-fix
fail(bit_vector)
true ->
## Min change
min_max_value = PackedMinMax.set_min(current_min_max, new_min)
case :atomics.compare_exchange(ref, min_max_idx, current_min_max, min_max_value) do
:ok ->
cond do
new_min == current_max -> :fixed
new_min <= current_min -> :no_change
true -> :min_change
end
changed_by_other_thread ->
min2 = PackedMinMax.get_min(changed_by_other_thread)
max2 = PackedMinMax.get_max(changed_by_other_thread)
set_min(bit_vector, new_min, {changed_by_other_thread, min_max_idx, min2, max2})
end
end
end
def set_max(bit_vector, new_max) do
set_max(bit_vector, new_max, get_min_max(bit_vector))
end
def set_max({:bit_vector, ref} = bit_vector, new_max, min_max_info) do
{current_min_max, min_max_idx, current_min, current_max} = min_max_info
cond do
new_max < current_min ->
## Inconsistency
fail(bit_vector)
new_max != current_max && current_min == current_max ->
## Attempt to re-fix
fail(bit_vector)
true ->
## Max change
min_max_value = PackedMinMax.set_max(current_min_max, new_max)
case :atomics.compare_exchange(ref, min_max_idx, current_min_max, min_max_value) do
:ok ->
cond do
new_max == current_min -> :fixed
new_max >= current_max -> :no_change
true -> :max_change
end
changed_by_other_thread ->
min2 = PackedMinMax.get_min(changed_by_other_thread)
max2 = PackedMinMax.get_max(changed_by_other_thread)
set_max(bit_vector, new_max, {changed_by_other_thread, min_max_idx, min2, max2})
end
end
end
def set_fixed({:bit_vector, ref} = bit_vector, fixed_value, min_max_info) do
{current_min_max, min_max_idx, current_min, current_max} = min_max_info
if fixed_value != current_max && current_min == current_max do
## Attempt to re-fix
fail(bit_vector)
else
min_max_value = PackedMinMax.set_min(0, fixed_value) |> PackedMinMax.set_max(fixed_value)
case :atomics.compare_exchange(ref, min_max_idx, current_min_max, min_max_value) do
:ok ->
:fixed
changed_by_other_thread ->
min2 = PackedMinMax.get_min(changed_by_other_thread)
max2 = PackedMinMax.get_max(changed_by_other_thread)
set_fixed(bit_vector, fixed_value, {changed_by_other_thread, min_max_idx, min2, max2})
end
end
end
## Update (cached) min, if necessary
defp tighten_min(
{:bit_vector, atomics_ref} = bit_vector,
starting_at,
max_value
) do
{current_max_block, _} = vector_address(max_value)
{rightmost_block, position_in_block} = vector_address(starting_at + 1)
## Find a new min (on the right of the current one)
min_value =
Enum.reduce_while(rightmost_block..current_max_block, false, fn idx, min_block_empty? ->
case :atomics.get(atomics_ref, idx) do
0 ->
{:cont, min_block_empty?}
non_zero_block ->
block_lsb =
if min_block_empty? do
lsb(non_zero_block)
else
## Reset all bits in the block to the left of the position
shift = position_in_block
lsb(non_zero_block >>> shift <<< shift)
end
(block_lsb &&
{:halt, (idx - 1) * 64 + block_lsb}) || {:cont, true}
end
end)
(is_integer(min_value) && set_min(bit_vector, min_value)) || fail(bit_vector)
end
## Update (cached) max
defp tighten_max(
{:bit_vector, atomics_ref} = bit_vector,
starting_at,
min_value
) do
{current_min_block_idx, _} = vector_address(min_value)
{leftmost_block_idx, position_in_block} = vector_address(starting_at - 1)
## Find a new max (on the left of the current one)
##
max_value =
Enum.reduce_while(
leftmost_block_idx..current_min_block_idx,
false,
fn idx, max_block_empty? ->
case :atomics.get(atomics_ref, idx) do
0 ->
{:cont, max_block_empty?}
non_zero_block ->
block_msb =
if max_block_empty? do
msb(non_zero_block)
else
## Reset all bits in the block to the right of the position
mask = (1 <<< (position_in_block + 1)) - 1
msb(non_zero_block &&& mask)
end
(block_msb &&
{:halt, (idx - 1) * 64 + block_msb}) || {:cont, true}
end
end
)
(is_integer(max_value) && set_max(bit_vector, max_value)) || fail(bit_vector)
end
defp fail(bit_vector \\ nil) do
bit_vector && set_min_max(bit_vector, @failure_value)
throw(:fail)
end
def get_bound_addrs(bit_vector) do
{_, _, current_min, current_max} = get_min_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
defp block_index(n) do
div(n, 64) + 1
end
defp vector_address(n) do
{block_index(n), rem(n, 64)}
end
## Find least significant bit for given number
def lsb(n, method \\ :debruijn)
def lsb(0, _method) do
nil
end
def lsb(n, :shift) do
lsb_impl(n, 0)
end
def lsb(n, :debruijn) do
deBruijnSequence = 0x022FDD63CC95386D
## Complement, multiply and normalize to 64-bit
normalized = (n &&& -n) * deBruijnSequence &&& (1 <<< 64) - 1
## Use first 6 bits to locate in index table
normalized >>> 58
## || lsb(n, :shift)
|> deBruijnTable()
end
defp lsb_impl(1, idx) do
idx
end
defp lsb_impl(n, idx) do
((n &&& 1) == 1 && idx) ||
lsb_impl(n >>> 1, idx + 1)
end
def msb_(n) do
if n > 0 do
msb_impl(n, -1)
end
end
defp msb_impl(0, acc) do
acc
end
defp msb_impl(n, acc) do
msb_impl(n >>> 1, acc + 1)
end
def msb(n) do
if n > 0 do
n = n ||| n >>> 1
n = n ||| n >>> 2
n = n ||| n >>> 4
n = n ||| n >>> 8
n = n ||| n >>> 16
n = n ||| n >>> 32
log2(n - (n >>> 1))
end
end
def bit_count(0) do
0
end
def bit_count(n) do
n = (n &&& 0x5555555555555555) + (n >>> 1 &&& 0x5555555555555555)
n = (n &&& 0x3333333333333333) + (n >>> 2 &&& 0x3333333333333333)
n = (n &&& 0x0F0F0F0F0F0F0F0F) + (n >>> 4 &&& 0x0F0F0F0F0F0F0F0F)
n = (n &&& 0x00FF00FF00FF00FF) + (n >>> 8 &&& 0x00FF00FF00FF00FF)
n = (n &&& 0x0000FFFF0000FFFF) + (n >>> 16 &&& 0x0000FFFF0000FFFF)
(n &&& 0x00000000FFFFFFFF) + (n >>> 32 &&& 0x00000000FFFFFFFF)
end
def bit_positions(0, _mapper) do
MapSet.new()
end
def bit_positions(n, mapper) do
lsb = lsb(n)
msb = msb(n)
initial_set =
Enum.reduce([lsb, msb], MapSet.new(), fn value, acc ->
case mapper.(value) do
nil ->
acc
new_value ->
MapSet.put(acc, new_value)
end
end)
bit_positions(n >>> lsb, 1, lsb, msb, mapper, initial_set)
end
def bit_positions(_n, _shift, iteration, msb, _mapper, positions) when iteration == msb do
positions
end
def bit_positions(n, shift, iteration, msb, mapper, positions) do
acc =
((n &&& shift) > 0 &&
case mapper.(iteration) do
nil -> positions
new_value -> MapSet.put(positions, new_value)
end) ||
positions
bit_positions(n, shift <<< 1, iteration + 1, msb, mapper, acc)
end
## Precompiled log2 values for powers of 2
defp log2(1), do: 0
defp log2(2), do: 1
defp log2(4), do: 2
defp log2(8), do: 3
defp log2(16), do: 4
defp log2(32), do: 5
defp log2(64), do: 6
defp log2(128), do: 7
defp log2(256), do: 8
defp log2(512), do: 9
defp log2(1024), do: 10
defp log2(2048), do: 11
defp log2(4096), do: 12
defp log2(8192), do: 13
defp log2(16384), do: 14
defp log2(32768), do: 15
defp log2(65536), do: 16
defp log2(131_072), do: 17
defp log2(262_144), do: 18
defp log2(524_288), do: 19
defp log2(1_048_576), do: 20
defp log2(2_097_152), do: 21
defp log2(4_194_304), do: 22
defp log2(8_388_608), do: 23
defp log2(16_777_216), do: 24
defp log2(33_554_432), do: 25
defp log2(67_108_864), do: 26
defp log2(134_217_728), do: 27
defp log2(268_435_456), do: 28
defp log2(536_870_912), do: 29
defp log2(1_073_741_824), do: 30
defp log2(2_147_483_648), do: 31
defp log2(4_294_967_296), do: 32
defp log2(8_589_934_592), do: 33
defp log2(17_179_869_184), do: 34
defp log2(34_359_738_368), do: 35
defp log2(68_719_476_736), do: 36
defp log2(137_438_953_472), do: 37
defp log2(274_877_906_944), do: 38
defp log2(549_755_813_888), do: 39
defp log2(1_099_511_627_776), do: 40
defp log2(2_199_023_255_552), do: 41
defp log2(4_398_046_511_104), do: 42
defp log2(8_796_093_022_208), do: 43
defp log2(17_592_186_044_416), do: 44
defp log2(35_184_372_088_832), do: 45
defp log2(70_368_744_177_664), do: 46
defp log2(140_737_488_355_328), do: 47
defp log2(281_474_976_710_656), do: 48
defp log2(562_949_953_421_312), do: 49
defp log2(1_125_899_906_842_624), do: 50
defp log2(2_251_799_813_685_248), do: 51
defp log2(4_503_599_627_370_496), do: 52
defp log2(9_007_199_254_740_992), do: 53
defp log2(18_014_398_509_481_984), do: 54
defp log2(36_028_797_018_963_968), do: 55
defp log2(72_057_594_037_927_936), do: 56
defp log2(144_115_188_075_855_872), do: 57
defp log2(288_230_376_151_711_744), do: 58
defp log2(576_460_752_303_423_488), do: 59
defp log2(1_152_921_504_606_846_976), do: 60
defp log2(2_305_843_009_213_693_952), do: 61
defp log2(4_611_686_018_427_387_904), do: 62
defp log2(9_223_372_036_854_775_808), do: 63
## De Bruijn table for sequence 0x022FDD63CC95386D
defp deBruijnTable(0), do: 0
defp deBruijnTable(1), do: 1
defp deBruijnTable(2), do: 2
defp deBruijnTable(3), do: 53
defp deBruijnTable(4), do: 3
defp deBruijnTable(5), do: 7
defp deBruijnTable(6), do: 54
defp deBruijnTable(7), do: 27
defp deBruijnTable(8), do: 4
defp deBruijnTable(9), do: 38
defp deBruijnTable(10), do: 41
defp deBruijnTable(11), do: 8
defp deBruijnTable(12), do: 34
defp deBruijnTable(13), do: 55
defp deBruijnTable(14), do: 48
defp deBruijnTable(15), do: 28
defp deBruijnTable(16), do: 62
defp deBruijnTable(17), do: 5
defp deBruijnTable(18), do: 39
defp deBruijnTable(19), do: 46
defp deBruijnTable(20), do: 44
defp deBruijnTable(21), do: 42
defp deBruijnTable(22), do: 22
defp deBruijnTable(23), do: 9
defp deBruijnTable(24), do: 24
defp deBruijnTable(25), do: 35
defp deBruijnTable(26), do: 59
defp deBruijnTable(27), do: 56
defp deBruijnTable(28), do: 49
defp deBruijnTable(29), do: 18
defp deBruijnTable(30), do: 29
defp deBruijnTable(31), do: 11
defp deBruijnTable(32), do: 63
defp deBruijnTable(33), do: 52
defp deBruijnTable(34), do: 6
defp deBruijnTable(35), do: 26
defp deBruijnTable(36), do: 37
defp deBruijnTable(37), do: 40
defp deBruijnTable(38), do: 33
defp deBruijnTable(39), do: 47
defp deBruijnTable(40), do: 61
defp deBruijnTable(41), do: 45
defp deBruijnTable(42), do: 43
defp deBruijnTable(43), do: 21
defp deBruijnTable(44), do: 23
defp deBruijnTable(45), do: 58
defp deBruijnTable(46), do: 17
defp deBruijnTable(47), do: 10
defp deBruijnTable(48), do: 51
defp deBruijnTable(49), do: 25
defp deBruijnTable(50), do: 36
defp deBruijnTable(51), do: 32
defp deBruijnTable(52), do: 60
defp deBruijnTable(53), do: 20
defp deBruijnTable(54), do: 57
defp deBruijnTable(55), do: 16
defp deBruijnTable(56), do: 50
defp deBruijnTable(57), do: 31
defp deBruijnTable(58), do: 19
defp deBruijnTable(59), do: 15
defp deBruijnTable(60), do: 30
defp deBruijnTable(61), do: 14
defp deBruijnTable(62), do: 13
defp deBruijnTable(63), do: 12
end