diff --git a/develop/testing/pairs_tests.lua b/develop/testing/pairs_tests.lua index 2204a9d..bcd1238 100644 --- a/develop/testing/pairs_tests.lua +++ b/develop/testing/pairs_tests.lua @@ -99,3 +99,315 @@ do assert(evo.get(evo.pair(p1, s1), p1) == nil) assert(evo.get(evo.pair(p1, s1), s1) == nil) end + +do + local p, s1, s2 = evo.id(3) + + do + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + evo.remove(e, evo.pair(p, s1)) + + assert(not evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == nil) + + assert(evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == 42) + + evo.remove(e, evo.pair(p, s2)) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + end + + do + local e = evo.builder() + :set(evo.pair(p, s1), 21) + :set(evo.pair(p, s2), 42) + :spawn() + + evo.remove(e, evo.pair(p, s2)) + + assert(evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == 21) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + + evo.remove(e, evo.pair(p, s1)) + + assert(not evo.has(e, evo.pair(p, s1))) + assert(evo.get(e, evo.pair(p, s1)) == nil) + + assert(not evo.has(e, evo.pair(p, s2))) + assert(evo.get(e, evo.pair(p, s2)) == nil) + end +end + +do + local p1, p2, s1, s2 = evo.id(4) + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(p1, evo.ANY)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p1, evo.ANY))) + + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == 22) + assert(evo.has(e, evo.pair(p2, evo.ANY))) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(p2, evo.ANY)) + + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(not evo.has(e, evo.pair(p2, evo.ANY))) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 11) + assert(evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == 12) + assert(evo.has(e, evo.pair(p1, evo.ANY))) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, s1)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(evo.ANY, s1))) + + assert(evo.has(e, evo.pair(p1, s2))) + assert(evo.get(e, evo.pair(p1, s2)) == 12) + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == 22) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, s2)) + + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s2))) + assert(not evo.has(e, evo.pair(evo.ANY, s2))) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 11) + assert(evo.has(e, evo.pair(p2, s1))) + assert(evo.get(e, evo.pair(p2, s1)) == 21) + end + + do + local e = evo.builder() + :set(evo.pair(p1, s1), 11) + :set(evo.pair(p1, s2), 12) + :set(evo.pair(p2, s1), 21) + :set(evo.pair(p2, s2), 22) + :set(p1, s1) + :set(p2, s2) + :spawn() + + evo.remove(e, evo.pair(evo.ANY, evo.ANY)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(not evo.has(e, evo.pair(p1, s2))) + assert(not evo.has(e, evo.pair(p2, s1))) + assert(not evo.has(e, evo.pair(p2, s2))) + + assert(evo.has(e, p1) and evo.get(e, p1) == s1) + assert(evo.has(e, p2) and evo.get(e, p2) == s2) + end +end + +do + local p1, s1, p2, s2 = evo.id(4) + + local e = evo.builder() + :set(evo.pair(p1, s1), 42) + :spawn() + + evo.remove(e, evo.pair(p2, evo.ANY)) + evo.remove(e, evo.pair(evo.ANY, s2)) + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 42) + + evo.remove(e, evo.pair(p1, s1)) + + assert(not evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == nil) +end + +do + local f1, f2, f3, p1, s1, p2, s2 = evo.id(7) + evo.set(f1, evo.REQUIRES, { f2 }) + evo.set(f2, evo.DEFAULT, 84) + evo.set(f2, evo.REQUIRES, { evo.pair(p2, s2) }) + evo.set(p1, evo.REQUIRES, { f3 }) + evo.set(s1, evo.REQUIRES, { f3 }) + evo.set(p2, evo.REQUIRES, { f3 }) + evo.set(s2, evo.REQUIRES, { f3 }) + + local e = evo.builder() + :set(f1, 21) + :set(evo.pair(p1, s1), 42) + :spawn() + + assert(evo.has(e, evo.pair(p1, s1))) + assert(evo.get(e, evo.pair(p1, s1)) == 42) + + assert(evo.has(e, evo.pair(p2, s2))) + assert(evo.get(e, evo.pair(p2, s2)) == true) + + assert(evo.has(e, f1)) + assert(evo.get(e, f1) == 21) + + assert(evo.has(e, f2)) + assert(evo.get(e, f2) == 84) + + assert(not evo.has(e, f3)) + assert(evo.get(e, f3) == nil) +end + +do + local p1, p2, s1, s2 = evo.id(4) + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(p1, evo.ANY)) + evo.remove(e2, evo.pair(p1, evo.ANY)) + assert(evo.empty_all(e1, e2)) + end + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p2, s1)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, s1)) + evo.remove(e2, evo.pair(evo.ANY, s1)) + assert(evo.empty_all(e1, e2)) + end + + do + local e1 = evo.builder() + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :set(evo.pair(p2, s1)) + :set(evo.pair(p2, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) + evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) + assert(evo.empty_all(e1, e2)) + end +end + +do + local f, p1, p2, s1, s2 = evo.id(5) + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(p1, evo.ANY)) + evo.remove(e2, evo.pair(p1, evo.ANY)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p2, s1)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, s1)) + evo.remove(e2, evo.pair(evo.ANY, s1)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end + + do + local e1 = evo.builder() + :set(f, 42) + :set(evo.pair(p1, s1)) + :set(evo.pair(p1, s2)) + :set(evo.pair(p2, s1)) + :set(evo.pair(p2, s2)) + :spawn() + + local e2 = evo.clone(e1) + + evo.remove(e1, evo.pair(evo.ANY, evo.ANY)) + evo.remove(e2, evo.pair(evo.ANY, evo.ANY)) + + assert(evo.has(e1, f) and evo.has(e2, f)) + assert(not evo.has(e1, evo.pair(evo.ANY, evo.ANY))) + end +end + +-- TODO: +-- How should required fragments work with pairs? +-- How can we set defaults for paired fragments? +-- Prevent setting wildcard pairs to entities! +-- Should paired fragments be greater than common fragments? diff --git a/evolved.lua b/evolved.lua index 0eb07d1..be71c61 100644 --- a/evolved.lua +++ b/evolved.lua @@ -1385,10 +1385,6 @@ local function __chunk_without_fragment(chunk, fragment) return nil end - if not chunk.__fragment_set[fragment] then - return chunk - end - if fragment == chunk.__fragment then return chunk.__parent end @@ -1398,6 +1394,111 @@ local function __chunk_without_fragment(chunk, fragment) if without_fragment_edge then return without_fragment_edge end end + if fragment < 0 and chunk.__has_pairs then + local primary_index = (0 - fragment) % 0x100000 + local secondary_index = (0 - fragment - primary_index) / 0x100000 + + local primary = __freelist_ids[primary_index] --[[@as evolved.id]] + local secondary = __freelist_ids[secondary_index] --[[@as evolved.id]] + + if primary == __ANY and secondary == __ANY then + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pairs do + sib_chunk = sib_chunk.__parent + end + + local ini_fragment_list = chunk.__fragment_list + local ini_fragment_count = chunk.__fragment_count + + for ini_fragment_index = 1, ini_fragment_count do + local ini_fragment = ini_fragment_list[ini_fragment_index] + if ini_fragment > 0 then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) + end + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + elseif primary == __ANY then + local sec_pairs = chunk.__secondary_pairs[secondary] + + if not sec_pairs then + return chunk + end + + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pairs and sib_chunk.__secondary_pairs[secondary] do + sib_chunk = sib_chunk.__parent + end + + local ini_fragment_list = chunk.__fragment_list + local ini_fragment_count = chunk.__fragment_count + + for ini_fragment_index = 1, ini_fragment_count do + local ini_fragment = ini_fragment_list[ini_fragment_index] + if ini_fragment > 0 then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) + else + local _, ini_secondary = __evolved_unpair(ini_fragment) + if secondary ~= ini_secondary then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) + end + end + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + elseif secondary == __ANY then + local pri_pairs = chunk.__primary_pairs[primary] + + if not pri_pairs then + return chunk + end + + local sib_chunk = chunk.__parent + + while sib_chunk and sib_chunk.__has_pairs and sib_chunk.__primary_pairs[primary] do + sib_chunk = sib_chunk.__parent + end + + local ini_fragment_list = chunk.__fragment_list + local ini_fragment_count = chunk.__fragment_count + + for ini_fragment_index = 1, ini_fragment_count do + local ini_fragment = ini_fragment_list[ini_fragment_index] + if ini_fragment > 0 then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) + else + local ini_primary, _ = __evolved_unpair(ini_fragment) + if primary ~= ini_primary then + sib_chunk = __chunk_with_fragment(sib_chunk, ini_fragment) + end + end + end + + if sib_chunk then + chunk.__without_fragment_edges[fragment] = sib_chunk + sib_chunk.__with_fragment_edges[fragment] = chunk + end + + return sib_chunk + end + end + + if not chunk.__fragment_set[fragment] then + return chunk + end + if fragment < chunk.__fragment then local sibling_chunk = __chunk_with_fragment( __chunk_without_fragment(chunk.__parent, fragment),