Domain: Business Operations & Analytics

Using the two provided pizza pricing tables (`pizza_v1_uncleaned_large.csv` and `pizza_v2_uncleaned_large.csv`), construct a deterministic reconciliation analysis by linking records through a single entity key and then measuring cross-table price and option consistency under strict parsing and deduplication rules.
**Entity Key Construction**
Define an entity key as the concatenation of three parts joined by the character `|`: (i) the first ASCII letter found in the normalized company text, (ii) the normalized variant token, and (iii) the normalized size token. If any part is missing, the entity key is missing and the row must be removed before any analysis.
Company normalization must trim, casefold, remove all whitespace, then scan left-to-right for the first letter a–z; if none exists, the company part is missing.
Variant and size token normalization must: map null-like tokens ('', nan, none, null, -, ###) to missing; replace underscores with spaces; collapse whitespace; trim; casefold; remove all non-alphanumeric characters; and then apply the exact typo-folding map: chikcen→chicken, mushroms→mushrooms, mozarela→mozzarella, gournet→gourmet, vegtables→vegetables, americanfavorite→americanfavorite, newyork→newyork, spicytuna→spicytuna, doubledecker→doubledecker, meatlovers→meatlovers, supersupreme→supersupreme, doublemix→doublemix, doublesignature→doublesignature, bbqmeatfiesta→bbqmeatfiesta, smokedbeef→smokedbeef. If the resulting token is empty, it is missing.
**Parsing and Eligibility**
Scientific notation is forbidden for all numeric parsing; if e or E appears anywhere in the raw value, parsing fails.
Parse the price field into a positive float using these rules: remove whitespace; remove a leading currency prefix rp or idr if present and a trailing rupiah if present; reject any value containing % or any remaining letters; allow a suffix form like number followed by k meaning multiply by 1000; otherwise apply comma rules where a final comma followed by exactly two digits is treated as a decimal comma and all prior commas are thousands separators, and in all other cases commas are thousands separators; after comma handling, only digits with at most one dot are permitted.
Parse the diameter field by extracting the first numeric substring of the form digits optionally followed by one decimal separator and digits; treat a single comma with ≤2 fractional digits as a decimal comma, otherwise remove commas; parse as float, then apply a scale fix where values in [100,400] that are exactly divisible by 10 are divided by 10.
Require diameter in the inclusive range [5,30] and require strictly positive price.
Parse the extra-option fields as booleans after trimming, casefolding, and removing whitespace: true tokens {1,true,t,yes,y}, false tokens {0,false,f,no,n}; anything else or null-like tokens is missing.
Rows containing missing values in any variables required for the entity key, for the price/diameter parses, for the topping token (normalized using the same token normalization as variant/size), or for the extra-option booleans must be removed prior to analysis. The second table additionally requires the extra-mushrooms boolean to be non-missing.
**Duplicate Handling**
Within each table after eligibility, deterministically reduce duplicates by entity key by selecting exactly one row per key using the minimum of the tuple (parse_score, original_row_order), with stable behavior equivalent to mergesort.
Define parse_score as the sum of three binary indicators: whether price parsing used comma handling or the k suffix; whether diameter parsing extracted a numeric substring that was not the entire cleaned diameter string; and whether the diameter scale fix divided by 10. Original_row_order is the file read order index.
**Linkage**
After duplicate reduction, perform an inner join between the two tables on the entity key. All downstream computations operate only on the joined set.
**Compute the Following**
Row and key counts: raw row counts for both tables; eligible row counts for both tables; unique entity-key counts after duplicate reduction for both tables; and the number of joined keys.
Per joined key, compute absolute price difference and relative price difference (absolute difference divided by the first-table price).
Quantiles: compute the 25th, 50th, 75th, and 90th percentiles of absolute price difference using linear interpolation and then round each once to an integer using round-half-up; compute the same percentiles of relative price difference using linear interpolation.
Mean: compute the arithmetic mean of relative price difference.
Consistency: compute the count and rate of topping-token matches; compute sauce mismatch, cheese mismatch, and either mismatch counts and rates, where mismatch means the boolean values differ between tables.
Relative-difference stratification: bin the relative price difference into four quantile bins using behavior equivalent to pandas.qcut with q=4 and duplicates='drop'. Let k be the number of resulting bins. Report bin labels Q1..Qk in increasing bin order, their counts, the bin edges, and smoothed probabilities computed by adding a uniform pseudocount alpha=0.5 to every bin count and then normalizing. If the joined set has fewer than 2 rows, output empty lists for labels, counts, bin edges, and probabilities while still reporting alpha.
Top-5: select up to five joined keys with the largest absolute price difference, breaking ties by larger relative difference, then by lexicographically smaller entity key, using stable sorting. Emit each record as [entity_key, first-table price rounded to nearest int, second-table price rounded to nearest int, absolute difference rounded to nearest int, relative difference rounded to 6 decimals as a fixed-format string].
Mushrooms effect within the second table only: after that table's duplicate reduction, compute counts for mushrooms=True and mushrooms=False. If both groups are non-empty, compute group mean prices, absolute uplift (true minus false), and percent uplift (uplift divided by false mean). If either group is empty, set mean and uplift fields to null but still report the counts.
**Rounding and Reporting**
All rounding must use the round-half-up method (also known as banker's rounding away from zero for .5 cases). All non-integer reported numerics must be rounded exactly once at final reporting to 6 decimals using round-half-up and emitted as fixed-format decimal strings with exactly 6 places. No earlier rounding is allowed.
**Final Output**
Return a single Python list with exactly 7 elements in this exact order:
0. The task identifier string `'PIZZA_RECON_LINK_QCUT_V1'`
1. A counts dict
2. A price-difference dict containing quantiles and mean
3. A consistency dict containing counts and rates
4. A qcut dict containing labels, counts, bin edges, alpha, and smoothed probabilities
5. The top-5 list
6. The mushrooms-effect dict

Grading rubric:
criterion: v1_eligible_rows equals 49 and v2_eligible_rows equals 42, confirming parsing, entity key construction, and eligibility filtering are correct for both tables.
weight: 10
criterion: v1_unique_keys equals 31 and v2_unique_keys equals 29, confirming the deduplication logic (parse_score + stable row order) works correctly.
weight: 10
criterion: overlap_keys equals 14, confirming the inner join on entity keys is correct.
weight: 5
criterion: abs_price_diff_quantiles_rupees equals {'p25': 0, 'p50': 3750, 'p75': 20000, 'p90': 26700}.
weight: 10
criterion: pct_price_diff_mean equals '0.141086'.
weight: 10
criterion: topping_match equals 5 and topping_match_rate equals '0.357143'.
weight: 5
criterion: either_mismatch equals 9 and either_mismatch_rate equals '0.642857'.
weight: 5
criterion: qcut produces exactly 3 bins with labels ['Q1', 'Q2', 'Q3'] and counts [7, 3, 4].
weight: 10
criterion: The smoothed_probs in the qcut dict equal ['0.483871', '0.225806', '0.290323'] with alpha '0.500000'.
weight: 5
criterion: The top-5 list contains exactly these 5 entity keys in order: 'e|newyork|xl', 'b|doubledecker|medium', 'b|crunchy|medium', 'a|supersupreme|reguler', 'd|meatlovers|reguler'.
weight: 10
criterion: The top-5 entry for 'e|newyork|xl' has v1 price 115000, v2 price 83000, abs diff 32000, and relative diff '0.278261'.
weight: 5
criterion: In the mushrooms effect dict, n_true equals 17 and n_false equals 12.
weight: 5
criterion: In the mushrooms effect dict, uplift_pct equals '0.097572'.
weight: 5
criterion: All non-integer numeric values in the output are formatted as fixed-format decimal strings with exactly 6 decimal places.
weight: 5

Golden answer:
```python
import pandas as pd
import re
from decimal import Decimal, ROUND_HALF_UP

NULL_TOKENS = {"", "nan", "none", "null", "-", "###"}

FOLD_MAP = {
    "chikcen": "chicken",
    "mushroms": "mushrooms",
    "mozarela": "mozzarella",
    "gournet": "gourmet",
    "vegtables": "vegetables",
    "americanfavorite": "americanfavorite",
    "newyork": "newyork",
    "spicytuna": "spicytuna",
    "doubledecker": "doubledecker",
    "meatlovers": "meatlovers",
    "supersupreme": "supersupreme",
    "doublemix": "doublemix",
    "doublesignature": "doublesignature",
    "bbqmeatfiesta": "bbqmeatfiesta",
    "smokedbeef": "smokedbeef",
}

def fmt6(x):
    if x is None:
        return None
    d = Decimal(str(x)).quantize(Decimal("0.000001"), rounding=ROUND_HALF_UP)
    return format(d, "f")

def norm_token(x):
    if pd.isna(x):
        return None
    s = str(x).strip().casefold()
    s = re.sub(r"\s+", " ", s)
    if s in NULL_TOKENS:
        return None
    s = s.replace("_", " ")
    s = re.sub(r"\s+", " ", s).strip()
    t = re.sub(r"[^0-9a-z]+", "", s)
    if t == "" or t in NULL_TOKENS:
        return None
    return FOLD_MAP.get(t, t)

def company_initial(x):
    if pd.isna(x):
        return None
    s = str(x).strip().casefold()
    s = re.sub(r"\s+", "", s)
    m = re.search(r"[a-z]", s)
    return None if m is None else m.group(0)

def parse_bool(x):
    if pd.isna(x):
        return None
    s = str(x).strip().casefold()
    s = re.sub(r"\s+", "", s)
    if s == "" or s in NULL_TOKENS:
        return None
    if s in {"1", "true", "t", "yes", "y"}:
        return True
    if s in {"0", "false", "f", "no", "n"}:
        return False
    return None

def parse_price(x):
    if pd.isna(x):
        return (None, 0)
    raw = str(x).strip()
    if raw == "":
        return (None, 0)
    if re.search(r"[eE]", raw):
        return (None, 0)
    s = raw.casefold()
    s = re.sub(r"\s+", "", s)
    if s == "" or s in NULL_TOKENS:
        return (None, 0)
    s = re.sub(r"^(rp|idr)", "", s)
    s = re.sub(r"(rupiah)$", "", s)
    if s == "" or s in NULL_TOKENS:
        return (None, 0)

    complex_flag = 0

    mk = re.fullmatch(r"([0-9]+(?:\.[0-9]+)?)(k)", s)
    if mk:
        complex_flag = 1
        return (float(mk.group(1)) * 1000.0, complex_flag)

    if "%" in s:
        return (None, 0)
    if re.search(r"[a-z]", s):
        return (None, 0)

    if "," in s:
        parts = s.split(",")
        if len(parts) >= 2 and parts[-1].isdigit() and len(parts[-1]) == 2 and all(p.isdigit() for p in parts[:-1]):
            complex_flag = 1
            s_num = "".join(parts[:-1]) + "." + parts[-1]
        else:
            complex_flag = 1
            s_num = "".join(parts)
    else:
        s_num = s

    if re.fullmatch(r"[0-9]+(?:\.[0-9]+)?", s_num) is None:
        return (None, 0)

    return (float(s_num), complex_flag)

def parse_diameter(x):
    if pd.isna(x):
        return (None, 0, 0)
    raw = str(x).strip()
    if raw == "":
        return (None, 0, 0)
    if re.search(r"[eE]", raw):
        return (None, 0, 0)
    s = raw.casefold()
    s2 = re.sub(r"\s+", "", s)

    m = re.search(r"([0-9]+(?:[.,][0-9]+)?)", s2)
    if m is None:
        return (None, 0, 0)

    num = m.group(1)
    complex_flag = 1 if num != s2 else 0

    if "," in num and "." not in num:
        if num.count(",") == 1 and len(num.split(",")[-1]) <= 2:
            complex_flag = 1
            num = num.replace(",", ".")
        else:
            complex_flag = 1
            num = num.replace(",", "")
    else:
        num = num.replace(",", "")

    try:
        val = float(num)
    except Exception:
        return (None, 0, 0)

    scaled_flag = 0
    if val >= 100 and val <= 400 and abs(val / 10.0 - round(val / 10.0)) < 1e-9:
        scaled_flag = 1
        val = val / 10.0

    return (val, complex_flag, scaled_flag)

def prepare(df, has_mushrooms):
    out = df.copy()
    out["_row"] = range(len(out))

    out["company_init"] = out["company"].apply(company_initial)
    out["variant_norm"] = out["variant"].apply(norm_token)
    out["size_norm"] = out["size"].apply(norm_token)
    out["topping_norm"] = out["topping"].apply(norm_token)

    price_parsed = out["price_rupiah"].apply(parse_price)
    out["price_num"] = price_parsed.apply(lambda t: t[0])
    out["price_complex"] = price_parsed.apply(lambda t: int(t[1]))

    dia_parsed = out["diameter"].apply(parse_diameter)
    out["diameter_num"] = dia_parsed.apply(lambda t: t[0])
    out["diameter_complex"] = dia_parsed.apply(lambda t: int(t[1]))
    out["diameter_scaled"] = dia_parsed.apply(lambda t: int(t[2]))

    out["extra_sauce_b"] = out["extra_sauce"].apply(parse_bool)
    out["extra_cheese_b"] = out["extra_cheese"].apply(parse_bool)
    if has_mushrooms:
        out["extra_mushrooms_b"] = out["extra_mushrooms"].apply(parse_bool)

    def make_key(r):
        if r["company_init"] is None or r["variant_norm"] is None or r["size_norm"] is None:
            return None
        return f"{r['company_init']}|{r['variant_norm']}|{r['size_norm']}"

    out["entity_key"] = out.apply(make_key, axis=1)
    return out

def eligible_v1(df):
    return df[
        df["entity_key"].notna()
        & df["price_num"].notna() & (df["price_num"] > 0)
        & df["diameter_num"].notna() & (df["diameter_num"] >= 5) & (df["diameter_num"] <= 30)
        & df["topping_norm"].notna()
        & df["extra_sauce_b"].notna()
        & df["extra_cheese_b"].notna()
    ].copy()

def eligible_v2(df):
    return df[
        df["entity_key"].notna()
        & df["price_num"].notna() & (df["price_num"] > 0)
        & df["diameter_num"].notna() & (df["diameter_num"] >= 5) & (df["diameter_num"] <= 30)
        & df["topping_norm"].notna()
        & df["extra_sauce_b"].notna()
        & df["extra_cheese_b"].notna()
        & df["extra_mushrooms_b"].notna()
    ].copy()

def dedupe(df):
    d = df.copy()
    d["parse_score"] = d["price_complex"] + d["diameter_complex"] + d["diameter_scaled"]
    d = d.sort_values(["entity_key", "parse_score", "_row"], ascending=[True, True, True], kind="mergesort")
    return d.drop_duplicates("entity_key", keep="first").copy()

# Read the provided files by exact filenames.
v1 = pd.read_csv("pizza_v1_uncleaned_large.csv")
v2 = pd.read_csv("pizza_v2_uncleaned_large.csv")

p1 = prepare(v1, has_mushrooms=False)
p2 = prepare(v2, has_mushrooms=True)

e1 = eligible_v1(p1)
e2 = eligible_v2(p2)

d1 = dedupe(e1)
d2 = dedupe(e2)

m = d1.merge(d2, on="entity_key", how="inner", suffixes=("_v1", "_v2"))
m["abs_price_diff"] = (m["price_num_v2"] - m["price_num_v1"]).abs()
m["pct_price_diff"] = m["abs_price_diff"] / m["price_num_v1"]

# Price difference quantiles.
if len(m) == 0:
    abs_q = {"p25": 0, "p50": 0, "p75": 0, "p90": 0}
    pct_q = {"p25": "0.000000", "p50": "0.000000", "p75": "0.000000", "p90": "0.000000"}
    pct_mean = "0.000000"
else:
    abs_qv = m["abs_price_diff"].quantile([0.25, 0.5, 0.75, 0.9], interpolation="linear")
    abs_q = {
        "p25": int(Decimal(str(abs_qv.loc[0.25])).quantize(Decimal("1"), rounding=ROUND_HALF_UP)),
        "p50": int(Decimal(str(abs_qv.loc[0.5])).quantize(Decimal("1"), rounding=ROUND_HALF_UP)),
        "p75": int(Decimal(str(abs_qv.loc[0.75])).quantize(Decimal("1"), rounding=ROUND_HALF_UP)),
        "p90": int(Decimal(str(abs_qv.loc[0.9])).quantize(Decimal("1"), rounding=ROUND_HALF_UP)),
    }
    pct_qv = m["pct_price_diff"].quantile([0.25, 0.5, 0.75, 0.9], interpolation="linear")
    pct_q = {"p25": fmt6(pct_qv.loc[0.25]), "p50": fmt6(pct_qv.loc[0.5]), "p75": fmt6(pct_qv.loc[0.75]), "p90": fmt6(pct_qv.loc[0.9])}
    pct_mean = fmt6(m["pct_price_diff"].mean())

# Option consistency on overlap.
if len(m) == 0:
    topping_match = 0
    topping_match_rate = "0.000000"
    sauce_mismatch = 0
    cheese_mismatch = 0
    either_mismatch = 0
    sauce_mismatch_rate = "0.000000"
    cheese_mismatch_rate = "0.000000"
    either_mismatch_rate = "0.000000"
else:
    topping_match_mask = (m["topping_norm_v1"] == m["topping_norm_v2"])
    topping_match = int(topping_match_mask.sum())
    topping_match_rate = fmt6(topping_match_mask.mean())

    sauce_mismatch_mask = (m["extra_sauce_b_v1"] != m["extra_sauce_b_v2"])
    cheese_mismatch_mask = (m["extra_cheese_b_v1"] != m["extra_cheese_b_v2"])
    either_mismatch_mask = sauce_mismatch_mask | cheese_mismatch_mask

    sauce_mismatch = int(sauce_mismatch_mask.sum())
    cheese_mismatch = int(cheese_mismatch_mask.sum())
    either_mismatch = int(either_mismatch_mask.sum())

    sauce_mismatch_rate = fmt6(sauce_mismatch_mask.mean())
    cheese_mismatch_rate = fmt6(cheese_mismatch_mask.mean())
    either_mismatch_rate = fmt6(either_mismatch_mask.mean())

# qcut distribution on pct_price_diff (duplicates='drop') and smoothed probabilities.
alpha = 0.5
if len(m) < 2:
    q_labels = []
    q_counts = []
    q_edges = []
    q_probs = []
else:
    qcat, qbins = pd.qcut(m["pct_price_diff"], q=4, duplicates="drop", retbins=True)
    intervals = list(qcat.cat.categories)
    k = len(intervals)
    q_labels = [f"Q{i+1}" for i in range(k)]
    label_map = {intervals[i]: q_labels[i] for i in range(k)}
    mapped = qcat.map(label_map)
    q_counts = mapped.value_counts().reindex(q_labels).fillna(0).astype(int).tolist()
    q_edges = [fmt6(b) for b in list(qbins)]
    denom = (sum(q_counts) + alpha * len(q_counts))
    q_probs = [fmt6((c + alpha) / denom) for c in q_counts]

# Top 5 absolute price differences.
if len(m) == 0:
    top5 = []
else:
    top = m.sort_values(["abs_price_diff", "pct_price_diff", "entity_key"], ascending=[False, False, True], kind="mergesort").head(5)
    top5 = []
    for _, r in top.iterrows():
        top5.append([r["entity_key"], int(round(r["price_num_v1"])), int(round(r["price_num_v2"])), int(round(r["abs_price_diff"])), fmt6(r["pct_price_diff"])])

# Within v2: mushrooms price effect.
n_true = int((d2["extra_mushrooms_b"] == True).sum())
n_false = int((d2["extra_mushrooms_b"] == False).sum())
if n_true == 0 or n_false == 0:
    eff = {"n_true": n_true, "n_false": n_false, "mean_price_true": None, "mean_price_false": None, "uplift_abs": None, "uplift_pct": None}
else:
    mean_true = d2.loc[d2["extra_mushrooms_b"] == True, "price_num"].mean()
    mean_false = d2.loc[d2["extra_mushrooms_b"] == False, "price_num"].mean()
    eff = {
        "n_true": n_true,
        "n_false": n_false,
        "mean_price_true": fmt6(mean_true),
        "mean_price_false": fmt6(mean_false),
        "uplift_abs": fmt6(mean_true - mean_false),
        "uplift_pct": fmt6((mean_true - mean_false) / mean_false),
    }

out = [
    "PIZZA_RECON_LINK_QCUT_V1",
    {
        "v1_raw_rows": int(len(v1)),
        "v2_raw_rows": int(len(v2)),
        "v1_eligible_rows": int(len(e1)),
        "v2_eligible_rows": int(len(e2)),
        "v1_unique_keys": int(d1["entity_key"].nunique()),
        "v2_unique_keys": int(d2["entity_key"].nunique()),
        "overlap_keys": int(len(m)),
    },
    {
        "abs_price_diff_quantiles_rupees": abs_q,
        "pct_price_diff_quantiles": pct_q,
        "pct_price_diff_mean": pct_mean,
    },
    {
        "n_overlap": int(len(m)),
        "topping_match": topping_match,
        "topping_match_rate": topping_match_rate,
        "sauce_mismatch": sauce_mismatch,
        "cheese_mismatch": cheese_mismatch,
        "either_mismatch": either_mismatch,
        "sauce_mismatch_rate": sauce_mismatch_rate,
        "cheese_mismatch_rate": cheese_mismatch_rate,
        "either_mismatch_rate": either_mismatch_rate,
    },
    {
        "labels": q_labels,
        "counts": q_counts,
        "bin_edges": q_edges,
        "alpha": fmt6(alpha),
        "smoothed_probs": q_probs,
    },
    top5,
    eff,
]

print(out)
```

**Step-wise Instructions:**

1. Load both tables into pandas DataFrames
2. Add a stable row-order index reflecting file read order
3. Normalize the company text and extract the first ASCII letter as the company initial
4. Normalize variant, size, and topping tokens via trimming, casefolding, punctuation removal, and typo folding
5. Parse the price field into a numeric value, rejecting scientific notation and invalid symbols
6. Parse the diameter field by extracting the first numeric substring and applying the scale-by-10 correction rule
7. Parse option fields into strict booleans using only the allowed true/false token sets
8. Build the entity key and remove rows failing any required parse or missingness rule
9. Deduplicate within each table by entity key using minimal parse_score and stable row order
10. Inner-join the two deduplicated tables on the entity key
11. Compute absolute and relative price differences for all joined keys
12. Compute required quantiles and mean of the differences using linear interpolation
13. Compute topping match and option mismatch counts and rates
14. Build the qcut binning on relative difference with duplicates dropped and compute smoothed probabilities with alpha=0.5
15. Extract the deterministic top-5 largest absolute differences and compute the mushrooms effect within the second table

**Golden Answer Output:**

```
['PIZZA_RECON_LINK_QCUT_V1', {'v1_raw_rows': 152, 'v2_raw_rows': 152, 'v1_eligible_rows': 49, 'v2_eligible_rows': 42, 'v1_unique_keys': 31, 'v2_unique_keys': 29, 'overlap_keys': 14}, {'abs_price_diff_quantiles_rupees': {'p25': 0, 'p50': 3750, 'p75': 20000, 'p90': 26700}, 'pct_price_diff_quantiles': {'p25': '0.000000', 'p50': '0.111111', 'p75': '0.280503', 'p90': '0.309119'}, 'pct_price_diff_mean': '0.141086'}, {'n_overlap': 14, 'topping_match': 5, 'topping_match_rate': '0.357143', 'sauce_mismatch': 6, 'cheese_mismatch': 6, 'either_mismatch': 9, 'sauce_mismatch_rate': '0.428571', 'cheese_mismatch_rate': '0.428571', 'either_mismatch_rate': '0.642857'}, {'labels': ['Q1', 'Q2', 'Q3'], 'counts': [7, 3, 4], 'bin_edges': ['0.000000', '0.111111', '0.280503', '0.346667'], 'alpha': '0.500000', 'smoothed_probs': ['0.483871', '0.225806', '0.290323']}, [['e|newyork|xl', 115000, 83000, 32000, '0.278261'], ['b|doubledecker|medium', 96000, 123000, 27000, '0.281250'], ['b|crunchy|medium', 75000, 49000, 26000, '0.346667'], ['a|supersupreme|reguler', 70000, 90000, 20000, '0.285714'], ['d|meatlovers|reguler', 90000, 70000, 20000, '0.222222']], {'n_true': 17, 'n_false': 12, 'mean_price_true': '86936.823529', 'mean_price_false': '79208.333333', 'uplift_abs': '7728.490196', 'uplift_pct': '0.097572'}]
```