From f3060c0f1e21ce946a9fa21ab4c8bc1cf3c8125c Mon Sep 17 00:00:00 2001 From: Oldes Huhuman Date: Tue, 2 Apr 2024 11:40:13 +0200 Subject: [PATCH] FEAT: implemented map comparison resolves: https://github.com/Oldes/Rebol-issues/issues/2341 resolves: https://github.com/Oldes/Rebol-issues/issues/2438 --- src/core/t-map.c | 62 +++++++++++++++++++++++++++++++++---- src/tests/units/map-test.r3 | 48 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/core/t-map.c b/src/core/t-map.c index 0dc6e8d24e..97be8b11ea 100644 --- a/src/core/t-map.c +++ b/src/core/t-map.c @@ -76,9 +76,60 @@ { if (mode < 0) return -1; if (mode == 3) return VAL_SERIES(a) == VAL_SERIES(b); - return 0 == Cmp_Block(a, b, 0); + return 0 == Cmp_Map(a, b, mode == 2); } +/*********************************************************************** +** +*/ REBINT Cmp_Map(REBVAL* sval, REBVAL* tval, REBFLG is_case) +/* +** Compare two maps and return 0 if maps have same keys and equal values. +** Keys may be in different order! +** +***********************************************************************/ +{ + REBVAL* key; + REBVAL* val; + REBCNT idx; + REBSER* hser; + REBCNT* hashes = NULL; + REBCNT slen, tlen; + + if (VAL_SERIES(sval) == VAL_SERIES(tval)) + return 0; + + // Compare real map lengths (ignoring deleted values) + slen = Length_Map(VAL_SERIES(sval)); + tlen = Length_Map(VAL_SERIES(tval)); + if (slen != tlen) return -1; + + hser = VAL_SERIES(tval)->series; + if (VAL_TAIL(sval) < VAL_TAIL(tval)) { + // Make sure that the larger map will be on the left side! + val = sval; sval = tval; tval = val; + } + + hser = VAL_SERIES(tval)->series; + if (hser) hashes = (REBCNT*)hser->data; + + // Traverse all keys of the left map and compare values if found in the second map + for (key = VAL_BLK(sval); NOT_END(key) && NOT_END(key + 1); key += 2) { + if (VAL_MAP_REMOVED(key)) continue; // ignore deleted key + idx = Find_Key(VAL_SERIES(tval), hser, key, 2, is_case, 1); + if (idx == NOT_FOUND) return -1; // stop if the target key is not found + if (hashes) { + // the target map has a hash table, so get the real index of the key + idx = ((hashes[idx] - 1) * 2); + // check if the target key is not removed; if so, we can end + if (VAL_MAP_REMOVED(VAL_BLK_SKIP(tval,idx))) return -1; + } + // compare both values + if (Cmp_Value(key + 1, VAL_BLK_SKIP(tval, idx + 1), is_case) != 0) return -1; + } + return 0; +} + + /*********************************************************************** ** @@ -134,7 +185,7 @@ // Append new value the target series: Append_Series(series, (REBYTE*)key, wide); } - return -1; + return NOT_FOUND; } return hash; } @@ -331,7 +382,7 @@ /*********************************************************************** ** -*/ REBINT Length_Map(REBSER *series) +*/ REBCNT Length_Map(REBSER *series) /* ***********************************************************************/ { @@ -543,7 +594,7 @@ { REBVAL *val = D_ARG(1); REBVAL *arg = D_ARG(2); - REBINT n = 0; + REBCNT n = 0; REBSER *series = VAL_SERIES(val); // Check must be in this order (to avoid checking a non-series value); @@ -565,8 +616,7 @@ if (!IS_BLOCK(arg)) Trap_Arg(val); *D_RET = *val; if (DS_REF(AN_DUP)) { - n = Int32(DS_ARG(AN_COUNT)); - if (n <= 0) break; + Trap0(RE_BAD_REFINES); } Append_Map(series, arg, Partial1(arg, D_ARG(AN_LENGTH))); break; diff --git a/src/tests/units/map-test.r3 b/src/tests/units/map-test.r3 index 1eae9a629c..ad61ac4c04 100644 --- a/src/tests/units/map-test.r3 +++ b/src/tests/units/map-test.r3 @@ -136,6 +136,54 @@ Rebol [ ===end-group=== +===start-group=== "compare" + ;@@ https://github.com/Oldes/Rebol-issues/issues/2341 + --test-- "equal?" + --assert equal? #[a: 1 b: 2] #[b: 2 a: 1] + --assert equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 c: "A"] + --assert equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 c "A"] + --assert equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 'c "A"] + --assert not equal? #[a: 1] #[b: 2 c: 3 a: 1] + --assert not equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1] + + --test-- "strict-equal?" + --assert strict-equal? #[a: 1 b: 2] #[b: 2 a: 1] + --assert not strict-equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 c: "A"] + --assert not strict-equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 c "A"] + --assert not strict-equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 'c "A"] + --assert not strict-equal? #[a: 1] #[b: 2 c: 3 a: 1] + --assert not strict-equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1] + + ;; repeating above tests with large enough maps, because hashing is not used in small enough maps + + --test-- "equal? (large maps)" + --assert equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2] #[b: 2 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 c: "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 c "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 'c "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not equal? #[a: 1] #[b: 2 c: 3 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + + --test-- "strict-equal? (large maps)" + --assert strict-equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 ] #[b: 2 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not strict-equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 c: "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not strict-equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 c "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not strict-equal? #[e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9 a: 1 b: 2 c: "a"] #[b: 2 a: 1 'c "A" e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not strict-equal? #[a: 1] #[b: 2 c: 3 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + --assert not strict-equal? #[a: 1 b: 2 c: "a"] #[b: 2 a: 1 e: 1 f: 2 g: 3 h: 4 i: 5 j: 6 k: 7 l: 8 m: 9] + + ;@@ https://github.com/Oldes/Rebol-issues/issues/2438 + --test-- "equal? (after remove)" + m: remove/key #[a: 1 b: 2] 'b + --assert equal? :m #[a: 1] + --assert equal? #[a: 1] :m + --assert not equal? :m #[a: 1 b: 2] + --assert not equal? #[a: 1 b: 2] :m + + +===end-group=== + + ===start-group=== "map issues" ;@@ https://github.com/Oldes/Rebol-issues/issues/699 --test-- "map-issue-699"