# Composite Solver Skeleton

This package bundles specialized solvers behind one profiler and dispatcher.
The exported binary keeps the normal lymphosat solver contract:

```bash
./solver <formula.cnf> <proof.out>
```

## Layout

```text
core/
  formula_profile.h   shared FormulaProfile schema and matcher macro
  profiler.cpp        DIMACS parser and cheap structural feature extractor
  main.cpp            dispatch loop and debug modes
order.json            ordered matcher/solver list plus fallback solver name
solvers/<name>/
  solver/             nested solver package; build.sh must produce ./solver
  matcher/match.cpp   in-process matcher for this solver
```

`build.sh` first runs every referenced nested `solvers/<name>/solver/build.sh`,
then uses CMake to compile the fixed core and all `match.cpp` files. CMake reads
`order.json`, assigns each matcher a unique symbol, and generates the matcher
registry used by `core/main.cpp`.

## Matcher API

A matcher source file should include `formula_profile.h` and define exactly one
function with `LYMPHOSAT_MATCHER`:

```cpp
#include "formula_profile.h"

LYMPHOSAT_MATCHER {
    return profile.binary_clause_fraction > 0.8 &&
           profile.all_positive_clause_fraction > 0.01;
}
```

The macro expands to the unique linker symbol generated for that solver entry.
Matcher code must be deterministic, in-process, and cheap.

## Debug Modes

Emit a cached profile:

```bash
./solver formula.cnf --profile-only profile.json
```

Check linked matcher behavior against a cached profile:

```bash
./solver --check-match profile.json
./solver --check-match profile.json example
```

`--check-match` deserializes the cached JSON back into a `FormulaProfile`.
The profile JSON mirrors that struct: each struct field is a top-level key,
with `clause_len_counts` and `clause_len_fracs` represented as arrays. The
supported format does not include a nested `features` object, `feature_names`,
or `feature_vector`; the JSON key `binary_clause_fraction` maps directly to the
C++ member `profile.binary_clause_fraction`.

List compiled matchers:

```bash
./solver --list-matchers
```

## Dispatch Semantics

At runtime the dispatcher computes one `FormulaProfile`, tries matchers in
`order.json`, and launches the first matching solver package. If that solver
returns `SAT` or `UNSAT` (`10` or `20`), the dispatcher returns the result. If it
returns `UNKNOWN` (`0`) or crashes, the dispatcher continues to the next match
and finally tries `fallback_solver`.
