Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve C scanner conditional inclusion #4626

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

mwichmann
Copy link
Collaborator

@mwichmann mwichmann commented Nov 7, 2024

Simplistic macro replacement is now done on the contents of CPPDEFINES, to improve accuracy of conditional inclusion as compared to the real preprocessor (ref: issue #4623). The replacement will happen for both the Conditional and the original C scanner, but since the original does not attempt to process the logic around conditional includes, it will simply have no effect on it. EDITED: now only applied to Conditional scanner.

This should have no doc impacts: we don't say how scanners determine implicit dependencies, so a small tweak to that process is more like a bugfix.

Contributor Checklist:

  • I have created a new test or updated the unit tests to cover the new/changed functionality.
  • I have updated CHANGES.txt and RELEASE.txt (and read the README.rst).
  • I have updated the appropriate documentation

@bdbaddog
Copy link
Contributor

Seems like this could have significant performance impact? And really it's only impacting the conditional C scanner?

@mwichmann
Copy link
Collaborator Author

Seems like this could have significant performance impact? And really it's only impacting the conditional C scanner?

For the vast majority of cases, it's a single iteration over the dict made from CPPDEFINES. Doesn't seem that expensive. If there are replaceable elements, it will make as many additional passes as needed. As noted (somewhere...), this could be guarded by a flag, or moved to the conditional scanner - although it's a little bit more awkward to implement there.

old_ns = mapping
ns = {}
while True:
ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will loop forever if one variable self references.

-DXYZ=XYZ

Copy link
Collaborator Author

@mwichmann mwichmann Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not convinced... if you have {'XYZ': 'XYZ'}, then k gets the looked-up value old_ns['XYZ'], which is 'XYZ', which is actually the same as it was before. Thus, the following check finds that old_ns and ns have the same values, so it breaks out of the loop. There might still be a way to get into a pathological state, but I don't think that example is enough.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps if you have three defines that circularly point to each other? Is that worth worrying about? cpp does detect that as a pathological case and errors out.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this to line 581 in CTests.py and run..

                    ('XYZ','XYZ')

Copy link
Contributor

@bdbaddog bdbaddog Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm. it completed. Trying 3 way loop..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect two way (A: B, B: A) to work as well - first pass you'll have (A: A, B: B) and then there's no more work to do. Three-way i'm pretty sure will loop. We could decide that it's pathlogical to do more than two transformations, and not try to loop, or bail on a loop counter with an exception, if you think either is worthwhile.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 way loop hangs infinitely... I think that'd be BAD...
subst() has an implementation to avoid this BTW.
Could use that to do the resolution.. of course that'd be slower.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, of course it's bad, but who would do something like that? I think fairly simple measures should be enough, as noted above. It's not supposed to be a perfect tool, as the C scanner has a limited job - unlike subst, which has to come out just right.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example - no-loop, just two-pass:

        ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()}
        if old_ns == ns:  # there were no substitutions
            return ns
        # do one more round
        return {k: ns[v] if v in ns else v for k, v in ns.items()}

@mwichmann
Copy link
Collaborator Author

mwichmann commented Nov 26, 2024

So to be able to wrap this one up, two questions (update: I've ticked the two boxes that seem to be preferred):

  • Switch substitution to two passes, the second used only if the first made any replacements, but never go further than two replacement passes.
  • Add a counter to the replacement loop, and bail out if it reaches some arbitrary number and is still changing (say - 5 loops).
  • Leave substitution loop as is, on the basis that a three-way (or more) circular redefining of variables in CPPDEFINES is just not something someone would do.

and:

  • Add a check so the replacement is only done if asked for, and have the conditional scanner ask while the classic scanner does not.
  • Leave current version where replacement is done for both C scanners.

@bdbaddog
Copy link
Contributor

I'd say 5 loops is sufficient, I'd definitely note this in the release/changes.
as long as it doesn't impact regular c scanner.
I'd even say make two versions of the function so it can't affect regular c scanner.
Since the scanner gets hit very hard in a decent sized build.

Simplistic macro replacement is now done on the contents of CPPDEFINES,
to improve accuracy of conditional inclusion as compared to the real
preprocessor (ref: issue SCons#4623).

Signed-off-by: Mats Wichmann <[email protected]>
Replacement is now limited to five passes, to avoid going into an endless
loop in pathlogical cases where there are three or more macros that
circularly refer to each other. No error is reported in this case.

Replacement is now only done for the otional C Conditional scanner,
not for the classical C scanner.

Signed-off-by: Mats Wichmann <[email protected]>
@jcbrill
Copy link
Contributor

jcbrill commented Nov 27, 2024

One solution is to keep a dictionary of the initial value and the processed values. If the processed value is in the dictionary before adding it, then a cycle is detected.

If a cycle is detected, there is a question of which value should be returned. One school of thought is the "prior value" before the cycle (i.e., the most "forward" progress).

This technique was used in my own work for multiple levels of environment variable substitution which is effectively the same problem.

@mwichmann
Copy link
Collaborator Author

Yes, of course there are ways to detect it (and thanks for the note!!). Trying to keep it lightweight, at the moment... a cycle should be a super-uncommon case. I hope.

@jcbrill
Copy link
Contributor

jcbrill commented Nov 28, 2024

A Thanksgiving gift [last revision 2024-12-06 11:50 EST].

Bugs possible.

Source Code

from typing import Dict

### PR IMPLEMENTATION

def _replace(mapping: Dict) -> Dict:
    old_ns = mapping
    loops = 0
    while loops < 5:  # don't recurse forever in case there's circular data
        ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()}
        if old_ns == ns:
            break
        old_ns = ns
        loops += 1
    return ns

### MODIFIED PR IMPLEMENTATION

def _replace_mod(mapping: Dict) -> Dict:
    old_ns = mapping
    loops = 0
    while loops < 5:  # don't recurse forever in case there's circular data
        again = False
        ns = {}
        for k, v in old_ns.items():
            if v in old_ns:
                ns[k] = old_ns[v]
                if not again and ns[k] != v:
                    again = True
            else:
                ns[k] = v
        if not again:
            break
        old_ns = ns
        loops += 1
    return ns

### ALTERNATE PR IMPLEMENTATION

def _replace_alt(mapping: Dict) -> Dict:

    replace_list = []
    resolved_map = {}

    for key, val in mapping.items():

        if val not in mapping:
            resolved_map[key] = val
        elif val != key:
            replace_list.append((key,val))
        else:
            # cycle (self-referential)
            resolved_map[key] = val

    if not replace_list:

        mapping = resolved_map

    else:

        mapping = dict(mapping)

        for key, val in replace_list:

            if val in resolved_map:
                mapping[key] = resolved_map[val]
                continue

            replace_seen = set()
            replace_trail = [val]

            while val in mapping:

                nextval = mapping[val]
                if nextval in resolved_map:
                    val = resolved_map[nextval]
                    break

                replace_seen.add(val)
                if nextval in replace_seen:
                    # cycle detected
                    val = replace_trail[-1]
                    break

                replace_trail.append(nextval)
                val = nextval

            for resolved_key in replace_trail:
                resolved_map[resolved_key] = val

            mapping[key] = val

    return mapping

if __name__ == "__main__":

    import gc
    import platform
    import time

    TESTING = False
    TESTING_EXPS = [10, 20, 21]
    TESTING_GC = [False]

    NINVOCATIONS = pow(10, 7)  # exp >= 5 for elapsed time division

    ### TIMING

    def profile(func, mapping, ncalls=NINVOCATIONS, gcenabled=True):
        gc.collect()
        if not gcenabled:
            gc.disable()
        rval = None
        begtime = time.perf_counter()
        while ncalls > 0:
            ncalls -= 1
            rval = func(mapping)
        endtime = time.perf_counter()
        if not gcenabled:
            gc.enable()
        gc.collect()
        elapsed = endtime - begtime
        return rval, elapsed

    ### TESTING

    print(f"python version = {platform.python_version()!s}")
    print(f"number of invocations = {NINVOCATIONS:,}")
    for gcenabled in (True, False):

        if TESTING and TESTING_GC and gcenabled not in TESTING_GC:
            continue

        gcstatus = "Y" if gcenabled else "N"

        testnum = 0
        for CPPDEFINES, expect in [
            (
                #01 no replacements
                [("STRING1", "VALUE1"), ("STRING2", "VALUE2"), ("STRING3", "VALUE3")],
                {"STRING1": "VALUE1", "STRING2": "VALUE2", "STRING3": "VALUE3"},
            ),
            (
                #02 no replacements
                [("STRING1", "STRING1"), ("STRING2", "STRING2"), ("STRING3", "STRING3")],
                {"STRING1": "STRING1", "STRING2": "STRING2", "STRING3": "STRING3"},
            ),
            (
                #03 single replacement
                [("STRING", "VALUE"), ("REPLACEABLE", "RVALUE"), ("RVALUE", "AVALUE")],
                # Alternate algorithm replacement steps:
                #   [REPLACEABLE] RVALUE = AVALUE
                {"STRING": "VALUE", "REPLACEABLE": "AVALUE", "RVALUE": "AVALUE"},
            ),
            (
                #04 single replacement (first)
                [("AAA", "BBB"), ("BBB", "VALUE1"), ("CCC", "VALUE2"), ("DDD", "VALUE3"), ("EEE", "VALUE4"), ("FFF", "VALUE5")],
                # Alternate algorithm replacement steps:
                #   [AAA] BBB = VALUE1
                {"AAA": "VALUE1", "BBB": "VALUE1", "CCC": "VALUE2", "DDD": "VALUE3", "EEE": "VALUE4", "FFF": "VALUE5"},
            ),
            (
                #05 single replacement (last)
                [("AAA", "VALUE1"), ("BBB", "VALUE2"), ("CCC", "VALUE3"), ("DDD", "VALUE4"), ("EEE", "VALUE5"), ("FFF", "EEE")],
                # Alternate algorithm replacement steps:
                #   [FFF] EEE = VALUE5
                {"AAA": "VALUE1", "BBB": "VALUE2", "CCC": "VALUE3", "DDD": "VALUE4", "EEE": "VALUE5", "FFF": "VALUE5"},
            ),
            (
                #06 multiple replacements
                [("AAA", "XXX"), ("BBB", "YYY"), ("CCC", "VALUE1"), ("DDD", "VALUE2"), ("XXX", "DDD"), ("YYY", "CCC")],
                # Alternate algorithm replacement steps:
                #   [AAA] XXX = DDD = VALUE2
                #   [BBB] YYY = CCC = VALUE1
                #   [XXX] VALUE2
                #   [YYY] VALUE1
                {"AAA": "VALUE2", "BBB": "VALUE1", "CCC": "VALUE1", "DDD": "VALUE2", "XXX": "VALUE2", "YYY": "VALUE1"},
            ),
            (
                #07 multiple replacements
                [("AAA", "BBB"), ("BBB", "VALUE1"), ("CCC", "DDD"), ("DDD", "VALUE2"), ("EEE", "FFF"), ("FFF", "VALUE3")],
                # Alternate algorithm replacement steps:
                #   [AAA] BBB = VALUE1
                #   [CCC] DDD = VALUE2
                #   [EEE] FFF = VALUE3
                {"AAA": "VALUE1", "BBB": "VALUE1", "CCC": "VALUE2", "DDD": "VALUE2", "EEE": "VALUE3", "FFF": "VALUE3"},
            ),
            (
                #08 multiple replacements
                [("AAA", "BBB"), ("BBB", "VALUE1"), ("CCC", "AAA"), ("DDD", "BBB"), ("EEE", "AAA"), ("FFF", "BBB")],
                # Alternate algorithm replacement steps:
                #   [AAA] BBB = VALUE1
                #   [CCC] AAA = VALUE1
                #   [DDD] BBB = VALUE1
                #   [EEE] AAA = VALUE1
                #   [FFF] BBB = VALUE1
                {"AAA": "VALUE1", "BBB": "VALUE1", "CCC": "VALUE1", "DDD": "VALUE1", "EEE": "VALUE1", "FFF": "VALUE1"},
            ),
            (
                #09 long trail, single value
                [("A", "B"), ("B", "C"), ("C", "D"), ("D", "E"), ("E", "F"), ("F", "G"), ("G", "H"), ("H", "I"), ("I", "J"), ("J", "K"), ("K", "0")],
                # Algorithm replacement steps:
                #   [A] B = C = D = E = F = G = H = I = J = K = 0
                #   [B] 0
                #   ...
                #   [J] 0
                {"A": "0", "B": "0", "C": "0", "D": "0", "E": "0", "F": "0", "G": "0", "H": "0", "I": "0", "J": "0", "K": "0"},
            ),
            (
                #10 cycles
                [("ABC", "DEF"), ("DEF", "GHI"), ("GHI", "ABC"), ("XXX", "YYY"), ("YYY", "ZZZ"), ("ZZZ", "DEF")],
                # Algorithm replacement steps:
                #   [ABC] DEF = GHI = ABC
                #   [DEF] ABC
                #   [GHI] ABC
                #   [XXX] YYY = ZZZ = DEF = ABC
                #   [YYY] ABC
                #   [ZZZ] ABC
                {"ABC": "ABC", "DEF": "ABC", "GHI": "ABC", "XXX": "ABC", "YYY": "ABC", "ZZZ": "ABC"},
            ),
            (
                #11 none
                [("A", 0), ("B", 1), ("C", 2), ("D", 3), ("E", 4), ("F", 5), ("G", 6), ("H", 7)],
                {"A": 0, "B": 1, "C": 2, "D": 3, "E": 4, "F": 5, "G": 6, "H": 7},
            ),
            (
                #12 1 trail: best case
                [("A", 0), ("B", "A"), ("C", "B"), ("D", "C"), ("E", "D"), ("F", "E"), ("G", "F"), ("H", "G")],
                {"A": 0, "B": 0, "C": 0, "D": 0, "E": 0, "F": 0, "G": 0, "H": 0},
            ),
            (
                #13 2 trails: best case, worst case
                [("A", 0), ("B", "A"), ("C", "B"), ("D", "C"), ("E", "F"), ("F", "G"), ("G", "H"), ("H", 1)],
                {"A": 0, "B": 0, "C": 0, "D": 0, "E": 1, "F": 1, "G": 1, "H": 1},
            ),
            (
                #14 1 trail: worst case
                [("A", "B"), ("B", "C"), ("C", "D"), ("D", "E"), ("E", "F"), ("F", "G"), ("G", "H"), ("H", 0)],
                {"A": 0, "B": 0, "C": 0, "D": 0, "E": 0, "F": 0, "G": 0, "H": 0},
            ),
            (
                #15 none
                [("A", "0"), ("B", "1"), ("C", "2"), ("D", "3"), ("E", "4"), ("F", "5"), ("G", "6"), ("H", "7")],
                {"A": "0", "B": "1", "C": "2", "D": "3", "E": "4", "F": "5", "G": "6", "H": "7"},
            ),
            (
                #16 1 trail: best case
                [("A", "0"), ("B", "A"), ("C", "B"), ("D", "C"), ("E", "D"), ("F", "E"), ("G", "F"), ("H", "G")],
                {"A": "0", "B": "0", "C": "0", "D": "0", "E": "0", "F": "0", "G": "0", "H": "0"},
            ),
            (
                #17 2 trails: best case, worst case
                [("A", "0"), ("B", "A"), ("C", "B"), ("D", "C"), ("E", "F"), ("F", "G"), ("G", "H"), ("H", "1")],
                {"A": "0", "B": "0", "C": "0", "D": "0", "E": "1", "F": "1", "G": "1", "H": "1"},
            ),
            (
                #18 1 trail: worst case
                [("A", "B"), ("B", "C"), ("C", "D"), ("D", "E"), ("E", "F"), ("F", "G"), ("G", "H"), ("H", "0")],
                {"A": "0", "B": "0", "C": "0", "D": "0", "E": "0", "F": "0", "G": "0", "H": "0"},
            ),
            (
                #19 trail length = 33
                [
                    ("A0", "B0"), ("B0", "C0"), ("C0", "D0"), ("D0", "E0"), ("E0", "F0"), ("F0", "G0"), ("G0", "H0"), ("H0", "I0"), ("I0", "J0"), ("J0", "K0"),
                    ("K0", "L0"), ("L0", "M0"), ("M0", "N0"), ("N0", "O0"), ("O0", "P0"), ("P0", "Q0"), ("Q0", "R0"), ("R0", "S0"), ("S0", "T0"), ("T0", "U0"),
                    ("U0", "V0"), ("V0", "W0"), ("W0", "X0"), ("X0", "Y0"), ("Y0", "Z0"), ("Z0", "A1"), ("A1", "B1"), ("B1", "C1"), ("C1", "D1"), ("D1", "E1"),
                    ("E1", "F1"), ("F1", "G1"), ("G1", "2"),
                ],
                {   "A0": "2", "B0": "2", "C0": "2", "D0": "2", "E0": "2", "F0": "2", "G0": "2", "H0": "2", "I0": "2", "J0": "2",
                    "K0": "2", "L0": "2", "M0": "2", "N0": "2", "O0": "2", "P0": "2", "Q0": "2", "R0": "2", "S0": "2", "T0": "2",
                    "U0": "2", "V0": "2", "W0": "2", "X0": "2", "Y0": "2", "Z0": "2", "A1": "2", "B1": "2", "C1": "2", "D1": "2",
                    "E1": "2", "F1": "2", "G1": "2",
                },
            ),
            (
                #20: cycle
                [("A", "B"), ("B", "A")],

                {"A": "A", "B": "A"},
            ),
            (
                #21: cycle
                [("A", "B"), ("B", "C"), ("C", "A")],
                {"A": "A", "B": "A", "C": "A"},
            ),
        ]:
            testnum += 1
            if TESTING and TESTING_EXPS and testnum not in TESTING_EXPS:
                continue
            print("")
            mapping = {k: v for k, v in CPPDEFINES}

            print(f"{gcstatus} {testnum:02d} CPPDEFINES       {mapping}")

            rval_cur, elapsed_cur = profile(_replace, mapping, ncalls=NINVOCATIONS, gcenabled=gcenabled)
            status_cur = 'pass' if rval_cur == expect else 'FAIL'
            print(f"{gcstatus} {testnum:02d} cur {status_cur} {elapsed_cur:7.4f} {rval_cur}")

            rval_mod, elapsed_mod = profile(_replace_mod, mapping, ncalls=NINVOCATIONS, gcenabled=gcenabled)
            status_mod = 'pass' if rval_mod == expect else 'FAIL'
            print(f"{gcstatus} {testnum:02d} mod {status_mod} {elapsed_mod:7.4f} {rval_mod}")

            rval_alt, elapsed_alt = profile(_replace_alt, mapping, ncalls=NINVOCATIONS, gcenabled=gcenabled)
            status_alt = 'pass' if rval_alt == expect else 'FAIL'
            print(f"{gcstatus} {testnum:02d} alt {status_alt} {elapsed_alt:7.4f} {rval_alt}")
            
            rel = elapsed_mod / elapsed_cur
            which = "mod" if rel < 1.0 else "CUR"
            rel_error = ((elapsed_cur - elapsed_mod) / elapsed_mod) * 100.0
            delta = (elapsed_cur - elapsed_mod) / NINVOCATIONS
            print(f"{gcstatus} {testnum:02d} {which} ---- cur/mod {rel:7.4f} (rel_error = {rel_error:6.2f}% avg_delta = {delta:11.4E})")

            rel = elapsed_alt / elapsed_cur
            which = "alt" if rel < 1.0 else "CUR"
            rel_error = ((elapsed_cur - elapsed_alt) / elapsed_alt) * 100.0
            delta = (elapsed_cur - elapsed_alt) / NINVOCATIONS
            print(f"{gcstatus} {testnum:02d} {which} ---- cur/alt {rel:7.4f} (rel_error = {rel_error:6.2f}% avg_delta = {delta:11.4E})")

            rel = elapsed_alt / elapsed_mod
            which = "alt" if rel < 1.0 else "MOD"
            rel_error = ((elapsed_mod - elapsed_alt) / elapsed_alt) * 100.0
            delta = (elapsed_mod - elapsed_alt) / NINVOCATIONS
            print(f"{gcstatus} {testnum:02d} {which} ---- mod/alt {rel:7.4f} (rel_error = {rel_error:6.2f}% avg_delta = {delta:11.4E})")

Batch File

@setlocal

@call e:\python\python-embed 3.7
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-37.txt 

@call e:\python\python-embed 3.8
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-38.txt 

@call e:\python\python-embed 3.9
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-39.txt 

@call e:\python\python-embed 3.10
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-310.txt 

@call e:\python\python-embed 3.11
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-311.txt 

@call e:\python\python-embed 3.12
@start "Python" /realtime /wait /B "%PYEXE%" c-replace.py > output-312.txt 

@endlocal

Raw Output

Test and profiling output [Python 3.7]:

python version = 3.7.9
number of invocations = 10,000,000

Y 01 CPPDEFINES       {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 cur pass  5.7664 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 mod pass  4.2834 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 alt pass  4.1108 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 mod ---- cur/mod  0.7428 (rel_error =  34.62% avg_delta =  1.4830E-07)
Y 01 alt ---- cur/alt  0.7129 (rel_error =  40.27% avg_delta =  1.6555E-07)
Y 01 alt ---- mod/alt  0.9597 (rel_error =   4.20% avg_delta =  1.7259E-08)

Y 02 CPPDEFINES       {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 cur pass  6.2119 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 mod pass  5.3964 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 alt pass  4.5210 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 mod ---- cur/mod  0.8687 (rel_error =  15.11% avg_delta =  8.1552E-08)
Y 02 alt ---- cur/alt  0.7278 (rel_error =  37.40% avg_delta =  1.6910E-07)
Y 02 alt ---- mod/alt  0.8378 (rel_error =  19.36% avg_delta =  8.7544E-08)

Y 03 CPPDEFINES       {'STRING': 'VALUE', 'REPLACEABLE': 'RVALUE', 'RVALUE': 'AVALUE'}
Y 03 cur pass 10.6086 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 mod pass  7.9254 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 alt pass  7.5913 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 mod ---- cur/mod  0.7471 (rel_error =  33.86% avg_delta =  2.6833E-07)
Y 03 alt ---- cur/alt  0.7156 (rel_error =  39.75% avg_delta =  3.0173E-07)
Y 03 alt ---- mod/alt  0.9578 (rel_error =   4.40% avg_delta =  3.3406E-08)

Y 04 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 cur pass 14.6407 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 mod pass 12.0367 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 alt pass  9.6571 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 mod ---- cur/mod  0.8221 (rel_error =  21.63% avg_delta =  2.6039E-07)
Y 04 alt ---- cur/alt  0.6596 (rel_error =  51.60% avg_delta =  4.9835E-07)
Y 04 alt ---- mod/alt  0.8023 (rel_error =  24.64% avg_delta =  2.3796E-07)

Y 05 CPPDEFINES       {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'EEE'}
Y 05 cur pass 14.8194 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 mod pass 12.1720 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 alt pass  9.7445 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 mod ---- cur/mod  0.8214 (rel_error =  21.75% avg_delta =  2.6474E-07)
Y 05 alt ---- cur/alt  0.6575 (rel_error =  52.08% avg_delta =  5.0749E-07)
Y 05 alt ---- mod/alt  0.8006 (rel_error =  24.91% avg_delta =  2.4275E-07)

Y 06 CPPDEFINES       {'AAA': 'XXX', 'BBB': 'YYY', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'DDD', 'YYY': 'CCC'}
Y 06 cur pass 22.1211 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 mod pass 19.0174 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 alt pass 18.2276 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 mod ---- cur/mod  0.8597 (rel_error =  16.32% avg_delta =  3.1036E-07)
Y 06 alt ---- cur/alt  0.8240 (rel_error =  21.36% avg_delta =  3.8935E-07)
Y 06 alt ---- mod/alt  0.9585 (rel_error =   4.33% avg_delta =  7.8985E-08)

Y 07 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'DDD', 'DDD': 'VALUE2', 'EEE': 'FFF', 'FFF': 'VALUE3'}
Y 07 cur pass 14.7092 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 mod pass 11.9902 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 alt pass 12.1500 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 mod ---- cur/mod  0.8152 (rel_error =  22.68% avg_delta =  2.7189E-07)
Y 07 alt ---- cur/alt  0.8260 (rel_error =  21.06% avg_delta =  2.5592E-07)
Y 07 MOD ---- mod/alt  1.0133 (rel_error =  -1.31% avg_delta = -1.5973E-08)

Y 08 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'AAA', 'DDD': 'BBB', 'EEE': 'AAA', 'FFF': 'BBB'}
Y 08 cur pass 22.6183 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 mod pass 19.3347 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 alt pass 18.2430 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 mod ---- cur/mod  0.8548 (rel_error =  16.98% avg_delta =  3.2836E-07)
Y 08 alt ---- cur/alt  0.8066 (rel_error =  23.98% avg_delta =  4.3753E-07)
Y 08 alt ---- mod/alt  0.9435 (rel_error =   5.98% avg_delta =  1.0917E-07)

Y 09 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 'I', 'I': 'J', 'J': 'K', 'K': '0'}
Y 09 cur pass 58.0317 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 mod pass 55.0648 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 alt pass 40.1179 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 mod ---- cur/mod  0.9489 (rel_error =   5.39% avg_delta =  2.9669E-07)
Y 09 alt ---- cur/alt  0.6913 (rel_error =  44.65% avg_delta =  1.7914E-06)
Y 09 alt ---- mod/alt  0.7286 (rel_error =  37.26% avg_delta =  1.4947E-06)

Y 10 CPPDEFINES       {'ABC': 'DEF', 'DEF': 'GHI', 'GHI': 'ABC', 'XXX': 'YYY', 'YYY': 'ZZZ', 'ZZZ': 'DEF'}
Y 10 cur FAIL 38.6350 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
Y 10 mod FAIL 34.8835 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
Y 10 alt pass 26.5676 {'ABC': 'ABC', 'DEF': 'ABC', 'GHI': 'ABC', 'XXX': 'ABC', 'YYY': 'ABC', 'ZZZ': 'ABC'}
Y 10 mod ---- cur/mod  0.9029 (rel_error =  10.75% avg_delta =  3.7515E-07)
Y 10 alt ---- cur/alt  0.6877 (rel_error =  45.42% avg_delta =  1.2067E-06)
Y 10 alt ---- mod/alt  0.7616 (rel_error =  31.30% avg_delta =  8.3159E-07)

Y 11 CPPDEFINES       {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 cur pass  9.2838 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 mod pass  7.5283 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 alt pass  7.1827 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 mod ---- cur/mod  0.8109 (rel_error =  23.32% avg_delta =  1.7554E-07)
Y 11 alt ---- cur/alt  0.7737 (rel_error =  29.25% avg_delta =  2.1010E-07)
Y 11 alt ---- mod/alt  0.9541 (rel_error =   4.81% avg_delta =  3.4560E-08)

Y 12 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
Y 12 cur pass 35.7833 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 mod pass 31.5426 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 alt pass 36.8408 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 mod ---- cur/mod  0.8815 (rel_error =  13.44% avg_delta =  4.2407E-07)
Y 12 CUR ---- cur/alt  1.0296 (rel_error =  -2.87% avg_delta = -1.0575E-07)
Y 12 MOD ---- mod/alt  1.1680 (rel_error = -14.38% avg_delta = -5.2982E-07)

Y 13 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 1}
Y 13 cur pass 26.6193 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 mod pass 23.4456 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 alt pass 28.9186 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 mod ---- cur/mod  0.8808 (rel_error =  13.54% avg_delta =  3.1737E-07)
Y 13 CUR ---- cur/alt  1.0864 (rel_error =  -7.95% avg_delta = -2.2993E-07)
Y 13 MOD ---- mod/alt  1.2334 (rel_error = -18.93% avg_delta = -5.4730E-07)

Y 14 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 0}
Y 14 cur pass 34.9275 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 mod pass 31.6326 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 alt pass 29.9881 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 mod ---- cur/mod  0.9057 (rel_error =  10.42% avg_delta =  3.2950E-07)
Y 14 alt ---- cur/alt  0.8586 (rel_error =  16.47% avg_delta =  4.9394E-07)
Y 14 alt ---- mod/alt  0.9480 (rel_error =   5.48% avg_delta =  1.6444E-07)

Y 15 CPPDEFINES       {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 cur pass  9.0637 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 mod pass  7.3423 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 alt pass  7.0986 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 mod ---- cur/mod  0.8101 (rel_error =  23.44% avg_delta =  1.7213E-07)
Y 15 alt ---- cur/alt  0.7832 (rel_error =  27.68% avg_delta =  1.9651E-07)
Y 15 alt ---- mod/alt  0.9668 (rel_error =   3.43% avg_delta =  2.4375E-08)

Y 16 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
Y 16 cur pass 35.0913 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 mod pass 30.2018 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 alt pass 36.0384 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 mod ---- cur/mod  0.8607 (rel_error =  16.19% avg_delta =  4.8895E-07)
Y 16 CUR ---- cur/alt  1.0270 (rel_error =  -2.63% avg_delta = -9.4706E-08)
Y 16 MOD ---- mod/alt  1.1933 (rel_error = -16.20% avg_delta = -5.8365E-07)

Y 17 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '1'}
Y 17 cur pass 26.4975 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 mod pass 23.2185 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 alt pass 28.5502 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 mod ---- cur/mod  0.8763 (rel_error =  14.12% avg_delta =  3.2789E-07)
Y 17 CUR ---- cur/alt  1.0775 (rel_error =  -7.19% avg_delta = -2.0527E-07)
Y 17 MOD ---- mod/alt  1.2296 (rel_error = -18.67% avg_delta = -5.3317E-07)

Y 18 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '0'}
Y 18 cur pass 33.8574 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 mod pass 30.2771 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 alt pass 29.9435 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 mod ---- cur/mod  0.8943 (rel_error =  11.83% avg_delta =  3.5803E-07)
Y 18 alt ---- cur/alt  0.8844 (rel_error =  13.07% avg_delta =  3.9140E-07)
Y 18 alt ---- mod/alt  0.9890 (rel_error =   1.11% avg_delta =  3.3363E-08)

Y 19 CPPDEFINES       {'A0': 'B0', 'B0': 'C0', 'C0': 'D0', 'D0': 'E0', 'E0': 'F0', 'F0': 'G0', 'G0': 'H0', 'H0': 'I0', 'I0': 'J0', 'J0': 'K0', 'K0': 'L0', 'L0': 'M0', 'M0': 'N0', 'N0': 'O0', 'O0': 'P0', 'P0': 'Q0', 'Q0': 'R0', 'R0': 'S0', 'S0': 'T0', 'T0': 'U0', 'U0': 'V0', 'V0': 'W0', 'W0': 'X0', 'X0': 'Y0', 'Y0': 'Z0', 'Z0': 'A1', 'A1': 'B1', 'B1': 'C1', 'C1': 'D1', 'D1': 'E1', 'E1': 'F1', 'F1': 'G1', 'G1': '2'}
Y 19 cur FAIL 137.4857 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 mod FAIL 139.2319 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 alt pass 120.6106 {'A0': '2', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 CUR ---- cur/mod  1.0127 (rel_error =  -1.25% avg_delta = -1.7463E-07)
Y 19 alt ---- cur/alt  0.8773 (rel_error =  13.99% avg_delta =  1.6875E-06)
Y 19 alt ---- mod/alt  0.8663 (rel_error =  15.44% avg_delta =  1.8621E-06)

Y 20 CPPDEFINES       {'A': 'B', 'B': 'A'}
Y 20 cur FAIL 10.0715 {'A': 'A', 'B': 'B'}
Y 20 mod FAIL  7.8323 {'A': 'A', 'B': 'B'}
Y 20 alt pass 12.9339 {'A': 'A', 'B': 'A'}
Y 20 mod ---- cur/mod  0.7777 (rel_error =  28.59% avg_delta =  2.2392E-07)
Y 20 CUR ---- cur/alt  1.2842 (rel_error = -22.13% avg_delta = -2.8624E-07)
Y 20 MOD ---- mod/alt  1.6514 (rel_error = -39.44% avg_delta = -5.1016E-07)

Y 21 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'A'}
Y 21 cur FAIL 26.9724 {'A': 'C', 'B': 'A', 'C': 'B'}
Y 21 mod FAIL 22.1710 {'A': 'C', 'B': 'A', 'C': 'B'}
Y 21 alt pass 16.2520 {'A': 'A', 'B': 'A', 'C': 'A'}
Y 21 mod ---- cur/mod  0.8220 (rel_error =  21.66% avg_delta =  4.8014E-07)
Y 21 alt ---- cur/alt  0.6025 (rel_error =  65.96% avg_delta =  1.0720E-06)
Y 21 alt ---- mod/alt  0.7330 (rel_error =  36.42% avg_delta =  5.9190E-07)

N 01 CPPDEFINES       {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 cur pass  5.7194 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 mod pass  4.2327 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 alt pass  4.1012 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 mod ---- cur/mod  0.7401 (rel_error =  35.12% avg_delta =  1.4867E-07)
N 01 alt ---- cur/alt  0.7171 (rel_error =  39.46% avg_delta =  1.6182E-07)
N 01 alt ---- mod/alt  0.9689 (rel_error =   3.21% avg_delta =  1.3149E-08)

N 02 CPPDEFINES       {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 cur pass  6.2681 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 mod pass  5.4216 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 alt pass  4.5620 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 mod ---- cur/mod  0.8650 (rel_error =  15.61% avg_delta =  8.4647E-08)
N 02 alt ---- cur/alt  0.7278 (rel_error =  37.40% avg_delta =  1.7062E-07)
N 02 alt ---- mod/alt  0.8414 (rel_error =  18.84% avg_delta =  8.5968E-08)

N 03 CPPDEFINES       {'STRING': 'VALUE', 'REPLACEABLE': 'RVALUE', 'RVALUE': 'AVALUE'}
N 03 cur pass 10.7427 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 mod pass  7.9634 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 alt pass  7.4386 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 mod ---- cur/mod  0.7413 (rel_error =  34.90% avg_delta =  2.7794E-07)
N 03 alt ---- cur/alt  0.6924 (rel_error =  44.42% avg_delta =  3.3041E-07)
N 03 alt ---- mod/alt  0.9341 (rel_error =   7.05% avg_delta =  5.2475E-08)

N 04 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 cur pass 14.5984 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 mod pass 12.1822 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 alt pass  9.7490 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 mod ---- cur/mod  0.8345 (rel_error =  19.83% avg_delta =  2.4162E-07)
N 04 alt ---- cur/alt  0.6678 (rel_error =  49.74% avg_delta =  4.8494E-07)
N 04 alt ---- mod/alt  0.8003 (rel_error =  24.96% avg_delta =  2.4333E-07)

N 05 CPPDEFINES       {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'EEE'}
N 05 cur pass 14.8553 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 mod pass 12.1675 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 alt pass  9.6004 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 mod ---- cur/mod  0.8191 (rel_error =  22.09% avg_delta =  2.6878E-07)
N 05 alt ---- cur/alt  0.6463 (rel_error =  54.74% avg_delta =  5.2549E-07)
N 05 alt ---- mod/alt  0.7890 (rel_error =  26.74% avg_delta =  2.5671E-07)

N 06 CPPDEFINES       {'AAA': 'XXX', 'BBB': 'YYY', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'DDD', 'YYY': 'CCC'}
N 06 cur pass 22.0827 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 mod pass 19.0132 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 alt pass 18.1441 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 mod ---- cur/mod  0.8610 (rel_error =  16.14% avg_delta =  3.0696E-07)
N 06 alt ---- cur/alt  0.8216 (rel_error =  21.71% avg_delta =  3.9387E-07)
N 06 alt ---- mod/alt  0.9543 (rel_error =   4.79% avg_delta =  8.6910E-08)

N 07 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'DDD', 'DDD': 'VALUE2', 'EEE': 'FFF', 'FFF': 'VALUE3'}
N 07 cur pass 14.5879 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 mod pass 11.9170 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 alt pass 12.3127 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 mod ---- cur/mod  0.8169 (rel_error =  22.41% avg_delta =  2.6709E-07)
N 07 alt ---- cur/alt  0.8440 (rel_error =  18.48% avg_delta =  2.2752E-07)
N 07 MOD ---- mod/alt  1.0332 (rel_error =  -3.21% avg_delta = -3.9570E-08)

N 08 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'AAA', 'DDD': 'BBB', 'EEE': 'AAA', 'FFF': 'BBB'}
N 08 cur pass 22.5386 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 mod pass 19.0176 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 alt pass 18.0585 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 mod ---- cur/mod  0.8438 (rel_error =  18.51% avg_delta =  3.5210E-07)
N 08 alt ---- cur/alt  0.8012 (rel_error =  24.81% avg_delta =  4.4800E-07)
N 08 alt ---- mod/alt  0.9496 (rel_error =   5.31% avg_delta =  9.5908E-08)

N 09 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 'I', 'I': 'J', 'J': 'K', 'K': '0'}
N 09 cur pass 56.5563 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 mod pass 53.8486 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 alt pass 40.2019 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 mod ---- cur/mod  0.9521 (rel_error =   5.03% avg_delta =  2.7077E-07)
N 09 alt ---- cur/alt  0.7108 (rel_error =  40.68% avg_delta =  1.6354E-06)
N 09 alt ---- mod/alt  0.7466 (rel_error =  33.95% avg_delta =  1.3647E-06)

N 10 CPPDEFINES       {'ABC': 'DEF', 'DEF': 'GHI', 'GHI': 'ABC', 'XXX': 'YYY', 'YYY': 'ZZZ', 'ZZZ': 'DEF'}
N 10 cur FAIL 38.5820 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
N 10 mod FAIL 34.4424 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
N 10 alt pass 26.6584 {'ABC': 'ABC', 'DEF': 'ABC', 'GHI': 'ABC', 'XXX': 'ABC', 'YYY': 'ABC', 'ZZZ': 'ABC'}
N 10 mod ---- cur/mod  0.8927 (rel_error =  12.02% avg_delta =  4.1396E-07)
N 10 alt ---- cur/alt  0.6910 (rel_error =  44.73% avg_delta =  1.1924E-06)
N 10 alt ---- mod/alt  0.7740 (rel_error =  29.20% avg_delta =  7.7841E-07)

N 11 CPPDEFINES       {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 cur pass  9.2198 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 mod pass  7.4789 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 alt pass  7.1445 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 mod ---- cur/mod  0.8112 (rel_error =  23.28% avg_delta =  1.7409E-07)
N 11 alt ---- cur/alt  0.7749 (rel_error =  29.05% avg_delta =  2.0754E-07)
N 11 alt ---- mod/alt  0.9553 (rel_error =   4.68% avg_delta =  3.3446E-08)

N 12 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
N 12 cur pass 35.6603 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 mod pass 31.6990 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 alt pass 36.5123 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 mod ---- cur/mod  0.8889 (rel_error =  12.50% avg_delta =  3.9613E-07)
N 12 CUR ---- cur/alt  1.0239 (rel_error =  -2.33% avg_delta = -8.5197E-08)
N 12 MOD ---- mod/alt  1.1518 (rel_error = -13.18% avg_delta = -4.8133E-07)

N 13 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 1}
N 13 cur pass 26.7872 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 mod pass 23.5450 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 alt pass 29.0130 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 mod ---- cur/mod  0.8790 (rel_error =  13.77% avg_delta =  3.2422E-07)
N 13 CUR ---- cur/alt  1.0831 (rel_error =  -7.67% avg_delta = -2.2258E-07)
N 13 MOD ---- mod/alt  1.2322 (rel_error = -18.85% avg_delta = -5.4680E-07)

N 14 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 0}
N 14 cur pass 34.9559 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 mod pass 31.6801 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 alt pass 30.1820 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 mod ---- cur/mod  0.9063 (rel_error =  10.34% avg_delta =  3.2758E-07)
N 14 alt ---- cur/alt  0.8634 (rel_error =  15.82% avg_delta =  4.7739E-07)
N 14 alt ---- mod/alt  0.9527 (rel_error =   4.96% avg_delta =  1.4981E-07)

N 15 CPPDEFINES       {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 cur pass  9.0016 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 mod pass  7.3902 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 alt pass  7.1233 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 mod ---- cur/mod  0.8210 (rel_error =  21.80% avg_delta =  1.6113E-07)
N 15 alt ---- cur/alt  0.7913 (rel_error =  26.37% avg_delta =  1.8782E-07)
N 15 alt ---- mod/alt  0.9639 (rel_error =   3.75% avg_delta =  2.6689E-08)

N 16 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
N 16 cur pass 35.0509 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 mod pass 30.2803 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 alt pass 35.7165 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 mod ---- cur/mod  0.8639 (rel_error =  15.75% avg_delta =  4.7706E-07)
N 16 CUR ---- cur/alt  1.0190 (rel_error =  -1.86% avg_delta = -6.6557E-08)
N 16 MOD ---- mod/alt  1.1795 (rel_error = -15.22% avg_delta = -5.4362E-07)

N 17 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '1'}
N 17 cur pass 26.6773 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 mod pass 23.2339 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 alt pass 28.7699 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 mod ---- cur/mod  0.8709 (rel_error =  14.82% avg_delta =  3.4434E-07)
N 17 CUR ---- cur/alt  1.0784 (rel_error =  -7.27% avg_delta = -2.0926E-07)
N 17 MOD ---- mod/alt  1.2383 (rel_error = -19.24% avg_delta = -5.5360E-07)

N 18 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '0'}
N 18 cur pass 33.9412 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 mod pass 30.1672 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 alt pass 29.9700 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 mod ---- cur/mod  0.8888 (rel_error =  12.51% avg_delta =  3.7740E-07)
N 18 alt ---- cur/alt  0.8830 (rel_error =  13.25% avg_delta =  3.9712E-07)
N 18 alt ---- mod/alt  0.9935 (rel_error =   0.66% avg_delta =  1.9726E-08)

N 19 CPPDEFINES       {'A0': 'B0', 'B0': 'C0', 'C0': 'D0', 'D0': 'E0', 'E0': 'F0', 'F0': 'G0', 'G0': 'H0', 'H0': 'I0', 'I0': 'J0', 'J0': 'K0', 'K0': 'L0', 'L0': 'M0', 'M0': 'N0', 'N0': 'O0', 'O0': 'P0', 'P0': 'Q0', 'Q0': 'R0', 'R0': 'S0', 'S0': 'T0', 'T0': 'U0', 'U0': 'V0', 'V0': 'W0', 'W0': 'X0', 'X0': 'Y0', 'Y0': 'Z0', 'Z0': 'A1', 'A1': 'B1', 'B1': 'C1', 'C1': 'D1', 'D1': 'E1', 'E1': 'F1', 'F1': 'G1', 'G1': '2'}
N 19 cur FAIL 137.5676 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 mod FAIL 139.5599 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 alt pass 121.5024 {'A0': '2', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 CUR ---- cur/mod  1.0145 (rel_error =  -1.43% avg_delta = -1.9923E-07)
N 19 alt ---- cur/alt  0.8832 (rel_error =  13.22% avg_delta =  1.6065E-06)
N 19 alt ---- mod/alt  0.8706 (rel_error =  14.86% avg_delta =  1.8057E-06)

N 20 CPPDEFINES       {'A': 'B', 'B': 'A'}
N 20 cur FAIL 10.1326 {'A': 'A', 'B': 'B'}
N 20 mod FAIL  7.8523 {'A': 'A', 'B': 'B'}
N 20 alt pass 12.6713 {'A': 'A', 'B': 'A'}
N 20 mod ---- cur/mod  0.7750 (rel_error =  29.04% avg_delta =  2.2803E-07)
N 20 CUR ---- cur/alt  1.2506 (rel_error = -20.04% avg_delta = -2.5387E-07)
N 20 MOD ---- mod/alt  1.6137 (rel_error = -38.03% avg_delta = -4.8190E-07)

N 21 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'A'}
N 21 cur FAIL 27.0719 {'A': 'C', 'B': 'A', 'C': 'B'}
N 21 mod FAIL 22.1615 {'A': 'C', 'B': 'A', 'C': 'B'}
N 21 alt pass 16.2892 {'A': 'A', 'B': 'A', 'C': 'A'}
N 21 mod ---- cur/mod  0.8186 (rel_error =  22.16% avg_delta =  4.9103E-07)
N 21 alt ---- cur/alt  0.6017 (rel_error =  66.20% avg_delta =  1.0783E-06)
N 21 alt ---- mod/alt  0.7350 (rel_error =  36.05% avg_delta =  5.8724E-07)

Test and profiling output [Python 3.12]:

python version = 3.12.3
number of invocations = 10,000,000

Y 01 CPPDEFINES       {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 cur pass  4.5948 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 mod pass  4.0497 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 alt pass  3.9863 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
Y 01 mod ---- cur/mod  0.8814 (rel_error =  13.46% avg_delta =  5.4503E-08)
Y 01 alt ---- cur/alt  0.8676 (rel_error =  15.26% avg_delta =  6.0842E-08)
Y 01 alt ---- mod/alt  0.9843 (rel_error =   1.59% avg_delta =  6.3397E-09)

Y 02 CPPDEFINES       {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 cur pass  4.8981 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 mod pass  5.3348 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 alt pass  4.4201 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
Y 02 CUR ---- cur/mod  1.0891 (rel_error =  -8.19% avg_delta = -4.3666E-08)
Y 02 alt ---- cur/alt  0.9024 (rel_error =  10.81% avg_delta =  4.7800E-08)
Y 02 alt ---- mod/alt  0.8285 (rel_error =  20.69% avg_delta =  9.1466E-08)

Y 03 CPPDEFINES       {'STRING': 'VALUE', 'REPLACEABLE': 'RVALUE', 'RVALUE': 'AVALUE'}
Y 03 cur pass  9.2908 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 mod pass  8.6409 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 alt pass  6.7857 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
Y 03 mod ---- cur/mod  0.9300 (rel_error =   7.52% avg_delta =  6.4992E-08)
Y 03 alt ---- cur/alt  0.7304 (rel_error =  36.92% avg_delta =  2.5051E-07)
Y 03 alt ---- mod/alt  0.7853 (rel_error =  27.34% avg_delta =  1.8552E-07)

Y 04 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 cur pass 13.3866 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 mod pass 12.1525 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 alt pass  8.3269 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
Y 04 mod ---- cur/mod  0.9078 (rel_error =  10.16% avg_delta =  1.2341E-07)
Y 04 alt ---- cur/alt  0.6220 (rel_error =  60.76% avg_delta =  5.0597E-07)
Y 04 alt ---- mod/alt  0.6852 (rel_error =  45.94% avg_delta =  3.8256E-07)

Y 05 CPPDEFINES       {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'EEE'}
Y 05 cur pass 13.5113 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 mod pass 12.6417 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 alt pass  8.2732 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
Y 05 mod ---- cur/mod  0.9356 (rel_error =   6.88% avg_delta =  8.6961E-08)
Y 05 alt ---- cur/alt  0.6123 (rel_error =  63.31% avg_delta =  5.2381E-07)
Y 05 alt ---- mod/alt  0.6544 (rel_error =  52.80% avg_delta =  4.3684E-07)

Y 06 CPPDEFINES       {'AAA': 'XXX', 'BBB': 'YYY', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'DDD', 'YYY': 'CCC'}
Y 06 cur pass 19.9799 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 mod pass 19.5123 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 alt pass 15.8647 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
Y 06 mod ---- cur/mod  0.9766 (rel_error =   2.40% avg_delta =  4.6753E-08)
Y 06 alt ---- cur/alt  0.7940 (rel_error =  25.94% avg_delta =  4.1152E-07)
Y 06 alt ---- mod/alt  0.8131 (rel_error =  22.99% avg_delta =  3.6477E-07)

Y 07 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'DDD', 'DDD': 'VALUE2', 'EEE': 'FFF', 'FFF': 'VALUE3'}
Y 07 cur pass 13.7486 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 mod pass 13.0574 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 alt pass 10.2825 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
Y 07 mod ---- cur/mod  0.9497 (rel_error =   5.29% avg_delta =  6.9121E-08)
Y 07 alt ---- cur/alt  0.7479 (rel_error =  33.71% avg_delta =  3.4661E-07)
Y 07 alt ---- mod/alt  0.7875 (rel_error =  26.99% avg_delta =  2.7749E-07)

Y 08 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'AAA', 'DDD': 'BBB', 'EEE': 'AAA', 'FFF': 'BBB'}
Y 08 cur pass 20.6973 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 mod pass 20.0095 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 alt pass 15.4593 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
Y 08 mod ---- cur/mod  0.9668 (rel_error =   3.44% avg_delta =  6.8781E-08)
Y 08 alt ---- cur/alt  0.7469 (rel_error =  33.88% avg_delta =  5.2381E-07)
Y 08 alt ---- mod/alt  0.7726 (rel_error =  29.43% avg_delta =  4.5503E-07)

Y 09 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 'I', 'I': 'J', 'J': 'K', 'K': '0'}
Y 09 cur pass 50.7885 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 mod pass 51.3218 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 alt pass 34.7897 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
Y 09 CUR ---- cur/mod  1.0105 (rel_error =  -1.04% avg_delta = -5.3329E-08)
Y 09 alt ---- cur/alt  0.6850 (rel_error =  45.99% avg_delta =  1.5999E-06)
Y 09 alt ---- mod/alt  0.6779 (rel_error =  47.52% avg_delta =  1.6532E-06)

Y 10 CPPDEFINES       {'ABC': 'DEF', 'DEF': 'GHI', 'GHI': 'ABC', 'XXX': 'YYY', 'YYY': 'ZZZ', 'ZZZ': 'DEF'}
Y 10 cur FAIL 33.9385 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
Y 10 mod FAIL 34.3206 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
Y 10 alt pass 23.1844 {'ABC': 'ABC', 'DEF': 'ABC', 'GHI': 'ABC', 'XXX': 'ABC', 'YYY': 'ABC', 'ZZZ': 'ABC'}
Y 10 CUR ---- cur/mod  1.0113 (rel_error =  -1.11% avg_delta = -3.8216E-08)
Y 10 alt ---- cur/alt  0.6831 (rel_error =  46.38% avg_delta =  1.0754E-06)
Y 10 alt ---- mod/alt  0.6755 (rel_error =  48.03% avg_delta =  1.1136E-06)

Y 11 CPPDEFINES       {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 cur pass  8.8189 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 mod pass  7.6603 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 alt pass  7.5686 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
Y 11 mod ---- cur/mod  0.8686 (rel_error =  15.12% avg_delta =  1.1586E-07)
Y 11 alt ---- cur/alt  0.8582 (rel_error =  16.52% avg_delta =  1.2502E-07)
Y 11 alt ---- mod/alt  0.9880 (rel_error =   1.21% avg_delta =  9.1659E-09)

Y 12 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
Y 12 cur pass 34.4552 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 mod pass 33.4912 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 alt pass 31.7343 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 12 mod ---- cur/mod  0.9720 (rel_error =   2.88% avg_delta =  9.6398E-08)
Y 12 alt ---- cur/alt  0.9210 (rel_error =   8.57% avg_delta =  2.7209E-07)
Y 12 alt ---- mod/alt  0.9475 (rel_error =   5.54% avg_delta =  1.7569E-07)

Y 13 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 1}
Y 13 cur pass 25.3231 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 mod pass 24.6366 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 alt pass 25.3124 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
Y 13 mod ---- cur/mod  0.9729 (rel_error =   2.79% avg_delta =  6.8652E-08)
Y 13 alt ---- cur/alt  0.9996 (rel_error =   0.04% avg_delta =  1.0715E-09)
Y 13 MOD ---- mod/alt  1.0274 (rel_error =  -2.67% avg_delta = -6.7581E-08)

Y 14 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 0}
Y 14 cur pass 33.7410 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 mod pass 33.5696 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 alt pass 26.2380 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
Y 14 mod ---- cur/mod  0.9949 (rel_error =   0.51% avg_delta =  1.7144E-08)
Y 14 alt ---- cur/alt  0.7776 (rel_error =  28.60% avg_delta =  7.5031E-07)
Y 14 alt ---- mod/alt  0.7816 (rel_error =  27.94% avg_delta =  7.3316E-07)

Y 15 CPPDEFINES       {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 cur pass  8.2759 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 mod pass  7.4727 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 alt pass  7.2274 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
Y 15 mod ---- cur/mod  0.9029 (rel_error =  10.75% avg_delta =  8.0321E-08)
Y 15 alt ---- cur/alt  0.8733 (rel_error =  14.51% avg_delta =  1.0486E-07)
Y 15 alt ---- mod/alt  0.9672 (rel_error =   3.39% avg_delta =  2.4535E-08)

Y 16 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
Y 16 cur pass 33.5874 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 mod pass 32.3776 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 alt pass 30.7005 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 16 mod ---- cur/mod  0.9640 (rel_error =   3.74% avg_delta =  1.2098E-07)
Y 16 alt ---- cur/alt  0.9140 (rel_error =   9.40% avg_delta =  2.8869E-07)
Y 16 alt ---- mod/alt  0.9482 (rel_error =   5.46% avg_delta =  1.6771E-07)

Y 17 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '1'}
Y 17 cur pass 24.2658 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 mod pass 24.1808 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 alt pass 24.8746 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
Y 17 mod ---- cur/mod  0.9965 (rel_error =   0.35% avg_delta =  8.5001E-09)
Y 17 CUR ---- cur/alt  1.0251 (rel_error =  -2.45% avg_delta = -6.0883E-08)
Y 17 MOD ---- mod/alt  1.0287 (rel_error =  -2.79% avg_delta = -6.9383E-08)

Y 18 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '0'}
Y 18 cur pass 32.3708 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 mod pass 32.3795 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 alt pass 26.1215 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
Y 18 CUR ---- cur/mod  1.0003 (rel_error =  -0.03% avg_delta = -8.7076E-10)
Y 18 alt ---- cur/alt  0.8069 (rel_error =  23.92% avg_delta =  6.2493E-07)
Y 18 alt ---- mod/alt  0.8067 (rel_error =  23.96% avg_delta =  6.2580E-07)

Y 19 CPPDEFINES       {'A0': 'B0', 'B0': 'C0', 'C0': 'D0', 'D0': 'E0', 'E0': 'F0', 'F0': 'G0', 'G0': 'H0', 'H0': 'I0', 'I0': 'J0', 'J0': 'K0', 'K0': 'L0', 'L0': 'M0', 'M0': 'N0', 'N0': 'O0', 'O0': 'P0', 'P0': 'Q0', 'Q0': 'R0', 'R0': 'S0', 'S0': 'T0', 'T0': 'U0', 'U0': 'V0', 'V0': 'W0', 'W0': 'X0', 'X0': 'Y0', 'Y0': 'Z0', 'Z0': 'A1', 'A1': 'B1', 'B1': 'C1', 'C1': 'D1', 'D1': 'E1', 'E1': 'F1', 'F1': 'G1', 'G1': '2'}
Y 19 cur FAIL 134.9039 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 mod FAIL 139.7814 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 alt pass 111.0249 {'A0': '2', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
Y 19 CUR ---- cur/mod  1.0362 (rel_error =  -3.49% avg_delta = -4.8775E-07)
Y 19 alt ---- cur/alt  0.8230 (rel_error =  21.51% avg_delta =  2.3879E-06)
Y 19 alt ---- mod/alt  0.7943 (rel_error =  25.90% avg_delta =  2.8756E-06)

Y 20 CPPDEFINES       {'A': 'B', 'B': 'A'}
Y 20 cur FAIL  7.8169 {'A': 'A', 'B': 'B'}
Y 20 mod FAIL  7.8900 {'A': 'A', 'B': 'B'}
Y 20 alt pass 11.0082 {'A': 'A', 'B': 'A'}
Y 20 CUR ---- cur/mod  1.0094 (rel_error =  -0.93% avg_delta = -7.3164E-09)
Y 20 CUR ---- cur/alt  1.4083 (rel_error = -28.99% avg_delta = -3.1914E-07)
Y 20 MOD ---- mod/alt  1.3952 (rel_error = -28.33% avg_delta = -3.1182E-07)

Y 21 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'A'}
Y 21 cur FAIL 21.4049 {'A': 'C', 'B': 'A', 'C': 'B'}
Y 21 mod FAIL 22.0288 {'A': 'C', 'B': 'A', 'C': 'B'}
Y 21 alt pass 14.1860 {'A': 'A', 'B': 'A', 'C': 'A'}
Y 21 CUR ---- cur/mod  1.0291 (rel_error =  -2.83% avg_delta = -6.2389E-08)
Y 21 alt ---- cur/alt  0.6627 (rel_error =  50.89% avg_delta =  7.2189E-07)
Y 21 alt ---- mod/alt  0.6440 (rel_error =  55.29% avg_delta =  7.8428E-07)

N 01 CPPDEFINES       {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 cur pass  4.6131 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 mod pass  4.0994 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 alt pass  3.9796 {'STRING1': 'VALUE1', 'STRING2': 'VALUE2', 'STRING3': 'VALUE3'}
N 01 mod ---- cur/mod  0.8886 (rel_error =  12.53% avg_delta =  5.1372E-08)
N 01 alt ---- cur/alt  0.8627 (rel_error =  15.92% avg_delta =  6.3353E-08)
N 01 alt ---- mod/alt  0.9708 (rel_error =   3.01% avg_delta =  1.1981E-08)

N 02 CPPDEFINES       {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 cur pass  4.9957 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 mod pass  5.2811 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 alt pass  4.4661 {'STRING1': 'STRING1', 'STRING2': 'STRING2', 'STRING3': 'STRING3'}
N 02 CUR ---- cur/mod  1.0571 (rel_error =  -5.40% avg_delta = -2.8541E-08)
N 02 alt ---- cur/alt  0.8940 (rel_error =  11.86% avg_delta =  5.2960E-08)
N 02 alt ---- mod/alt  0.8457 (rel_error =  18.25% avg_delta =  8.1501E-08)

N 03 CPPDEFINES       {'STRING': 'VALUE', 'REPLACEABLE': 'RVALUE', 'RVALUE': 'AVALUE'}
N 03 cur pass  9.3413 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 mod pass  8.6846 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 alt pass  6.7117 {'STRING': 'VALUE', 'REPLACEABLE': 'AVALUE', 'RVALUE': 'AVALUE'}
N 03 mod ---- cur/mod  0.9297 (rel_error =   7.56% avg_delta =  6.5662E-08)
N 03 alt ---- cur/alt  0.7185 (rel_error =  39.18% avg_delta =  2.6296E-07)
N 03 alt ---- mod/alt  0.7728 (rel_error =  29.40% avg_delta =  1.9730E-07)

N 04 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 cur pass 13.1350 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 mod pass 12.2852 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 alt pass  8.4032 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE3', 'EEE': 'VALUE4', 'FFF': 'VALUE5'}
N 04 mod ---- cur/mod  0.9353 (rel_error =   6.92% avg_delta =  8.4982E-08)
N 04 alt ---- cur/alt  0.6398 (rel_error =  56.31% avg_delta =  4.7318E-07)
N 04 alt ---- mod/alt  0.6840 (rel_error =  46.20% avg_delta =  3.8820E-07)

N 05 CPPDEFINES       {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'EEE'}
N 05 cur pass 13.5536 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 mod pass 12.4665 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 alt pass  8.3273 {'AAA': 'VALUE1', 'BBB': 'VALUE2', 'CCC': 'VALUE3', 'DDD': 'VALUE4', 'EEE': 'VALUE5', 'FFF': 'VALUE5'}
N 05 mod ---- cur/mod  0.9198 (rel_error =   8.72% avg_delta =  1.0871E-07)
N 05 alt ---- cur/alt  0.6144 (rel_error =  62.76% avg_delta =  5.2263E-07)
N 05 alt ---- mod/alt  0.6680 (rel_error =  49.71% avg_delta =  4.1392E-07)

N 06 CPPDEFINES       {'AAA': 'XXX', 'BBB': 'YYY', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'DDD', 'YYY': 'CCC'}
N 06 cur pass 19.9948 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 mod pass 19.7594 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 alt pass 15.7196 {'AAA': 'VALUE2', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE2', 'XXX': 'VALUE2', 'YYY': 'VALUE1'}
N 06 mod ---- cur/mod  0.9882 (rel_error =   1.19% avg_delta =  2.3543E-08)
N 06 alt ---- cur/alt  0.7862 (rel_error =  27.20% avg_delta =  4.2752E-07)
N 06 alt ---- mod/alt  0.7956 (rel_error =  25.70% avg_delta =  4.0397E-07)

N 07 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'DDD', 'DDD': 'VALUE2', 'EEE': 'FFF', 'FFF': 'VALUE3'}
N 07 cur pass 13.6189 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 mod pass 12.9363 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 alt pass 10.4570 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE2', 'DDD': 'VALUE2', 'EEE': 'VALUE3', 'FFF': 'VALUE3'}
N 07 mod ---- cur/mod  0.9499 (rel_error =   5.28% avg_delta =  6.8257E-08)
N 07 alt ---- cur/alt  0.7678 (rel_error =  30.24% avg_delta =  3.1619E-07)
N 07 alt ---- mod/alt  0.8083 (rel_error =  23.71% avg_delta =  2.4793E-07)

N 08 CPPDEFINES       {'AAA': 'BBB', 'BBB': 'VALUE1', 'CCC': 'AAA', 'DDD': 'BBB', 'EEE': 'AAA', 'FFF': 'BBB'}
N 08 cur pass 20.7447 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 mod pass 19.8956 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 alt pass 15.5439 {'AAA': 'VALUE1', 'BBB': 'VALUE1', 'CCC': 'VALUE1', 'DDD': 'VALUE1', 'EEE': 'VALUE1', 'FFF': 'VALUE1'}
N 08 mod ---- cur/mod  0.9591 (rel_error =   4.27% avg_delta =  8.4912E-08)
N 08 alt ---- cur/alt  0.7493 (rel_error =  33.46% avg_delta =  5.2008E-07)
N 08 alt ---- mod/alt  0.7813 (rel_error =  28.00% avg_delta =  4.3517E-07)

N 09 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 'I', 'I': 'J', 'J': 'K', 'K': '0'}
N 09 cur pass 51.4487 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 mod pass 51.3849 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 alt pass 34.6963 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0', 'I': '0', 'J': '0', 'K': '0'}
N 09 mod ---- cur/mod  0.9988 (rel_error =   0.12% avg_delta =  6.3775E-09)
N 09 alt ---- cur/alt  0.6744 (rel_error =  48.28% avg_delta =  1.6752E-06)
N 09 alt ---- mod/alt  0.6752 (rel_error =  48.10% avg_delta =  1.6689E-06)

N 10 CPPDEFINES       {'ABC': 'DEF', 'DEF': 'GHI', 'GHI': 'ABC', 'XXX': 'YYY', 'YYY': 'ZZZ', 'ZZZ': 'DEF'}
N 10 cur FAIL 34.6302 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
N 10 mod FAIL 35.2788 {'ABC': 'GHI', 'DEF': 'ABC', 'GHI': 'DEF', 'XXX': 'ABC', 'YYY': 'DEF', 'ZZZ': 'GHI'}
N 10 alt pass 23.1883 {'ABC': 'ABC', 'DEF': 'ABC', 'GHI': 'ABC', 'XXX': 'ABC', 'YYY': 'ABC', 'ZZZ': 'ABC'}
N 10 CUR ---- cur/mod  1.0187 (rel_error =  -1.84% avg_delta = -6.4864E-08)
N 10 alt ---- cur/alt  0.6696 (rel_error =  49.34% avg_delta =  1.1442E-06)
N 10 alt ---- mod/alt  0.6573 (rel_error =  52.14% avg_delta =  1.2090E-06)

N 11 CPPDEFINES       {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 cur pass  8.8496 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 mod pass  7.8190 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 alt pass  7.6939 {'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7}
N 11 mod ---- cur/mod  0.8835 (rel_error =  13.18% avg_delta =  1.0305E-07)
N 11 alt ---- cur/alt  0.8694 (rel_error =  15.02% avg_delta =  1.1557E-07)
N 11 alt ---- mod/alt  0.9840 (rel_error =   1.63% avg_delta =  1.2511E-08)

N 12 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
N 12 cur pass 34.3680 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 mod pass 33.3584 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 alt pass 31.9655 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 12 mod ---- cur/mod  0.9706 (rel_error =   3.03% avg_delta =  1.0096E-07)
N 12 alt ---- cur/alt  0.9301 (rel_error =   7.52% avg_delta =  2.4025E-07)
N 12 alt ---- mod/alt  0.9582 (rel_error =   4.36% avg_delta =  1.3929E-07)

N 13 CPPDEFINES       {'A': 0, 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 1}
N 13 cur pass 25.5536 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 mod pass 25.1869 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 alt pass 25.4156 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 1, 'F': 1, 'G': 1, 'H': 1}
N 13 mod ---- cur/mod  0.9856 (rel_error =   1.46% avg_delta =  3.6675E-08)
N 13 alt ---- cur/alt  0.9946 (rel_error =   0.54% avg_delta =  1.3805E-08)
N 13 MOD ---- mod/alt  1.0091 (rel_error =  -0.90% avg_delta = -2.2871E-08)

N 14 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': 0}
N 14 cur pass 33.3068 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 mod pass 33.7096 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 alt pass 26.3872 {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0}
N 14 CUR ---- cur/mod  1.0121 (rel_error =  -1.19% avg_delta = -4.0279E-08)
N 14 alt ---- cur/alt  0.7922 (rel_error =  26.22% avg_delta =  6.9196E-07)
N 14 alt ---- mod/alt  0.7828 (rel_error =  27.75% avg_delta =  7.3224E-07)

N 15 CPPDEFINES       {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 cur pass  8.3758 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 mod pass  7.3401 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 alt pass  7.2632 {'A': '0', 'B': '1', 'C': '2', 'D': '3', 'E': '4', 'F': '5', 'G': '6', 'H': '7'}
N 15 mod ---- cur/mod  0.8763 (rel_error =  14.11% avg_delta =  1.0357E-07)
N 15 alt ---- cur/alt  0.8672 (rel_error =  15.32% avg_delta =  1.1126E-07)
N 15 alt ---- mod/alt  0.9895 (rel_error =   1.06% avg_delta =  7.6904E-09)

N 16 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'D', 'F': 'E', 'G': 'F', 'H': 'G'}
N 16 cur pass 33.1331 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 mod pass 32.2312 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 alt pass 30.9013 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 16 mod ---- cur/mod  0.9728 (rel_error =   2.80% avg_delta =  9.0183E-08)
N 16 alt ---- cur/alt  0.9326 (rel_error =   7.22% avg_delta =  2.2318E-07)
N 16 alt ---- mod/alt  0.9587 (rel_error =   4.30% avg_delta =  1.3300E-07)

N 17 CPPDEFINES       {'A': '0', 'B': 'A', 'C': 'B', 'D': 'C', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '1'}
N 17 cur pass 24.5079 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 mod pass 23.6720 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 alt pass 24.6200 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '1', 'F': '1', 'G': '1', 'H': '1'}
N 17 mod ---- cur/mod  0.9659 (rel_error =   3.53% avg_delta =  8.3587E-08)
N 17 CUR ---- cur/alt  1.0046 (rel_error =  -0.46% avg_delta = -1.1214E-08)
N 17 MOD ---- mod/alt  1.0400 (rel_error =  -3.85% avg_delta = -9.4801E-08)

N 18 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'D', 'D': 'E', 'E': 'F', 'F': 'G', 'G': 'H', 'H': '0'}
N 18 cur pass 32.3005 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 mod pass 32.7356 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 alt pass 26.4467 {'A': '0', 'B': '0', 'C': '0', 'D': '0', 'E': '0', 'F': '0', 'G': '0', 'H': '0'}
N 18 CUR ---- cur/mod  1.0135 (rel_error =  -1.33% avg_delta = -4.3508E-08)
N 18 alt ---- cur/alt  0.8188 (rel_error =  22.13% avg_delta =  5.8538E-07)
N 18 alt ---- mod/alt  0.8079 (rel_error =  23.78% avg_delta =  6.2889E-07)

N 19 CPPDEFINES       {'A0': 'B0', 'B0': 'C0', 'C0': 'D0', 'D0': 'E0', 'E0': 'F0', 'F0': 'G0', 'G0': 'H0', 'H0': 'I0', 'I0': 'J0', 'J0': 'K0', 'K0': 'L0', 'L0': 'M0', 'M0': 'N0', 'N0': 'O0', 'O0': 'P0', 'P0': 'Q0', 'Q0': 'R0', 'R0': 'S0', 'S0': 'T0', 'T0': 'U0', 'U0': 'V0', 'V0': 'W0', 'W0': 'X0', 'X0': 'Y0', 'Y0': 'Z0', 'Z0': 'A1', 'A1': 'B1', 'B1': 'C1', 'C1': 'D1', 'D1': 'E1', 'E1': 'F1', 'F1': 'G1', 'G1': '2'}
N 19 cur FAIL 135.3855 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 mod FAIL 139.6285 {'A0': 'G1', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 alt pass 111.4540 {'A0': '2', 'B0': '2', 'C0': '2', 'D0': '2', 'E0': '2', 'F0': '2', 'G0': '2', 'H0': '2', 'I0': '2', 'J0': '2', 'K0': '2', 'L0': '2', 'M0': '2', 'N0': '2', 'O0': '2', 'P0': '2', 'Q0': '2', 'R0': '2', 'S0': '2', 'T0': '2', 'U0': '2', 'V0': '2', 'W0': '2', 'X0': '2', 'Y0': '2', 'Z0': '2', 'A1': '2', 'B1': '2', 'C1': '2', 'D1': '2', 'E1': '2', 'F1': '2', 'G1': '2'}
N 19 CUR ---- cur/mod  1.0313 (rel_error =  -3.04% avg_delta = -4.2430E-07)
N 19 alt ---- cur/alt  0.8232 (rel_error =  21.47% avg_delta =  2.3931E-06)
N 19 alt ---- mod/alt  0.7982 (rel_error =  25.28% avg_delta =  2.8174E-06)

N 20 CPPDEFINES       {'A': 'B', 'B': 'A'}
N 20 cur FAIL  7.7654 {'A': 'A', 'B': 'B'}
N 20 mod FAIL  7.8706 {'A': 'A', 'B': 'B'}
N 20 alt pass 11.0593 {'A': 'A', 'B': 'A'}
N 20 CUR ---- cur/mod  1.0136 (rel_error =  -1.34% avg_delta = -1.0523E-08)
N 20 CUR ---- cur/alt  1.4242 (rel_error = -29.78% avg_delta = -3.2938E-07)
N 20 MOD ---- mod/alt  1.4051 (rel_error = -28.83% avg_delta = -3.1886E-07)

N 21 CPPDEFINES       {'A': 'B', 'B': 'C', 'C': 'A'}
N 21 cur FAIL 21.5705 {'A': 'C', 'B': 'A', 'C': 'B'}
N 21 mod FAIL 22.1377 {'A': 'C', 'B': 'A', 'C': 'B'}
N 21 alt pass 14.2759 {'A': 'A', 'B': 'A', 'C': 'A'}
N 21 CUR ---- cur/mod  1.0263 (rel_error =  -2.56% avg_delta = -5.6718E-08)
N 21 alt ---- cur/alt  0.6618 (rel_error =  51.10% avg_delta =  7.2946E-07)
N 21 alt ---- mod/alt  0.6449 (rel_error =  55.07% avg_delta =  7.8618E-07)

Changes

Edits:

  • Refine modified PR implementation.
  • Refine alternative implementation.
  • Add additional tests.
  • Add profiling information.
  • Add summary tables.

@jcbrill
Copy link
Contributor

jcbrill commented Dec 6, 2024

Observations

There is an asymetric difference in behavior between backward references (i.e., a value refers to an earlier key) compared to forward references (i.e., a value refers to a later key).

For example:

  • Best case - backward references (8 entries: 1 iteration):

    mapping8BC = {
        "A": "0",
        "B": "A",
        "C": "B",
        "D": "C",
        "E": "D",
        "F": "E",
        "G": "F",
        "H": "G",
    }
    
    Loop               0     1 
    -------------     ---   ---
    mapping8BC["A"] =  0  =  0
    mapping8BC["B"] = "A" =  0
    mapping8BC["C"] = "B" =  0
    mapping8BC["D"] = "C" =  0
    mapping8BC["E"] = "D" =  0
    mapping8BC["F"] = "E" =  0
    mapping8BC["G"] = "F" =  0
    mapping8BC["H"] = "G" =  0
    
  • Worst case: forward references (8 entries: 3 iterations):

    mapping8WC = {
        "A": "B",
        "B": "C",
        "C": "D",
        "D": "E",
        "E": "F",
        "F": "G",
        "G": "H",
        "H": 0,
    }
    
    Loop               0     1     2     3
    -------------     ---   ---   ---   ---
    mapping8WC["B"] = "C" = "D" = "F" =  0
    mapping8WC["C"] = "D" = "E" = "G" =  0
    mapping8WC["D"] = "E" = "F" = "H" =  0
    mapping8WC["E"] = "F" = "G" =  0  =  0
    mapping8WC["F"] = "G" = "H" =  0  =  0
    mapping8WC["G"] = "H" =  0  =  0  =  0
    mapping8WC["H"] =  0  =  0  =  0  =  0
    

For every loop variable value N (i.e., the initial value of variable loops), trails of forward references with a length greater than 2N will not be fully resolved (i.e., the loop count is exceeded but there is not a cycle).

While exiting due to exceeding the loop count due to a cycle is the most likely reason, it is not guaranteed.

In the post above,

  • Experiments 10, 20, and 21 have cycles.
  • Experiment 19 has a forward reference trail of length 33.

The current and modified algorithms in the post above exceed the loop count for experiments 10, 19, 20, 21. Timing comparisons to the alternative algorithm for these four experiments are not necessarily meaningful.

Algorithms

The results for python versions 3.7 to 3.11 are very different from the results for python version 3.12.

For python versions prior to 3.12, the modified algorithm appears to be faster than the current algorithm in most all cases.

The three algorithms in the post above are:

  • Current algorithm:

    The equality test condition is likely most expensive when the two dictionaries are equal.

    Exiting due to the loop count means that:

    • there are one or more cycles, and/or
    • there are one or more trail lengths of forward references greater than 2NLoops.
    def _replace(mapping: Dict) -> Dict:
        old_ns = mapping
        loops = 0
        while loops < 5:  # don't recurse forever in case there's circular data
            ns = {k: old_ns[v] if v in old_ns else v for k, v in old_ns.items()}
            if old_ns == ns:
                break
            old_ns = ns
            loops += 1
        return ns
  • Modified algorithm:

    Replace the dictionary comprehension and equality test with a loop and boolean exit condition.

    Exiting due to the loop count means that:

    • there are one or more cycles, and/or
    • there are one or more trail lengths of forward references greater than 2NLoops.
    def _replace_mod(mapping: Dict) -> Dict:
        old_ns = mapping
        loops = 0
        while loops < 5:  # don't recurse forever in case there's circular data
            again = False
            ns = {}
            for k, v in old_ns.items():
                if v in old_ns:
                    ns[k] = old_ns[v]
                    if not again and ns[k] != v:
                        again = True
                else:
                    ns[k] = v
            if not again:
                break
            old_ns = ns
            loops += 1
        return ns
  • Alternate algorithm:

    Cycles are detected.

    Long chains of backward references might be slower than the modified algorithm.

    def _replace_alt(mapping: Dict) -> Dict:
    
        replace_list = []
        resolved_map = {}
    
        for key, val in mapping.items():
    
            if val not in mapping:
                resolved_map[key] = val
            elif val != key:
                replace_list.append((key,val))
            else:
                # cycle (self-referential)
                resolved_map[key] = val
    
        if not replace_list:
    
            mapping = resolved_map
    
        else:
    
            mapping = dict(mapping)
    
            for key, val in replace_list:
    
                if val in resolved_map:
                    mapping[key] = resolved_map[val]
                    continue
    
                replace_seen = set()
                replace_trail = [val]
    
                while val in mapping:
    
                    nextval = mapping[val]
                    if nextval in resolved_map:
                        val = resolved_map[nextval]
                        break
    
                    replace_seen.add(val)
                    if nextval in replace_seen:
                        # cycle detected
                        val = replace_trail[-1]
                        break
    
                    replace_trail.append(nextval)
                    val = nextval
    
                for resolved_key in replace_trail:
                    resolved_map[resolved_key] = val
    
                mapping[key] = val
    
        return mapping

@jcbrill
Copy link
Contributor

jcbrill commented Dec 6, 2024

<Earlier markdown table remove pending better solution>

Temporary PDF tables of algorithm comparisons:

Raw output files by python version:

Legend

The following 3 tables compare pairs of algorithms. The abbreviated name of the "faster" algorithm is shown in the table cells.

The text colors of the table cells correspond to the relative error percentage in the raw output:

  • $\color{#00A815}0.0\% &lt;= abs(relative\ error\ percentage) &lt; 10.0\%$
  • $\color{#FFB100}10.0\% &lt;= abs(relative\ error) &lt; 20.0\%$
  • $\color{#E30022}20.0\% &lt;= abs(relative\ error)$
  • $\color{#BFBDC1}Current\ and\ modified\ algorithms\ exceeded\ loop\ count$

A - Current Algorithm Versus Modified Algorithm

Relative error percentage = ((total_elapsed_time_current - total_elapsed_time_modified) / total_elapsed_time_modified) * 100.0

Test 3.7 3.7 3.8 3.8 3.9 3.9 3.10 3.10 3.11 3.11
GC Y N Y N Y N Y N Y N
01 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
02 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$
03 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
04 $\color{#E30022}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
05 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
06 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#FFB100}{mod}$
07 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
08 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
09 $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$
10 $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$
11 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
12 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$
13 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$
14 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$
15 $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
16 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$ $\color{#E30022}{mod}$
17 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#E30022}{mod}$
18 $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#00A815}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$ $\color{#FFB100}{mod}$
19 $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$
20 $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$
21 $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$ $\color{#BFBDC1}{mod}$

B - Current Algorithm Versus Alternate Algorithm

Relative error percentage = ((total_elapsed_time_current - total_elapsed_time_alternate) / total_elapsed_time_alternate) * 100.0

Test 3.7 3.7 3.8 3.8 3.9 3.9 3.10 3.10 3.11 3.11
GC Y N Y N Y N Y N Y N
01 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
02 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
03 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
04 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
05 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
06 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
07 $\color{#E30022}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
08 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
09 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
10 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$
11 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
12 $\color{#00A815}{CUR}$ $\color{#00A815}{CUR}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
13 $\color{#00A815}{CUR}$ $\color{#00A815}{CUR}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
14 $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
15 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
16 $\color{#00A815}{CUR}$ $\color{#00A815}{CUR}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
17 $\color{#00A815}{CUR}$ $\color{#00A815}{CUR}$ $\color{#00A815}{alt}$ $\color{#00A815}{CUR}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#00A815}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
18 $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
19 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$
20 $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$ $\color{#BFBDC1}{CUR}$
21 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$

C - Modified Algorithm Versus Alternate Algorithm

Relative error percentage = ((total_elapsed_time_modified - total_elapsed_time_alternate) / total_elapsed_time_alternate) * 100.0

Test 3.7 3.7 3.8 3.8 3.9 3.9 3.10 3.10 3.11 3.11
GC Y N Y N Y N Y N Y N
01 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$
02 $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#FFB100}{alt}$
03 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
04 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
05 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
06 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
07 $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
08 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
09 $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
10 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$
11 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$
12 $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$
13 $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#00A815}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$
14 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
15 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$
16 $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$
17 $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#FFB100}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{MOD}$ $\color{#00A815}{alt}$ $\color{#00A815}{alt}$
18 $\color{#00A815}{alt}$ $\color{#00A815}{alt}$ $\color{#FFB100}{alt}$ $\color{#FFB100}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$ $\color{#E30022}{alt}$
19 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$
20 $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$ $\color{#BFBDC1}{MOD}$
21 $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$ $\color{#BFBDC1}{alt}$

@mwichmann
Copy link
Collaborator Author

Boy, that's a lot. Sadly, with the winter sun shining in my window causing glare, the colors don't really stand out, plus something didn't render in the last row of the last table.

Is there a "short summary" version of all this?

@bdbaddog
Copy link
Contributor

bdbaddog commented Dec 6, 2024

What do you mean by relative error? What metrics are you using for this calculation? time? other?

@jcbrill
Copy link
Contributor

jcbrill commented Dec 6, 2024

All of this is much ado about nothing.

Boy, that's a lot. Sadly, with the winter sun shining in my window causing glare, the colors don't really stand out, plus something didn't render in the last row of the last table.

I'm going to delete the content for the previous post until I can figure out how to handle it correctly. Abusing inline latex for colors again. Somewhere, I have code to generate html tables. Sigh.

Initially, the last row didn't render here but refreshing the page "fixed" it. Go figure.

Is there a "short summary" version of all this?

Notes:

  • The relative timing changes with python 3.12.
  • The modified version of the PR is usually faster than the existing version by avoiding the dictionary comparison and putting a boolean flag in the loop replacement for the comprehension.
  • The alternate version is usually faster than the existing version.
  • The alternate version and modified versions are reasonably competitive based on the input data configurations (e.g., backward references favor the current/modified algorithms).
  • The alternate version detects cycles and always terminates.

The modified version is simple and easy to understand.

The alternate version detects cycles and does not have trail length limitations but is more involved.

In the end, the timing differences per invocation are REALLY small.

@jcbrill
Copy link
Contributor

jcbrill commented Dec 6, 2024

What do you mean by relative error? What metrics are you using for this calculation? time? other?

Total elapsed time.

    def profile(func, mapping, ncalls=NINVOCATIONS, gcenabled=True):
        gc.collect()
        if not gcenabled:
            gc.disable()
        rval = None
        begtime = time.perf_counter()
        while ncalls > 0:
            ncalls -= 1
            rval = func(mapping)
        endtime = time.perf_counter()
        if not gcenabled:
            gc.enable()
        gc.collect()
        elapsed = endtime - begtime
        return rval, elapsed

The relative error percentage for comparing the current and modified algorithms using total elapsed time is:

  • relative error percentage = ((total_elapsed_current - total_elapsed_modified) / total_elapsed_modified) * 100.0

@mwichmann
Copy link
Collaborator Author

Notes:

* The relative timing changes with python 3.12.

Well, the project to substantially speed up Python (that brought Guido out of a brief retirment) landed its first bits for 3.12, 3.13 might make another difference. All depends on what code paths we hit... differences between versions aren't unusual when I've done other benchmarking - including sometimes things got slower for a release or two!

* The modified version of the PR is usually faster than the existing version by avoiding the dictionary comparison and putting a boolean flag in the loop replacement for the comprehension.

that might be worthwhile... although comprehensions shouldn't normally be slower than an unrolled loop...

@jcbrill
Copy link
Contributor

jcbrill commented Dec 6, 2024

that might be worthwhile... although comprehensions shouldn't normally be slower than an unrolled loop...

It is not the loop that is slower; it is the comparison for equality following the loop. I would think that the worst-case performance of the dictionary comparison is when they are equal (i.e., all elements compared).

By unrolling the comprehension, a flag can be set if the old and new values are different indicating another iteration is necessary. Once the flag is set, there is no need to compare the values anymore. The comparisons only take place when writing to keys in which the values need to be resolved.

@mwichmann
Copy link
Collaborator Author

that might be worthwhile... although comprehensions shouldn't normally be slower than an unrolled loop...

It is not the loop that is slower; it is the comparison for equality following the loop. I would think that the worst-case performance of the dictionary comparison is when they are equal (i.e., all elements compared).

By unrolling the comprehension, a flag can be set if the old and new values are different indicating another iteration is necessary. Once the flag is set, there is no need to compare the values anymore. The comparisons only take place when writing to keys in which the values need to be resolved.

right, makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants