From 5ee290deffe78bdca61d63f0a5b183b45909a37c Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Fri, 19 Apr 2024 13:47:26 +0200 Subject: [PATCH] Implement llListFindListNext and llListFindStrided --- lslopt/lslbasefuncs.py | 143 ++++++++++++++++--------- unit_tests/expr.suite/list-funcs-5.lsl | 34 ++++++ unit_tests/expr.suite/list-funcs-5.out | 34 ++++++ unit_tests/expr.suite/list-funcs-6.lsl | 15 +++ unit_tests/expr.suite/list-funcs-6.out | 15 +++ 5 files changed, 188 insertions(+), 53 deletions(-) create mode 100644 unit_tests/expr.suite/list-funcs-5.lsl create mode 100644 unit_tests/expr.suite/list-funcs-5.out create mode 100644 unit_tests/expr.suite/list-funcs-6.lsl create mode 100644 unit_tests/expr.suite/list-funcs-6.out diff --git a/lslopt/lslbasefuncs.py b/lslopt/lslbasefuncs.py index 4bdf902..a150545 100644 --- a/lslopt/lslbasefuncs.py +++ b/lslopt/lslbasefuncs.py @@ -688,6 +688,86 @@ def InternalGetDeleteSubSequence(val, start, end, isGet): return val[:end+1] + val[start:] if isGet else val[end+1:start] return val[start:end+1] if isGet else val[:start] + val[end+1:] +def InternalCompareElems(e1, e2): + """Compares two list elements according to llListFindList rules.""" + # NaN is found in floats, but not in vectors + if type(e1) == type(e2) == float: + if e1 == e2: + return True + # Exceptionally, NaN equals NaN + if math.isnan(e1) and math.isnan(e2): + return True + # Mismatch + return False + elif type(e1) == type(e2) in (Vector, Quaternion): + # Act as if the list's vector/quat was all floats, even if not + if type(e1) == Vector: + e1 = v2f(e1) + e2 = v2f(e2) + else: + e1 = q2f(e1) + e2 = q2f(e2) + # Unfortunately, Python fails to consider (NaN,) != (NaN,) sometimes + # so we need to implement our own test + for e1e,e2e in zip(e1,e2): + # NaNs are considered different to themselves here as normal + if e1e != e2e: + # Mismatch in vector/quaternion component + break + else: + # No mismatch in any component + return True + # Mismatch + return False + elif type(e1) != type(e2) or e1 != e2: + return False + return True + +def InternalListFindList(lst, elems, start, end, stride, instance): + """Generalizes llListFindList with start/end, stride and instance args.""" + L1 = len(lst) + L2 = len(elems) + if L1 == L2 == 0: + return 0 # an empty list is always found in an empty list + if L2 > L1: + return -1 # can't find a sublist longer than the original list + if start < 0: + start += L1 + if end < 0: + end += L1 + if end >= L1: + end = L1 - 1 + if start < 0 or start >= L1 or end < 0 or end >= L1 or start > end: + return -1 + if L2 == 0: + # empty list is always found at position 0 + return 0 + if stride < 1: + return -1 # stride 0 or negative returns -1 + if instance >= 0: + # Forward search + for i in xrange(start, end+2-L2, stride): + for j in xrange(L2): + if not InternalCompareElems(lst[i+j], elems[j]): + break # mismatch + else: + # no mismatch + if instance == 0: + return i + instance -= 1 + else: + # Backward search + for i in xrange(end+1-L2, start-1, -stride): + for j in xrange(L2): + if not InternalCompareElems(lst[i+j], elems[j]): + break # mismatch + else: + # no mismatch + instance += 1 + if instance == 0: + return i + return -1 + def reduce(t): t = F32(t) if not t.is_integer(): @@ -1516,64 +1596,21 @@ def llList2Vector(lst, pos): def llListFindList(lst, elems): lst = fl(lst) elems = fl(elems) - # NaN is found in floats, but not in vectors - L1 = len(lst) - L2 = len(elems) - if L2 > L1: - return -1 # can't find a sublist longer than the original list - if L2 == 0: - # empty list is always found at position 0 - return 0 - for i in xrange(L1-L2+1): - for j in xrange(L2): - e1 = lst[i+j] - e2 = elems[j] - if type(e1) == type(e2) == float: - if e1 == e2: - continue - # Exceptionally, NaN equals NaN - if math.isnan(e1) and math.isnan(e2): - continue - # Mismatch - break - elif type(e1) == type(e2) in (Vector, Quaternion): - # Act as if the list's vector/quat was all floats, even if not - if type(e1) == Vector: - e1 = v2f(e1) - e2 = v2f(e2) - else: - e1 = q2f(e1) - e2 = q2f(e2) - # Unfortunately, Python fails to consider (NaN,) != (NaN,) sometimes - # so we need to implement our own test - for e1e,e2e in zip(e1,e2): - if e1e != e2e: # NaNs are considered different to themselves here as normal - # Mismatch in vector/quaternion sub-element - break - else: - # No mismatch in any sub-element, try next list element - continue - break # discrepancy found - elif type(e1) != type(e2) or e1 != e2: - break # mismatch - else: - # no mismatch - return i - return -1 + return InternalListFindList(lst, elems, 0, -1, 1, 0) -def llListFindListNext(src, test, n): - src = fl(src) - test = fl(test) - n = fi(n) - raise eLSLCantCompute # TODO: Implement llListFindListNext +def llListFindListNext(lst, elems, instance): + lst = fl(lst) + elems = fl(elems) + instance = fi(instance) + return InternalListFindList(lst, elems, 0, -1, 1, instance) -def llListFindStrided(src, test, start, end, stride): - src = fl(src) - test = fl(test) +def llListFindStrided(lst, elems, start, end, stride): + lst = fl(lst) + elems = fl(elems) start = fi(start) end = fi(end) stride = fi(stride) - raise ELSLCantCompute # TODO: Implement llListFindStrided + return InternalListFindList(lst, elems, start, end, stride, 0) def llListInsertList(lst, elems, pos): lst = fl(lst) diff --git a/unit_tests/expr.suite/list-funcs-5.lsl b/unit_tests/expr.suite/list-funcs-5.lsl new file mode 100644 index 0000000..33bd41c --- /dev/null +++ b/unit_tests/expr.suite/list-funcs-5.lsl @@ -0,0 +1,34 @@ +[ llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b"], 0, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 2, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b"], 3, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 2, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, -2, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",2], 0, -1, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["c"], 0, -1, 2) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["c"], 0, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["c"], 0, -1, 4) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["c"], 1, -1, 2) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, -1, 2) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 4, -1, 2) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 4, -2, 2) +, llListFindStrided([0,1,2,3,4,5,6], [4], 4, 0, 2) +, llListFindStrided([], [], 3, 1, 2) +, llListFindStrided([], [], 3, 1, 0) +, llListFindStrided([], [], 0, 0, 0) +, llListFindStrided([1], [], 3, 1, 2) +, llListFindStrided([1], [], 3, 1, 1) +, llListFindStrided([1], [], 0, 1, 1) +, llListFindStrided([1], [], 0, 0, 0) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, 6, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b"], 2, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b"], 3, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b"], 4, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 2, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 4, -1, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], -99999, 7, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], -99999, 7, 1) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 3, 99999, 3) +, llListFindStrided(["a",0,"b",1,"c",2,"b",1], ["b",1], 0, 99999, 1) +] diff --git a/unit_tests/expr.suite/list-funcs-5.out b/unit_tests/expr.suite/list-funcs-5.out new file mode 100644 index 0000000..cdadac1 --- /dev/null +++ b/unit_tests/expr.suite/list-funcs-5.out @@ -0,0 +1,34 @@ +[ 2 +, 2 +, 6 +, 2 +, 6 +, -1 +, -1 +, 4 +, -1 +, 4 +, -1 +, -1 +, 6 +, -1 +, -1 +, 0 +, 0 +, 0 +, -1 +, -1 +, 0 +, 0 +, -1 +, 2 +, 6 +, -1 +, 2 +, 6 +, -1 +, -1 +, -1 +, 6 +, 2 +] \ No newline at end of file diff --git a/unit_tests/expr.suite/list-funcs-6.lsl b/unit_tests/expr.suite/list-funcs-6.lsl new file mode 100644 index 0000000..a111544 --- /dev/null +++ b/unit_tests/expr.suite/list-funcs-6.lsl @@ -0,0 +1,15 @@ +[ llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], 0) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], 1) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], 2) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], 3) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], -1) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], -2) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], -3) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["A", 0], -4) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], [3], 0) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], [<1,2,3>], -1) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], [<1,2,3>, "c"], -1) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["c"], -1) +, llListFindListNext(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["c"], 0) +, llListFindList(["A", 0, "B", 1, "C", 2, "A", 0, "A", 1, "A", "A", 0, <1,2,3>, "c"], ["c"]) +] diff --git a/unit_tests/expr.suite/list-funcs-6.out b/unit_tests/expr.suite/list-funcs-6.out new file mode 100644 index 0000000..940f86c --- /dev/null +++ b/unit_tests/expr.suite/list-funcs-6.out @@ -0,0 +1,15 @@ +[ 0 +, 6 +, 11 +, -1 +, 11 +, 6 +, 0 +, -1 +, -1 +, 13 +, 13 +, 14 +, 14 +, 14 +] \ No newline at end of file