Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
# КАДЕНС: по расписанию, НЕ per-PR (мутация дорога — каждый мутант = полный прогон
# набора). Запускается ночным workflow `.github/workflows/mutation.yml`.
#
# СКОУП: параметр-свободное ЯДРО МАТЕМАТИКИ — самый критичный, детерминированный,
# включая exact alpha/composite и typed numerical decisions. Гигантские
# config.rs/solve.rs/semantic.rs НЕ
# мутируются по умолчанию (часы прогона, малая маржинальная ценность на orchestration-
# коде). Полный workspace-прогон — отдельный ручной/расширенный запуск (см. workflow).
# СКОУП: детерминированное ядро математики и границы correctness-evidence:
# exact alpha/composite, typed numerical decisions, observation admission и
# revision-bound recheck. Гигантские config.rs/solve.rs/semantic.rs не мутируются
# по умолчанию (часы прогона, малая маржинальная ценность). Полный workspace-прогон
# — отдельный ручной/расширенный запуск (см. workflow).
#
# ЧТО ПРОВЕРЯЕТ: что property/golden/characterization тесты РЕАЛЬНО кусаются —
# выживший мутант = молчаливая дыра в наборе (coverage=пол, mutation=правда).
Expand All @@ -16,12 +16,15 @@ examine_globs = [
"crates/labcolors-core/src/analog.rs",
"crates/labcolors-core/src/appearance.rs",
"crates/labcolors-core/src/composition.rs",
"crates/labcolors-core/src/constraints/mod.rs",
"crates/labcolors-core/src/constraints/exact.rs",
"crates/labcolors-core/src/glow.rs",
"crates/labcolors-core/src/material.rs",
"crates/labcolors-core/src/numerical_plan.rs",
"crates/labcolors-core/src/numerics.rs",
"crates/labcolors-core/src/observation.rs",
"crates/labcolors-core/src/pair.rs",
"crates/labcolors-core/src/recheck.rs",
"crates/labcolors-core/src/srgb8.rs",
"crates/labcolors-core/src/wcag22.rs",
"crates/labcolors-core/src/wcag22_evidence.rs",
Expand Down
126 changes: 65 additions & 61 deletions crates/labcolors-core/src/analog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,15 @@
use crate::Srgb8;
use crate::appearance::{
PhysicalProgramIdentityV1, PointOpacityOverSurfaceV1, ProgramOccurrenceBindingV1,
ResolvedOccurrence, SourceOverCertificateV1,
SourceOverCertificateV1, VisiblePointBindingV1,
};
use crate::composition::AdmittedOpacityV1;
use crate::constraints::{
ExactConstraintIdentityV1, ExactIdentityMismatchV1, ExactSrgb8IdentityV1,
BoundAssessment, BoundVerdict, ExactConstraintIdentityV1, ExactIdentityAssessmentV1,
ExactIdentityCapabilityV1, ExactIdentityMismatchV1, ExactIdentityReleaseV1,
ExactSrgb8IdentityV1, assess,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ExactIdentityCapabilityV1 {
FinalOccurrenceSrgb8IdentityV1,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ExactIdentityReleaseV1 {
V1,
}

/// Opaque identity authored invocation-а. Standalone helper не притворяется
/// client binding; named compiler назначает ordinal конкретной декларации.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand All @@ -47,35 +40,43 @@ pub(crate) struct ExactIdentityEvidenceV1 {

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct VerifiedAlphaAnalogV1 {
occurrence: ResolvedOccurrence,
authored: AuthoredAlphaBindingIdV1,
target: Srgb8,
assessment: BoundAssessment<
VisiblePointBindingV1,
ExactConstraintIdentityV1,
ExactIdentityReleaseV1,
ExactIdentityCapabilityV1,
Srgb8,
ExactIdentityAssessmentV1,
>,
}

impl VerifiedAlphaAnalogV1 {
pub(crate) fn tint(&self) -> Srgb8 {
Srgb8::new(self.occurrence.certificate().subject_rgb())
Srgb8::new(self.certificate().subject_rgb())
}

pub(crate) fn alpha(&self) -> f64 {
f64::from_bits(self.occurrence.certificate().subject_opacity_bits())
f64::from_bits(self.certificate().subject_opacity_bits())
}

pub(crate) const fn occurrence(&self) -> &ResolvedOccurrence {
&self.occurrence
pub(crate) fn certificate(&self) -> &SourceOverCertificateV1 {
self.assessment.binding().occurrence_ref()
}

pub(crate) fn evidence(&self) -> ExactIdentityEvidenceV1 {
let binding = *self.assessment.binding();
let occurrence = binding.occurrence();
ExactIdentityEvidenceV1 {
physical: ExactAlphaProgramV1::physical_identity(),
authored: self.authored,
constraint: ExactAlphaProgramV1::constraint_identity(),
capability: ExactIdentityCapabilityV1::FinalOccurrenceSrgb8IdentityV1,
release: ExactIdentityReleaseV1::V1,
program_occurrence: self.occurrence.program_occurrence_binding(),
occurrence: *self.occurrence.certificate(),
target: self.target,
actual: Srgb8::new(self.occurrence.visible()),
constraint: *self.assessment.identity(),
capability: *self.assessment.capability(),
release: *self.assessment.release(),
program_occurrence: binding.program_occurrence(),
occurrence,
target: *self.assessment.invocation(),
actual: Srgb8::new(occurrence.output_rgb()),
}
}
}
Expand Down Expand Up @@ -197,19 +198,21 @@ pub(crate) fn tint_at_alpha(target: Srgb8, alpha: f64, backdrop: Srgb8) -> Optio
(actual == target).then_some(Srgb8::new(tint))
}

fn propose(target: Srgb8, requested_alpha: f64, backdrop: Srgb8) -> Result<(Srgb8, f64), String> {
if !requested_alpha.is_finite() || !(0.0..=1.0).contains(&requested_alpha) {
return Err(format!(
"requested_alpha вне конечного [0,1]: {requested_alpha}"
));
}
if let Some(tint) = tint_at_alpha(target, requested_alpha, backdrop) {
return Ok((tint, requested_alpha));
fn propose(
target: Srgb8,
requested_alpha: f64,
backdrop: Srgb8,
) -> Result<(Srgb8, AdmittedOpacityV1), String> {
let requested = AdmittedOpacityV1::new(requested_alpha)
.map_err(|_| format!("requested_alpha вне конечного [0,1]: {requested_alpha}"))?;
if let Some(tint) = tint_at_alpha(target, requested.value(), backdrop) {
return Ok((tint, requested));
}

let alpha = first_alpha(target, backdrop);
debug_assert!(alpha > requested_alpha);
let tint = tint_at_alpha(target, alpha, backdrop)
let alpha = AdmittedOpacityV1::new(first_alpha(target, backdrop))
.map_err(|_| "выведенная alpha вышла из конечного [0,1]".to_owned())?;
debug_assert!(alpha.value() > requested.value());
let tint = tint_at_alpha(target, alpha.value(), backdrop)
.ok_or_else(|| "первая sRGB8-alpha не дала допустимый byte-тинт".to_owned())?;
Ok((tint, alpha))
}
Expand All @@ -228,14 +231,12 @@ pub(crate) fn resolve_verified(

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum ExactAlphaProgramErrorV1 {
InvalidOpacity(String),
IdentityMismatch(ExactIdentityMismatchV1),
}

impl ExactAlphaProgramErrorV1 {
pub(crate) fn message(&self) -> String {
match self {
Self::InvalidOpacity(message) => message.clone(),
Self::IdentityMismatch(mismatch) => format!(
"alpha-analog не воспроизвёл sRGB8-цель: target={:?}, actual={:?}",
mismatch.target().bytes(),
Expand All @@ -254,30 +255,31 @@ impl ExactAlphaProgramV1 {
PointOpacityOverSurfaceV1::physical_identity()
}

pub(crate) const fn constraint_identity() -> ExactConstraintIdentityV1 {
ExactSrgb8IdentityV1::IDENTITY
}

pub(crate) fn evaluate(
authored: AuthoredAlphaBindingIdV1,
target: Srgb8,
tint: Srgb8,
alpha: f64,
alpha: AdmittedOpacityV1,
backdrop: Srgb8,
) -> Result<VerifiedAlphaAnalogV1, ExactAlphaProgramErrorV1> {
let occurrence = PointOpacityOverSurfaceV1::evaluate(tint.bytes(), alpha, backdrop.bytes())
.map_err(|error| {
ExactAlphaProgramErrorV1::InvalidOpacity(error.message().to_owned())
})?;
let assessment = ExactSrgb8IdentityV1::evaluate(&occurrence, target)
.map_err(ExactAlphaProgramErrorV1::IdentityMismatch)?;
debug_assert_eq!(assessment.target(), assessment.actual());
let occurrence =
PointOpacityOverSurfaceV1::evaluate_admitted(tint.bytes(), alpha, backdrop.bytes());
let assessment = match assess(&occurrence, &ExactSrgb8IdentityV1, target) {
BoundVerdict::Pass(assessment) => assessment,
BoundVerdict::Fail(failure) => {
return Err(ExactAlphaProgramErrorV1::IdentityMismatch(
failure.into_outcome(),
));
}
};
let verified = VerifiedAlphaAnalogV1 {
occurrence,
authored,
target: assessment.target(),
assessment,
};
debug_assert_eq!(verified.evidence().actual, assessment.actual());
debug_assert_eq!(
verified.evidence().actual,
Srgb8::new(verified.certificate().output_rgb())
);
Ok(verified)
}
}
Expand All @@ -286,6 +288,10 @@ impl ExactAlphaProgramV1 {
mod tests {
use super::*;

fn admitted(alpha: f64) -> AdmittedOpacityV1 {
AdmittedOpacityV1::new(alpha).expect("test alpha must be admitted")
}

#[test]
fn public_alpha_facade_owns_no_byte_grid_proposal_or_verified_coordinator() {
let facade = include_str!("alpha.rs");
Expand Down Expand Up @@ -382,17 +388,15 @@ mod tests {
AuthoredAlphaBindingIdV1::Standalone,
target,
Srgb8::new([255, 255, 255]),
0.5,
admitted(0.5),
Srgb8::new([0, 0, 0]),
)
.expect_err("wrong final bytes must not mint VerifiedAlphaAnalogV1");
let message = error.message();
assert!(message.contains("target=[0, 0, 0]"), "{message}");
assert!(message.contains("actual=[128, 128, 128]"), "{message}");

let ExactAlphaProgramErrorV1::IdentityMismatch(mismatch) = error else {
panic!("unexpected exact-program error: {error:?}");
};
let ExactAlphaProgramErrorV1::IdentityMismatch(mismatch) = error;
assert_eq!(mismatch.target(), target);
assert_eq!(mismatch.actual(), Srgb8::new([128, 128, 128]));
assert_eq!(crate::composition::source_over_evaluation_count(), 1);
Expand All @@ -406,7 +410,7 @@ mod tests {
},
Srgb8::new([128, 128, 128]),
Srgb8::new([0, 0, 0]),
0.5,
admitted(0.5),
Srgb8::new([255, 255, 255]),
)
.unwrap();
Expand All @@ -423,7 +427,7 @@ mod tests {
assert_eq!(evidence.target, evidence.actual);
assert_eq!(
evidence.program_occurrence,
verified.occurrence().program_occurrence_binding()
verified.assessment.binding().program_occurrence()
);
assert_eq!(evidence.occurrence.output_rgb(), evidence.actual.bytes());
assert_eq!(
Expand All @@ -443,7 +447,7 @@ mod tests {
},
Srgb8::new([128, 128, 128]),
Srgb8::new([0, 0, 0]),
0.5,
admitted(0.5),
Srgb8::new([255, 255, 255]),
)
.unwrap()
Expand Down Expand Up @@ -474,7 +478,7 @@ mod tests {
AuthoredAlphaBindingIdV1::Standalone,
target,
tint,
0.5,
admitted(0.5),
backdrop,
)
});
Expand Down
Loading
Loading