Conversation
Move the "Download Verilog output" button below the output box in the web UI, and drop the stray blank lines from the emitted Verilog so small modules render compactly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Runs arjun --synth on a 3-clause CNF and fails if any blank line appears in the emitted Verilog. Guards against regressions of the prior cleanup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Craig interpolant reconstructed from a successful cadical proof trace is mathematically correct; the only outcomes are "valid interpolant" or "no interpolant available" (empty/no-input conflict, oversize AIG, budget exhausted). Reword CLAUDE.md, the verification-helper docstrings, the two release_assert messages, and the A->I / sample_check assertion-fail notes to reflect that — and re-frame the public check helpers as SLOW_DEBUG / test-only sanity helpers (which is how they are actually called: only from SLOW_DEBUG_DO blocks and unit tests). Comments and assertion messages only — no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The probe finds defs of the form t = L or t = ~L (equivalence to a single literal), which is what "equiv" names. The "conditional" label was misleading. Affects the config fields, public API setters/getters, internal identifiers, CLI flags (--unatedefcond* → --unatedefeq*), fuzzers, and comments. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plumbing only: declares the new Config knob and exposes it as --interprepairfullconf in main.cpp. No behavior change yet; the follow-up commits wire it through interp_repair and manthan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generalises the McMillan tracer to operate on an arbitrary shared-var set rather than hard-coding input_vars. compute_interpolant() now takes a full_conflict flag that, when set, moves every conflict unit to the B side (input + y_other) and tells the tracer the shared set is every conflict var. The resulting interpolant covers the whole conflict clause as-is — a smaller AIG over all conflict vars, not just the input projection. setup_mini_cnf, solve_one_interpolant, slow_check_a_implies_i, and the SLOW_DEBUG leaf-check all branch on the new flag. The quick/sample CEX-exclusion checks pin every conflict lit (input + y_other) so they work for both modes uniformly. Adds calls_full_conflict / calls_full_conflict_succeeded stats and extends print_stats accordingly. No caller switches the flag on yet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Forwards the new flag into compute_interpolant() so the full-conflict partition (B = every conflict unit, not just inputs) is used when the config knob is on. build_interp_branch_formula skips the y_other-leaf ANDs in that mode — the interpolant already encodes them, so the AND would just duplicate the constraint and bloat b1. The SLOW_DEBUG sanity calls and the startup InterpRepair-enabled verb_print are updated to take/show the flag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the new full-conflict flag to the per-iteration random choice in both fuzz_interp_repair.py (which forces interp_repair on every run) and fuzz_synth.py (which enables interp_repair ~25% of the time). Both fuzzers now flip the partition randomly so we exercise the new code path alongside the existing input-only one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The TOP REPAIRED VARS block conflated repair attempts with successful repairs and printed AIG-node vs conflict-lit averages with the same label, making "avg_confl_sz: 0.0" silently mean "no conflict-branch successes" rather than "size was zero". Split the bookkeeping by path: - attempts counter (= repair() calls incl. cost-zero failures) - per-var interp-branch successes + AIG-node sum - per-var conflict-branch successes + conflict-lit sum - cost-zero count derived as attempts - (interp + confl) Print attempts, the three path counts (i / c / z), interp% over successful repairs only, and avg sizes printed as "-" when their denominator is zero. Column names shortened ([m-stats], att/i/c/z/i%/ acl/ain/cl/an/ad/cf) so the line fits 1 row. Legend block follows the table. Drops the now-redundant "top vars driven by interp" block and the interp_conflict_lits_per_var field it relied on. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The per-pass stats line was wrapping in terminals and printed the "[aig-rewrite]" prefix twice (caller printed T:, then stats.print printed the rest with its own prefix on the same logical line). Fold the time into AIGRewriteStats (new total_time field) so print() emits exactly one prefixed line; shorten the prefix to [aig-rw] / [aig-rw/sweep] / [aig-rw/sweep-setup] / [aig-rw/sweep-prog] and abbreviate field names (n: cp: cmp: idm: abs: dst: xor: hh: for rewrite; g/chk/m/cm/r/cxf/to/ca/be/srv/cyc for sweep). Reduction is shown as "-X.X%" inline rather than "(X.X% reduction)". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deep proof-driven interpolant AIGs (200k+ nodes on real benchmarks)
overflow the program stack in the std::function-based recursive
rebuild lambdas. Failure mode: SIGSEGV in synth-extend's simplify_aig
on the first big interpolant, and a second crash in rewrite_aigs after
unate_def. Repro:
./arjun --mstrategy bve --synth --interprepairfullconf 0 \
--interprepair 1 benchmarks-qdimacs/load_full_3_comp5_REAL.sat.qdimacs
Converted the recursion sites to explicit stack-based post-order. The
algebraic bodies (simplify_pass's absorption / distribution / XOR
patterns, etc.) are unchanged — only the recursion shape is rewritten.
Touched:
arjun.cpp: AIG::simplify, AIG::simplify_cse,
SimplifiedCNF::map_aigs_to_orig's rebuild lambda
aig_rewrite.cpp: AIGRewriter::simplify_pass / hash_cons /
deep_absorb / flatten_ite_chains, plus rebuild_node
in sat_sweep (same pattern, fix in one pass)
The above repro now runs to completion with exit 0; Manthan finishes
with all 475 vars defined. fuzz_aig_rewrite/1000, fuzz_aig_to_cnf/200,
fuzz_synth/200, test-aig-rewrite, test-interp-repair all pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the table sorted by total repair() attempts (att = i + c + z), which let cost-zero-heavy vars dominate the "top" list even though their formulas were never actually repaired. Sort by i+c instead (interp + conflict-branch successes), tie-break on att, and rename the heading to TOP SUCCESSFULLY REPAIRED VARS so it matches what it shows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scaffolding for an in-tree port of CADET (Markus N. Rabe, SAT 2016) as
an alternative to Manthan for the final synthesis step. Enabled with
--cadet 1.
Phase A (this commit): when |inputs| <= 16, build each Skolem function
by enumerating every input assignment, calling cadical under the
assumption, and OR-ing input-minterms into a per-y AIG. Slow but
correct — sufficient to exercise the wiring on fuzzer-sized inputs.
Phase B (unique-consequence propagation) and Phase C (decisions +
conflict analysis), which together make up the actual incremental-
determinization algorithm, are the next milestones; they will replace
Phase A as the primary path and let CADET handle benchmarks with too
many inputs to enumerate.
Wiring:
- new Cadet class in src/cadet.{h,cpp}, mirroring Manthan's API
- Arjun::standalone_cadet() in arjun.{h,cpp}
- --cadet flag in main.cpp; do_synthesis() dispatches to standalone_cadet
instead of run_manthan_strategies when set
Verified manually on out/fuzzTest_1.cnf with --synthbve 0 --extend 0
--minimize 0 --unatedef 0 to force the cadet path (49 to_define vars,
4 inputs, 16 SAT calls); test-synth reports the result CORRECT.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes: 1. Enumeration scope: Phase A was iterating over `VarTypes.input`, which in addition to the orig sampling vars also includes extend-defined vars (vars whose AIG def already depends only on orig sampling vars). Those are not free — F constrains them via their AIG — so most of the 2^|input| assumption sets we'd build would be inconsistent and immediately rejected, and the iteration count would explode past the threshold. Track the orig sampling vars in CNF numbering separately (`orig_sampl_cnf`) and enumerate only over those; the SAT solver propagates the dependent extend-defined vars for free. 2. Dedicated fuzzer at scripts/fuzz_cadet.py (symlinked into build/). Mirrors fuzz_synth.py's shape but trims the option matrix to flags CADET actually consumes, smaller Brummayer CNFs, and clamps the projection size so |orig sampling| stays under Phase A's enumeration threshold. Verified by running ./fuzz_cadet.py --num 50 — all iterations pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase A previously OR'd one minterm per SAT call into the running Skolem AIG. After AIG-rewrite that compresses, but the intermediate representation is O(2^n_inputs) per to_define var — slow to construct and a lot of churn for the rewriter to undo. Now: collect a value table per y across all input assignments, then build the Skolem AIG by bottom-up Shannon decomposition over the sorted inputs. ITE(b, l, r) already collapses identical-children at the AIG layer, so constant regions of the function vanish naturally and shared subtrees are not built twice. Faster construction, far smaller intermediate AIG, identical post-rewrite results. Verified by ./fuzz_cadet.py --num 30 (all CORRECT) and by direct comparison on a hand-rolled 8-input CNF (1000 nodes for 12 vars, correctness check passes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase B decomposes F's clause graph into connected components (only orig sampling vars are sinks; extend-defined and backward-defined vars are traversed through, otherwise different components would implicitly share orig sampling vars via their AIG defs and produce mutually-inconsistent Skolems), then enumerates each component's to_define vars jointly over the component's restricted input set. When F decomposes naturally this is much cheaper than Phase A's global enumeration (a component with 3 inputs needs 2^3, not 2^|all-inputs|), and each component's to_defines come from the SAME SAT call so their Skolems are mutually consistent. A first version of this commit treated extend-defined/backward-defined vars as sinks too; that was incorrect and produced wrong AIGs on seed 9340754230162260130 (test-synth: "Sample does not satisfy the CNF"). The fix — only stop BFS at orig sampling vars — is also why Phase B is a stepping stone, not the endpoint: components grow through every defined intermediate, so this still won't help on CNFs where backward/extend has already linked everything to everything else. Phase C will be the answer for those. Verified by ./fuzz_cadet.py --num 100 — all 95 reachable iterations report AIGs CORRECT (the other 5 hit UNSAT CNFs or <2-var CNFs and are skipped). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase C is the actual CADET-flavored algorithm: for each undetermined to_define var y, iterate its clauses; when every other literal in a clause is already a function of inputs / earlier-determined vars, the clause forces y to a specific value over the "forced region" (where all other literals are false). OR positive-force regions across all positive-y clauses to get y's Skolem candidate. Iterate to fixpoint. Falls through to Phase B / Phase A if some y can't be propagated this way — Phase D (decisions + conflict analysis) would handle those. Cycle hazard fix: even when pos_force_y's direct cnf-space leaves are all "non-y", the leaves' orig-space AIG defs (from earlier extend / backward passes) may transitively reference y_orig. Committing then closes a defs[] cycle. Caught seed 12845165257551977592: extend had defined orig-19 via an AIG referencing orig-18 (then undef), and Phase C tried to define orig-18 via a clause involving orig-19. Fix: walk pos_force's cnf leaves, map each to orig, follow the transitive defs[] chain via get_dependent_vars_recursive, and reject the commit if y_orig is reachable. A separate one-line bug along the way: get_dependent_vars_recursive returns its result in insertion order, not sorted, so a binary_search on it silently always returns false — fixed to use a linear find. Fuzzer adjustments (fuzz_cadet.py): - Force all toggleable preprocessing passes (synthbve, autarky, extend, minimize, unate_def, ...) OFF, so the upstream pipeline can't always finish synthesis before cadet sees the CNF — otherwise the fuzzer never exercises Phase C/B. - Project size still clamped under Phase A's enumeration threshold. Verified: ./fuzz_cadet.py --num 100 → 92 CORRECT, 0 BUGs. Phase C is the primary entry; falls through to Phase B for the cases where propagation gets stuck (most fuzzer CNFs, since to_defines tangle). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously every iteration forced all toggleable preprocessing passes OFF, so the fuzzer never exercised cadet with a partially-preprocessed CNF — the more realistic case. Now: 50% of iterations keep everything off (cadet does the whole synthesis), 50% randomize each toggle independently with 80% off / 20% on (cadet sees a post-preprocessing CNF, like real pipelines). Verified: ./fuzz_cadet.py --num 150 → 140 CORRECT, 0 BUGs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When Phase C's propagation fixpoint leaves vars undetermined, pick the
undet var with the fewest clauses and ask the SAT solver whether F
(plus already-committed constant decisions) FORCES it to a constant:
if F + (y=true) is UNSAT → commit skol[y] := false
elif F + (y=false) is UNSAT → commit skol[y] := true
else → y is genuinely input-dependent,
fall back (Phase B/A)
The "F forces y = constant" check is what makes this sound: a looser
"F + (y=c) sat for some input" check would commit y to a constant
even when y's correct Skolem is non-constant, producing wrong AIGs
on other inputs (caught on seed 1152065877721653320 — wrong final.aig
under -u verification).
Trade-off: the sound check is also strict, so Phase D rarely fires on
fuzzer CNFs (most to_define vars after preprocessing are not constant
in F). When that happens, Phase C+D falls through to Phase B / Phase A
as before. The proper next step — non-constant decisions with conflict
analysis and backtracking — is left for future work.
Fuzzer changes (fuzz_cadet.py):
- Bigger CNFs half the time (Brummayer -i 12 -I 30), and a wider
projection-size range, so the run is forced past Phase A's 16-input
threshold and exercises Phase C+D + Phase B.
- New exit-code-42 protocol: cadet prints "c o [cadet] LIMITATION:"
and exits 42 when no implemented phase can finish. The fuzzer reads
exit 42 as a soft skip (with a "[skip]" log line), not a bug.
Without this the fuzzer would gate on every too-hard CNF.
Verified: ./fuzz_cadet.py --num 200 → 133 CORRECT, 0 BUGs, 54 skips.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three changes that together turn --cadet 1 from "cadet OR fail" into "cadet does what it can, Manthan finishes the rest": 1. Phase D loops over all undet candidates. The previous version broke on the first failed candidate, which on real benchmarks (sdlx-fixpoint-5) caused cadet to give up without trying any of the other 80 vars. Now: try every candidate ordered by ascending clause count, commit any that F forces, only fall through when no candidate is forced. 2. Cadet returns partial results instead of exit(42). When some phase can't finish, commit whatever skol[y] entries are non-null and return a CNF that may not be synth_done(). Phase B/A still reset skol[], so they only run when Phase C+D committed nothing — otherwise their reset would clobber Phase C+D's partial work. 3. main.cpp falls through to run_manthan_strategies() when --cadet 1 left vars undetermined. Manthan starts from the partial CNF where cadet's commits already live in cnf.defs (so they appear as backward_defined to Manthan), and only synthesizes the remaining vars. Strictly ≥ Manthan-alone: cadet can only ADD definitions, never remove them. User's sdlx-fixpoint-5 case: cadet commits 0 (no forced constants after preprocessing), hands off to Manthan, Manthan synthesizes all 81 vars in 156 s. test-synth reports CORRECT. Fuzzer changes (fuzz_cadet.py): - run_synth returns an `info` dict with cadet stats (committed count, partial vs all, handoff triggered) parsed from arjun's verb=1 output. - Main loop accumulates per-iteration stats and prints a summary at the end. The fuzzer FAILS if ≥ 20 iterations succeeded but ZERO of them had cadet commit ≥ 1 var AND hand off to Manthan — the whole point of the new coverage being to exercise that path. - Exit code 42 / LIMITATION handling removed (cadet no longer exits on incomplete synthesis). 100-iter fuzz: 93 succeeded, 0 BUGs, 22 fully-by-cadet, 58 cadet- partial-then-handoff (the new path we wanted to cover), 0 cadet- did-nothing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cross-tests the cadet→Manthan handoff against fuzz_synth.py's full Manthan flag matrix (which fuzz_cadet.py keeps fixed). Cadet does what it can, main.cpp falls through to the Manthan strategy ladder for the rest. Verified by running ./fuzz_synth.py --num 30 — 11/30 ran --cadet 1, all 28 successful runs verified CORRECT, 0 BUGs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the gap between Phase C+D's partial commits and the Manthan
handoff. Previously, when Phase C+D committed some but not all
to_define vars, Phase B/A couldn't help (they reset skol[] and would
clobber the partial work), so everything not handled by Phase C+D
fell through to Manthan even when the orig sampling space was small.
Phase E:
1. Builds a fresh SAT solver with F.
2. Tseitin-encodes every prior skol[] entry into the solver
— Phase C's pos_force AIGs via the existing AIGToCNF encoder,
Phase D's constant commits as unit clauses — and links each
y to its encoded skol via y ↔ root.
3. Enumerates orig sampling assignments via repeated SAT solving,
forbidding each found input pattern. Every model already
respects prior commits, so the y values it gives for the
still-undet vars are jointly consistent with what was
committed.
4. Stops when the solver returns UNSAT (every consistent input
pattern visited). Builds undet y's Skolems by Shannon
decomposition over the resulting value tables — same shape
as Phase A's output.
Threshold: |orig_sampl_cnf| <= kSmallInputThreshold (16). Above that
Phase E gives up and lets the caller hand off to Manthan, as before.
Fuzzer (fuzz_cadet.py) now tracks Phase E specifically:
- info["phase_e_ran"], info["phase_e_committed"] per iteration.
- Stats counters phase_e_ran and phase_e_finished_synthesis.
- Asserts that with >=30 successful iterations, Phase E ran at least
once AND finished synthesis at least once — otherwise the fuzzer
is misconfigured and not exercising Phase E.
Verified: ./fuzz_cadet.py --num 100 → 91 succeeded, 0 BUGs.
cadet_committed_all: 59 (was 22 pre-Phase-E)
cadet_committed_partial_then_handoff: 21 (was 58 pre-Phase-E)
phase_e_ran: 44, phase_e_finished_synthesis: 44.
Phase E shifts ~half of the previously-handoff iterations to fully-by-
cadet finishes. Manthan still picks up the cases Phase E can't reach
(large orig sampling space).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The pass labeled "CADET" was a brute-force Shannon-tree synthesizer, not incremental determinization — rename file, class (Cadet → ShannonSynth), public API (standalone_cadet → standalone_shannon_synth), config field (cadet_phase_e_threshold → shannon_synth_threshold), CLI flags (--cadet → --shannonsynth, --cadetphaseeth → --shannonsynththresh), log tags ([cadet] → [shannon_synth]), and debug AIG suffix accordingly. Flip the binary default to --shannonsynth 1 and update fuzz_synth.py to toggle 50/50 across both terminal paths. Delete fuzz_cadet.py — its --cadetcegar*/--cadetpartial knobs were removed in b164dff and the remaining coverage is folded into fuzz_synth.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Topology + 64-bit signature + used_vars build moves out of aig_rewrite.cpp into a standalone SimState class so the upcoming interp_resub pass can share it. SweepState now inherits from SimState and keeps only the sat-sweep-specific candidate-class / substitution scratch. sat_sweep stats line is bit-identical for a fixed-seed CNF before and after the refactor. Also fix up references to incorrect interpolants in code/comments Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
By the time shannon_synth runs, the CNF has been through extend_synth, backward_round_synth, AIG rewrite, and unate_def — passes that can introduce dependencies among orig sampling vars that didn't exist at initial Minimize::run_minimize time. Run Backward::minimize_subset on the current CNF with orig_sampl_cnf as the candidate set to shrink the enumeration domain before the 2^N truth-table allocation. Smaller domain → smaller tables, fewer SAT calls, and more cases stay under shannon_synth_threshold (16). E.g. on a fuzz CNF this trims |orig_sampl_cnf| 31 → 27, cutting 2^N rows 16×. The new Backward::minimize_subset is a single-use, dry-run entry: it inlines fill_solver's body to bypass the opt_sampl_vars guard and overrides sampling_vars with the caller's candidate, then runs the usual init → get_incidence → duplicate_problem → add_fixed_clauses → backward_round chain. Does not touch cnf state. Gated by --shannonsynthminim (default 1). fuzz_synth.py randomizes the flag 0/1 independently of --shannonsynth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two behavior changes plus cleanup: 1. Soft fallback. When the enum set (after minim) exceeds shannon_synth_threshold, shannon_synth now returns the CNF unchanged (synth_done() stays false) rather than release_asserting. main.cpp runs shannon first and falls through to Manthan when it declines, so --shannonsynth being default-on no longer crashes on inputs whose independent support is too large to brute-force. 2. Minim cap. --shannonsynthminimmax (default 40) bounds the candidate size at which the dry-run backward minim is attempted; above it the doubled-CNF minim is too costly and unlikely to reach the threshold, so it's skipped. Also: trim shannon_synth comments to WHY-only one-liners and replace the clunky `if (conf.verb >= 1) cout << "c o ..."` blocks with verb_print. fuzz_synth.py drops the now-dead threshold-skip branch and randomizes --shannonsynthminimmax. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zlib.net DNS resolution fails in CI; download the identical zlib-1.3.1 tarball from the madler/zlib GitHub release instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the AIGRewriter::sat_sweep pass and all its plumbing: the sweep
implementation and helpers in aig_rewrite.{h,cpp}, the now-dead SimState
helper (aig_sim.{h,cpp}, only ever a base for SweepState), the
--sat-sweep / --interprepairb1satsweep flags and their config, the
post-sweep opt-sampl revert logic in arjun.cpp, and the sweep calls in
manthan.cpp. Also strips the fuzzer's sweep/multi-def modes and the
dead flags from the fuzz scripts.
Build is clean; all four fuzzers and test-interp-repair pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
deep_absorb's per-node computation was a ~220-line inline block inside the traversal loop, hard to review. Extract it into absorb_and_node plus one helper per stage (absorb_local_and, fold_and_children, absorb_wide_or, absorb_cross_level, resolve_or_pairs). The traversal skeleton now just calls absorb_and_node(l, r). No behavior change; fuzzers and test-aig-rewrite pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.