Documentation
¶
Overview ¶
Package writingstylescope is a keelson app for finding shared writing between two documents. Paste two Markdown documents; the app splits each into sections (one per heading), measures the compression distance of every A-section against every B-section, and shows the result three ways: a document-level headline, the cross-matrix as a heatmap, and the empirical distribution of all the pairwise distances.
The measurement is normalized compression distance (NCD): compress the two texts together and see how much smaller that is than compressing them apart. A pair that shares wording compresses well together. The engine is public/analytics/similarity/compression and its stylometry sub-package.
The app returns no verdict, and it asserts no threshold — NCD has no absolute scale that survives a change of subject, language, or section length. What it shows instead is where each pair sits in the background distribution formed by every other pair of the same two documents. A section that was copied is the point that does not belong to that distribution. Deciding what that means is the reader's job. The decision and its alternatives are ADR-0175.
Three dock tabs: "Documents" (paste, sweep, headline), "Matrix" (the section-by-section heatmap and the closest pairs) and "Distribution" (the ECDF that calibrates them). The pairs table hands the whole cross-matrix to the SQL playground as an ephemeral dataset — see writingstylescope_handover.go.
Lifecycle: Mount captures the id stack, logger and bus; Frame renders inside the host-owned window and runs the sweep when the user asks for one; Unmount cancels any confidence-band warm-up still in flight and retracts the published dataset.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Analysis ¶
type Analysis struct {
// SecA / SecB are the sections that were actually swept — the ones that
// cleared MinSectionBytes. DroppedA / DroppedB count those that did not.
SecA []Section
SecB []Section
DroppedA int
DroppedB int
// Ncd is the cross-matrix, row-major: cell (i, j) is at i*len(SecB)+j.
Ncd []float64
// Sorted is Ncd ascending — the ECDF sample and the ranking source.
Sorted []float64
// Pairs is the closest maxRankedPairs cells, ascending by Ncd.
Pairs []Pair
// Document-level readings from stylometry's own measurement modes, taken
// with document A (its kept sections, concatenated) as the fixed
// reference. Profile mode truncates both sides to a common length;
// instance mode streams B's sections past the reference and may stop
// early — InstConverged and InstCount say whether it did.
ProfileNcd float64
ProfileCcc float64
InstCount int64
InstMin float64
InstMean float64
InstMax float64
InstStdDev float64
InstConverged bool
MinSectionBytes int
Elapsed time.Duration
}
Analysis is one completed sweep. It is immutable once returned and is held by the app until either pane's text changes.
func (*Analysis) At ¶
At returns the NCD of A-section i against B-section j. Out-of-range coordinates return NaN rather than panicking — a hover readout computes cell coordinates from cursor position and may briefly be off the grid.
func (*Analysis) Quantile ¶
Quantile returns the fraction of the matrix at or below ncd — where a cell sits in this document pair's own background distribution. This is the number the app reports instead of a threshold verdict (§SD3): "closer than all but 0.1% of pairs" is a statement about these two documents, where "below 0.4" would be a statement about a corpus nobody measured.
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the per-window writingstylescope instance.
type Pair ¶
Pair is one cell of the cross-matrix: section I of document A against section J of document B.
type Section ¶
type Section struct {
// Title is the heading's flattened plain text — inline styling dropped,
// any `{#anchor}` suffix already stripped by the parser. Empty for the
// preamble section.
Title string
// Level is the ATX heading depth, 1..6. Zero for the preamble.
Level uint8
// Start and End are byte offsets into the source the section was sliced
// from; Text is that slice.
Start int
End int
Text string
}
Section is one heading's own prose: the span from that heading's line start to the next heading's line start, at whatever level the next heading sits (ADR-0175 §SD1). Sliced that way a section never contains a descendant section's text, so the same prose is never measured twice, and the sections that carry substantial text are the deep ones — which is the granularity the app compares at.
The preamble (text before the first heading, after any frontmatter) is a Section with an empty Title and Level 0.