From 0f6ddab8636226b3029e3164177e0fe77d02f2bf Mon Sep 17 00:00:00 2001 From: Daniel from Labpics <63733699+lemone112@users.noreply.github.com> Date: Fri, 24 Jul 2026 22:42:00 +0300 Subject: [PATCH 1/2] core: project complete program certificate evidence --- crates/labcolors-core/src/appearance.rs | 9 +- crates/labcolors-core/src/constraints/mod.rs | 1 - .../labcolors-core/src/constraints/wcag22.rs | 2 +- crates/labcolors-core/src/lcs_occurrence.rs | 23 +- crates/labcolors-core/src/observation.rs | 8 + crates/labcolors-core/src/package_bridge.rs | 815 ++++++++++-- .../src/program_mixed_evaluator_tests.rs | 1148 ++++++++++++++++- crates/labcolors-core/src/program_session.rs | 11 + ...kage_bridge_red.rs => program_boundary.rs} | 233 +++- 9 files changed, 2048 insertions(+), 202 deletions(-) rename crates/labcolors-core/tests/{package_bridge_red.rs => program_boundary.rs} (69%) diff --git a/crates/labcolors-core/src/appearance.rs b/crates/labcolors-core/src/appearance.rs index 8c635e94..321a3709 100644 --- a/crates/labcolors-core/src/appearance.rs +++ b/crates/labcolors-core/src/appearance.rs @@ -134,17 +134,14 @@ pub(crate) struct ProgramOccurrenceBindingV1 { } impl ProgramOccurrenceBindingV1 { - #[cfg(test)] pub(crate) const fn occurrence(self) -> OccurrenceId { self.occurrence } - #[cfg(test)] pub(crate) const fn subject(self) -> PaintId { self.subject } - #[cfg(test)] pub(crate) const fn backdrop_surface(self) -> SurfaceId { self.backdrop_surface } @@ -1217,7 +1214,6 @@ impl EncodedPointPaintV1 { self.opacity } - #[cfg(test)] pub(crate) const fn opacity_bits(self) -> u64 { self.opacity.bits() } @@ -1242,7 +1238,6 @@ impl SourceOverCertificateV1 { .composite(self.subject_rgb, self.subject_opacity, self.backdrop_rgb) } - #[cfg(test)] pub(crate) const fn profile(&self) -> CompositionProfileV1 { self.profile } @@ -1357,11 +1352,11 @@ pub(crate) struct VisiblePointBindingV1 { } impl VisiblePointBindingV1 { - pub(crate) fn program_occurrence(self) -> ProgramOccurrenceBindingV1 { + pub(crate) const fn program_occurrence(self) -> ProgramOccurrenceBindingV1 { self.program_occurrence } - pub(crate) fn occurrence(self) -> SourceOverCertificateV1 { + pub(crate) const fn occurrence(self) -> SourceOverCertificateV1 { self.occurrence } diff --git a/crates/labcolors-core/src/constraints/mod.rs b/crates/labcolors-core/src/constraints/mod.rs index 4aae4880..fb887337 100644 --- a/crates/labcolors-core/src/constraints/mod.rs +++ b/crates/labcolors-core/src/constraints/mod.rs @@ -155,7 +155,6 @@ impl &self.invocation } - #[cfg(test)] pub(crate) fn measurement(&self) -> &Measurement { &self.measurement } diff --git a/crates/labcolors-core/src/constraints/wcag22.rs b/crates/labcolors-core/src/constraints/wcag22.rs index c4b28a87..cc9f3a78 100644 --- a/crates/labcolors-core/src/constraints/wcag22.rs +++ b/crates/labcolors-core/src/constraints/wcag22.rs @@ -30,7 +30,6 @@ pub(crate) struct ApplicableWcag22MeasurementV1 { evidence: NumericalDecisionEvidenceV1, } -#[cfg(test)] impl ApplicableWcag22MeasurementV1 { pub(crate) const fn profile_id(&self) -> Wcag22ProfileIdV1 { self.profile_id @@ -44,6 +43,7 @@ impl ApplicableWcag22MeasurementV1 { &self.measurement } + #[cfg(test)] pub(crate) const fn decision(&self) -> Wcag22ApplicableDecisionV1 { self.decision } diff --git a/crates/labcolors-core/src/lcs_occurrence.rs b/crates/labcolors-core/src/lcs_occurrence.rs index ea6a9295..fb7f08c5 100644 --- a/crates/labcolors-core/src/lcs_occurrence.rs +++ b/crates/labcolors-core/src/lcs_occurrence.rs @@ -28,6 +28,13 @@ pub struct ColorSignal { output_profile: OutputProfileId, } +/// Exhaustive internal decomposition for boundaries that must preserve the +/// signal profile instead of treating encoded bytes as self-describing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ColorSignalViewV1 { + Iec61966Srgb8D65(Srgb8), +} + impl ColorSignal { /// Form the only admitted encoded signal without accepting a free-form /// channel/profile pairing. @@ -45,6 +52,12 @@ impl ColorSignal { pub(crate) const fn output_profile(self) -> OutputProfileId { self.output_profile } + + pub(crate) const fn view(self) -> ColorSignalViewV1 { + match self.output_profile { + OutputProfileId::Iec61966Srgb8D65V1 => ColorSignalViewV1::Iec61966Srgb8D65(self.srgb8), + } + } } /// Exact code release for one colorimetric signal-to-tristimulus transform. @@ -321,6 +334,8 @@ fn derive_sample_with_binding( signal: ColorSignal, binding: AdmittedSrgb8TristimulusBindingV1, ) -> Result { + #[cfg(test)] + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(|calls| calls.set(calls.get() + 1)); let xyz = match ( signal.output_profile(), binding.signal_output_profile(), @@ -337,9 +352,9 @@ fn derive_sample_with_binding( #[cfg(test)] thread_local! { - /// Per-thread count of modeled signal-to-tristimulus derivations. Program - /// regression tests use this deterministic metric to pin one derivation - /// per unique target occurrence and physical case without timing noise. + /// Per-thread count of modeled signal-to-tristimulus kernel executions. + /// Counting below both initial derivation and replay keeps a projection + /// from hiding recomputation behind the replay API. pub(crate) static MODELED_TRISTIMULUS_DERIVATION_CALLS: std::cell::Cell = const { std::cell::Cell::new(0) }; } @@ -352,8 +367,6 @@ thread_local! { pub(crate) fn derive_modeled_tristimulus_v1( signal: ColorSignal, ) -> Result { - #[cfg(test)] - MODELED_TRISTIMULUS_DERIVATION_CALLS.with(|calls| calls.set(calls.get() + 1)); let binding = admitted_binding(signal.output_profile()); let sample = derive_sample_with_binding(signal, binding)?; Ok(ModeledTristimulusDerivationV1 { diff --git a/crates/labcolors-core/src/observation.rs b/crates/labcolors-core/src/observation.rs index 8881c4c8..d2549f3c 100644 --- a/crates/labcolors-core/src/observation.rs +++ b/crates/labcolors-core/src/observation.rs @@ -30,6 +30,10 @@ impl ObservationStreamId { pub(crate) const fn new(raw: u32) -> Self { Self(raw) } + + pub(crate) const fn value(self) -> u32 { + self.0 + } } /// Monotonic revision inside one [`ObservationStreamId`]. @@ -54,6 +58,10 @@ impl ScenarioId { pub(crate) const fn new(raw: u32) -> Self { Self(raw) } + + pub(crate) const fn value(self) -> u32 { + self.0 + } } /// Opaque reason why the current observation is unavailable. diff --git a/crates/labcolors-core/src/package_bridge.rs b/crates/labcolors-core/src/package_bridge.rs index 16666bba..7e594b19 100644 --- a/crates/labcolors-core/src/package_bridge.rs +++ b/crates/labcolors-core/src/package_bridge.rs @@ -12,12 +12,19 @@ use core::slice; use crate::Srgb8; use crate::appearance::{OccurrenceId, OpacityInputId, PaintId, SurfaceId, SurfaceInputPortId}; +use crate::composition::CompositionProfileV1; +use crate::constraints::{ + ExactSrgb8IdentityV1, ProgramVisiblePointBindingV1, ProgramVisiblePointPassEvidence, + ProgramVisiblePointViolationEvidence, Wcag22Srgb8V1, +}; use crate::joint::FiniteJointOrderErrorV1; use crate::lcs_occurrence::{ - AdaptingLuminanceCdM2, AppearanceContextDomainErrorV1, AppearanceContextFieldV1, - AppearanceContextId, AppearanceContextSchemaReleaseId, BackgroundLuminanceRatio, ColorSignal, - IEC_SRGB_D65_XYZ_FRAME_V1, NumericDomainError, SurroundProfileId, + AdaptingLuminanceCdM2, AdmittedSrgb8TristimulusBindingV1, AppearanceContextDomainErrorV1, + AppearanceContextFieldV1, AppearanceContextId, AppearanceContextSchemaReleaseId, + BackgroundLuminanceRatio, ColorSignal, ColorSignalViewV1, IEC_SRGB_D65_XYZ_FRAME_V1, + NumericDomainError, SurroundProfileId, }; +use crate::numerics::NumericalDecisionEvidenceV1; use crate::observation::{ ObservationError, ObservationPayloadInput, ObservationStreamId, ObservationUpdateInput, Revision, ScenarioId, SchemaOrderedScenarioSourceV1, UnknownReasonId, @@ -25,14 +32,16 @@ use crate::observation::{ use crate::program_session::{ CompiledCoreProgramV1, CompositionProfile, ConstraintId, ConstraintInvocation, CoreProgramConstraintInvocationV1, CoreProgramDraftErrorV1, CoreProgramDraftV1, - CoreProgramEvaluatorErrorV1, CoreProgramEvaluatorsV1, DeclaredJointSelectionV1, - JointCandidateStateV1, Occurrence, OpacityInput, OutputBinding, OutputSlotId, Paint, - ProgramCompileError, ProgramConflictV1, ProgramOutputV1, ProgramSessionEvaluationError, - ProgramSessionInstantiateError, ProgramSessionPlan, ProgramVerifiedV1, Source, SourceId, - Surface, Target, TargetCandidateChoiceV1, TargetCandidateId, TargetCandidateV1, TargetId, + CoreProgramEvaluatorErrorV1, CoreProgramEvaluatorsV1, CoreProgramPassEvidenceV1, + CoreProgramViolationEvidenceV1, DeclaredJointSelectionV1, JointCandidateStateV1, Occurrence, + OpacityInput, OutputBinding, OutputSlotId, Paint, ProgramCompileError, ProgramConflictV1, + ProgramConstraintCellV1, ProgramConstraintResultV1, ProgramContentIdentityV1, ProgramOutputV1, + ProgramSessionEvaluationError, ProgramSessionInstantiateError, ProgramSessionPlan, + ProgramVerifiedV1, Source, SourceId, Surface, Target, TargetCandidateChoiceV1, + TargetCandidateId, TargetCandidateV1, TargetId, }; use crate::session::{Session, SessionState, SessionUpdateError}; -use crate::wcag22::Wcag22CriterionV1; +use crate::wcag22::{Wcag22CriterionV1, Wcag22LuminanceBoundsQ55V1, Wcag22ProfileIdV1}; type CoreVerifiedV1 = ProgramVerifiedV1; type CoreConflictV1 = ProgramConflictV1; @@ -40,6 +49,11 @@ type CoreProgramPlanV1 = ProgramSessionPlan; type CoreProgramSessionV1 = Session; type CoreProgramStateV1 = SessionState; type CoreProgramPlanErrorV1 = ProgramSessionEvaluationError; +type CoreProgramConstraintCellV1 = ProgramConstraintCellV1; +type CoreExactPassEvidenceV1 = ProgramVisiblePointPassEvidence; +type CoreExactViolationEvidenceV1 = ProgramVisiblePointViolationEvidence; +type CoreWcag22PassEvidenceV1 = ProgramVisiblePointPassEvidence; +type CoreWcag22ViolationEvidenceV1 = ProgramVisiblePointViolationEvidence; macro_rules! package_program_id { ($name:ident, $core:ty) => { @@ -74,6 +88,31 @@ macro_rules! package_program_id { }; } +macro_rules! package_program_projected_id { + ($name:ident, $core:ty) => { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[must_use] + pub struct $name($core); + + impl $name { + const fn from_core(value: $core) -> Self { + Self(value) + } + + pub const fn value(self) -> u32 { + self.0.value() + } + } + + impl core::hash::Hash for $name { + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&self.value(), state); + } + } + }; +} + package_program_id!(PackageProgramSourceIdV1, SourceId); package_program_id!(PackageProgramTargetIdV1, TargetId); package_program_id!(PackageProgramTargetCandidateIdV1, TargetCandidateId); @@ -84,6 +123,8 @@ package_program_id!(PackageProgramSurfaceIdV1, SurfaceId); package_program_id!(PackageProgramOccurrenceIdV1, OccurrenceId); package_program_id!(PackageProgramConstraintIdV1, ConstraintId); package_program_id!(PackageProgramOutputSlotIdV1, OutputSlotId); +package_program_projected_id!(PackageProgramStreamIdV1, ObservationStreamId); +package_program_projected_id!(PackageProgramScenarioIdV1, ScenarioId); /// One finite candidate, stored as the actual Core target-candidate IR node. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -244,6 +285,22 @@ impl PackageProgramAppearanceContextV1 { surround.into_core(), ))) } + + pub fn adapting_luminance_cd_m2(self) -> f64 { + self.0.adapting_luminance_cd_m2() + } + + pub fn background_luminance_ratio_yb_yw(self) -> f64 { + self.0.background_luminance_ratio() + } + + pub const fn surround(self) -> PackageProgramSurroundV1 { + match self.0.surround_profile() { + SurroundProfileId::AverageV1 => PackageProgramSurroundV1::Average, + SurroundProfileId::DimV1 => PackageProgramSurroundV1::Dim, + SurroundProfileId::DarkV1 => PackageProgramSurroundV1::Dark, + } + } } /// Closed compile classification; the generic Core error never escapes. @@ -1168,7 +1225,7 @@ impl<'a> PackageProgramStateViewV1<'a> { /// Core-owned certificates in canonical same-call ordinal order. pub fn certificates( self, - ) -> impl ExactSizeIterator> + 'a { + ) -> impl ExactSizeIterator> + FusedIterator + 'a { let (first, second) = match self.state { SessionState::Waiting => (None, None), SessionState::Ready { current } | SessionState::Stale { previous: current } => { @@ -1183,7 +1240,9 @@ impl<'a> PackageProgramStateViewV1<'a> { } /// Total canonical output projection for this lifecycle state. - pub fn operations(self) -> impl ExactSizeIterator + 'a { + pub fn operations( + self, + ) -> impl ExactSizeIterator> + FusedIterator + 'a { let inner = match self.state { SessionState::Waiting => PackageProgramOperationSourceV1::Empty, SessionState::Ready { current } => { @@ -1195,17 +1254,21 @@ impl<'a> PackageProgramStateViewV1<'a> { .zip(self.output_slots) .all(|(output, slot)| output.output().value() == slot.value()) ); - PackageProgramOperationSourceV1::Set(current.outputs().iter()) + PackageProgramOperationSourceV1::Set { + outputs: current.outputs().iter(), + certificate: PackageProgramVerifiedCertificateV1 { inner: current }, + } } - SessionState::Stale { .. } => PackageProgramOperationSourceV1::Hold { + SessionState::Stale { previous } => PackageProgramOperationSourceV1::Hold { slots: self.output_slots.iter(), - certificate_index: 0, + certificate: PackageProgramVerifiedCertificateV1 { inner: previous }, }, SessionState::Failed { - previous: Some(_), .. + previous: Some(previous), + .. } => PackageProgramOperationSourceV1::Hold { slots: self.output_slots.iter(), - certificate_index: 1, + certificate: PackageProgramVerifiedCertificateV1 { inner: previous }, }, SessionState::Failed { previous: None, .. } => { PackageProgramOperationSourceV1::Remove(self.output_slots.iter()) @@ -1215,91 +1278,661 @@ impl<'a> PackageProgramStateViewV1<'a> { } } -/// Opaque certificate family; evaluator-specific evidence never escapes. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PackageProgramCertificateKindV1 { - Verified, - Conflict, +/// Collision-resistant address of the canonical physical Program content. +/// It deliberately does not identify an owner epoch or runtime authority. +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PackageProgramContentIdentityV1([u8; 32]); + +impl PackageProgramContentIdentityV1 { + const fn from_core(value: ProgramContentIdentityV1) -> Self { + Self(*value.as_bytes()) + } + + pub const fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } } +/// All hard cells passed over the complete admitted physical support. #[derive(Clone, Copy)] -enum PackageProgramCertificateRefV1<'a> { - Verified(&'a CoreVerifiedV1), - Conflict(&'a CoreConflictV1), +pub struct PackageProgramVerifiedCertificateV1<'a> { + inner: &'a CoreVerifiedV1, } -/// Borrowed opaque handle to one Core-owned certificate. +impl<'a> PackageProgramVerifiedCertificateV1<'a> { + pub const fn content_identity(self) -> PackageProgramContentIdentityV1 { + PackageProgramContentIdentityV1::from_core(self.inner.report().content_identity()) + } + + pub const fn observation(self) -> PackageProgramObservationV1<'a> { + PackageProgramObservationV1 { + inner: self.inner.report().observation(), + } + } + + pub const fn selected_state_index(self) -> Option { + self.inner.selected_state_index() + } + + pub fn cells( + self, + ) -> impl ExactSizeIterator> + FusedIterator + 'a { + self.inner + .report() + .cells() + .iter() + .map(PackageProgramVerifiedCellV1::from_core) + } + + pub fn outputs( + self, + ) -> impl ExactSizeIterator> + FusedIterator + 'a + { + self.inner + .outputs() + .iter() + .map(PackageProgramCertifiedOutputV1::from_core) + } +} + +/// Exhaustive proof that every declared candidate state violates a hard cell. #[derive(Clone, Copy)] -pub struct PackageProgramCertificateV1<'a> { - inner: PackageProgramCertificateRefV1<'a>, +pub struct PackageProgramConflictCertificateV1<'a> { + inner: &'a CoreConflictV1, +} + +impl<'a> PackageProgramConflictCertificateV1<'a> { + pub const fn content_identity(self) -> PackageProgramContentIdentityV1 { + PackageProgramContentIdentityV1::from_core(self.inner.report().content_identity()) + } + + pub const fn observation(self) -> PackageProgramObservationV1<'a> { + PackageProgramObservationV1 { + inner: self.inner.report().observation(), + } + } + + pub const fn considered_state_count(self) -> usize { + self.inner.considered_state_count() + } + + pub fn cells( + self, + ) -> impl ExactSizeIterator> + FusedIterator + 'a { + self.inner + .report() + .cells() + .iter() + .map(PackageProgramConflictCellV1::from_core) + } +} + +/// Closed borrowed projection of one exact Core-owned certificate. +#[derive(Clone, Copy)] +pub enum PackageProgramCertificateV1<'a> { + Verified(PackageProgramVerifiedCertificateV1<'a>), + Conflict(PackageProgramConflictCertificateV1<'a>), } impl<'a> PackageProgramCertificateV1<'a> { const fn verified(value: &'a CoreVerifiedV1) -> Self { - Self { - inner: PackageProgramCertificateRefV1::Verified(value), - } + Self::Verified(PackageProgramVerifiedCertificateV1 { inner: value }) } const fn conflict(value: &'a CoreConflictV1) -> Self { - Self { - inner: PackageProgramCertificateRefV1::Conflict(value), + Self::Conflict(PackageProgramConflictCertificateV1 { inner: value }) + } + + pub const fn content_identity(self) -> PackageProgramContentIdentityV1 { + match self { + Self::Verified(value) => value.content_identity(), + Self::Conflict(value) => value.content_identity(), + } + } + + pub const fn observation(self) -> PackageProgramObservationV1<'a> { + match self { + Self::Verified(value) => value.observation(), + Self::Conflict(value) => value.observation(), + } + } + + #[cfg(test)] + pub(crate) fn observation_backing_ptr_for_test(self) -> *const () { + self.observation().inner.backing_ptr_for_test() + } +} + +/// The exact revision-bound observation retained by a certificate. +#[derive(Clone, Copy)] +pub struct PackageProgramObservationV1<'a> { + inner: &'a crate::observation::RevisionBoundObservationV1, +} + +impl<'a> PackageProgramObservationV1<'a> { + pub const fn stream(self) -> PackageProgramStreamIdV1 { + PackageProgramStreamIdV1::from_core(self.inner.stream()) + } + + pub const fn revision(self) -> u64 { + self.inner.revision().value() + } + + /// Canonical schema shared by every physical case. Position `i` is the + /// identity of position `i` in each [`PackageProgramPhysicalCaseV1::values`] + /// iterator; the two exact-size iterators always have equal length. + pub fn surface_input_ports( + self, + ) -> impl ExactSizeIterator + FusedIterator + 'a + { + self.inner + .schema() + .iter() + .copied() + .map(PackageProgramSurfaceInputPortIdV1::from_core) + } + + /// Canonical unique physical value vectors. Each case is schema-ordered by + /// [`Self::surface_input_ports`]; scenario IDs remain in `provenance()`. + pub fn physical_cases( + self, + ) -> impl ExactSizeIterator> + FusedIterator + 'a { + (0..self.inner.physical_case_count()).map(move |index| PackageProgramPhysicalCaseV1 { + observation: self.inner, + index, + }) + } +} + +/// Closed encoded signal family retained in a physical observation case. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PackageProgramSignalV1 { + Iec61966Srgb8D65(Srgb8), +} + +/// One canonical physical observation case and its complete provenance set. +#[derive(Clone, Copy)] +pub struct PackageProgramPhysicalCaseV1<'a> { + observation: &'a crate::observation::RevisionBoundObservationV1, + index: usize, +} + +impl<'a> PackageProgramPhysicalCaseV1<'a> { + pub fn values( + self, + ) -> impl ExactSizeIterator + FusedIterator + 'a { + self.observation + .physical_values(self.index) + .expect("package case originates from the same observation") + .iter() + .copied() + .map(|signal| match signal.view() { + ColorSignalViewV1::Iec61966Srgb8D65(value) => { + PackageProgramSignalV1::Iec61966Srgb8D65(value) + } + }) + } + + pub fn provenance( + self, + ) -> impl ExactSizeIterator + FusedIterator + 'a { + self.observation + .provenance(self.index) + .expect("package case originates from the same observation") + .iter() + .copied() + .map(PackageProgramScenarioIdV1::from_core) + } +} + +/// Whether one constraint cell gates selection or is retained for reporting. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PackageProgramConstraintModeV1 { + Hard, + ReportOnly, +} + +/// One selected/fixed case × constraint cell; state is owned by its certificate. +#[derive(Clone, Copy)] +pub struct PackageProgramVerifiedCellV1<'a> { + inner: &'a CoreProgramConstraintCellV1, +} + +impl<'a> PackageProgramVerifiedCellV1<'a> { + const fn from_core(inner: &'a CoreProgramConstraintCellV1) -> Self { + Self { inner } + } + + pub const fn case_index(self) -> usize { + self.inner.case_index() + } + + pub const fn constraint(self) -> PackageProgramConstraintIdV1 { + PackageProgramConstraintIdV1::from_core(self.inner.constraint()) + } + + pub const fn occurrence(self) -> PackageProgramOccurrenceIdV1 { + PackageProgramOccurrenceIdV1::from_core(self.inner.target()) + } + + pub const fn mode(self) -> PackageProgramConstraintModeV1 { + project_constraint_mode(self.inner) + } + + pub fn assessment(self) -> PackageProgramAssessmentV1<'a> { + project_assessment(self.inner) + } +} + +/// One exhaustive candidate-state × case × constraint conflict cell. +#[derive(Clone, Copy)] +pub struct PackageProgramConflictCellV1<'a> { + inner: &'a CoreProgramConstraintCellV1, +} + +impl<'a> PackageProgramConflictCellV1<'a> { + const fn from_core(inner: &'a CoreProgramConstraintCellV1) -> Self { + Self { inner } + } + + pub const fn state_index(self) -> usize { + self.inner.candidate_state_index() + } + + pub const fn case_index(self) -> usize { + self.inner.case_index() + } + + pub const fn constraint(self) -> PackageProgramConstraintIdV1 { + PackageProgramConstraintIdV1::from_core(self.inner.constraint()) + } + + pub const fn occurrence(self) -> PackageProgramOccurrenceIdV1 { + PackageProgramOccurrenceIdV1::from_core(self.inner.target()) + } + + pub const fn mode(self) -> PackageProgramConstraintModeV1 { + project_constraint_mode(self.inner) + } + + pub fn assessment(self) -> PackageProgramAssessmentV1<'a> { + project_assessment(self.inner) + } +} + +const fn project_constraint_mode( + cell: &CoreProgramConstraintCellV1, +) -> PackageProgramConstraintModeV1 { + if cell.is_hard() { + PackageProgramConstraintModeV1::Hard + } else { + PackageProgramConstraintModeV1::ReportOnly + } +} + +fn project_assessment(cell: &CoreProgramConstraintCellV1) -> PackageProgramAssessmentV1<'_> { + match cell.result() { + ProgramConstraintResultV1::Pass(CoreProgramPassEvidenceV1::ExactSrgb8(evidence)) => { + PackageProgramAssessmentV1::ExactSrgb8(PackageProgramExactSrgb8EvidenceV1 { + inner: PackageProgramExactSrgb8EvidenceRefV1::Pass(evidence), + }) + } + ProgramConstraintResultV1::Violation(CoreProgramViolationEvidenceV1::ExactSrgb8( + evidence, + )) => PackageProgramAssessmentV1::ExactSrgb8(PackageProgramExactSrgb8EvidenceV1 { + inner: PackageProgramExactSrgb8EvidenceRefV1::Violation(evidence), + }), + ProgramConstraintResultV1::Pass(CoreProgramPassEvidenceV1::Wcag22Srgb8(evidence)) => { + PackageProgramAssessmentV1::Wcag22Srgb8(PackageProgramWcag22Srgb8EvidenceV1 { + inner: PackageProgramWcag22Srgb8EvidenceRefV1::Pass(evidence), + }) + } + ProgramConstraintResultV1::Violation(CoreProgramViolationEvidenceV1::Wcag22Srgb8( + evidence, + )) => PackageProgramAssessmentV1::Wcag22Srgb8(PackageProgramWcag22Srgb8EvidenceV1 { + inner: PackageProgramWcag22Srgb8EvidenceRefV1::Violation(evidence), + }), + } +} + +/// Stored evaluator family. Its sealed witness retains the incompatible verdict. +#[derive(Clone, Copy)] +pub enum PackageProgramAssessmentV1<'a> { + ExactSrgb8(PackageProgramExactSrgb8EvidenceV1<'a>), + Wcag22Srgb8(PackageProgramWcag22Srgb8EvidenceV1<'a>), +} + +impl<'a> PackageProgramAssessmentV1<'a> { + pub const fn verdict(self) -> PackageProgramVerdictV1 { + match self { + Self::ExactSrgb8(value) => value.verdict(), + Self::Wcag22Srgb8(value) => value.verdict(), + } + } + + pub fn binding(self) -> PackageProgramPointBindingV1<'a> { + match self { + Self::ExactSrgb8(value) => value.binding(), + Self::Wcag22Srgb8(value) => value.binding(), } } +} + +/// Incompatible stored classifier outcomes. Clients cannot construct evidence. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PackageProgramVerdictV1 { + Pass, + Violation, +} - pub const fn kind(self) -> PackageProgramCertificateKindV1 { +#[derive(Clone, Copy)] +enum PackageProgramExactSrgb8EvidenceRefV1<'a> { + Pass(&'a CoreExactPassEvidenceV1), + Violation(&'a CoreExactViolationEvidenceV1), +} + +/// Exact-sRGB8 expected value plus retained physical composition and modeled +/// tristimulus/context. +#[derive(Clone, Copy)] +pub struct PackageProgramExactSrgb8EvidenceV1<'a> { + inner: PackageProgramExactSrgb8EvidenceRefV1<'a>, +} + +impl<'a> PackageProgramExactSrgb8EvidenceV1<'a> { + pub const fn verdict(self) -> PackageProgramVerdictV1 { match self.inner { - PackageProgramCertificateRefV1::Verified(_) => { - PackageProgramCertificateKindV1::Verified + PackageProgramExactSrgb8EvidenceRefV1::Pass(_) => PackageProgramVerdictV1::Pass, + PackageProgramExactSrgb8EvidenceRefV1::Violation(_) => { + PackageProgramVerdictV1::Violation } - PackageProgramCertificateRefV1::Conflict(_) => { - PackageProgramCertificateKindV1::Conflict + } + } + + pub fn expected(self) -> Srgb8 { + match self.inner { + PackageProgramExactSrgb8EvidenceRefV1::Pass(value) => value.target(), + PackageProgramExactSrgb8EvidenceRefV1::Violation(value) => value.target(), + } + } + + pub fn binding(self) -> PackageProgramPointBindingV1<'a> { + let value = match self.inner { + PackageProgramExactSrgb8EvidenceRefV1::Pass(value) => value.binding(), + PackageProgramExactSrgb8EvidenceRefV1::Violation(value) => value.binding(), + }; + PackageProgramPointBindingV1 { inner: value } + } +} + +#[derive(Clone, Copy)] +enum PackageProgramWcag22Srgb8EvidenceRefV1<'a> { + Pass(&'a CoreWcag22PassEvidenceV1), + Violation(&'a CoreWcag22ViolationEvidenceV1), +} + +/// WCAG 2.2 profile, criterion, luminance evidence, physical composition and +/// modeled tristimulus/context retained by the Core report. +#[derive(Clone, Copy)] +pub struct PackageProgramWcag22Srgb8EvidenceV1<'a> { + inner: PackageProgramWcag22Srgb8EvidenceRefV1<'a>, +} + +impl<'a> PackageProgramWcag22Srgb8EvidenceV1<'a> { + pub const fn verdict(self) -> PackageProgramVerdictV1 { + match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(_) => PackageProgramVerdictV1::Pass, + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(_) => { + PackageProgramVerdictV1::Violation } } } - /// Revision bound into this exact evidence object. - pub const fn revision(self) -> u64 { - let revision = match self.inner { - PackageProgramCertificateRefV1::Verified(value) => { - value.report().observation().revision() + pub fn profile_id(self) -> Wcag22ProfileIdV1 { + match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => { + value.measurement().value().profile_id() } - PackageProgramCertificateRefV1::Conflict(value) => { - value.report().observation().revision() + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => { + value.measurement().value().profile_id() + } + } + } + + pub fn criterion(self) -> Wcag22CriterionV1 { + match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => { + value.measurement().value().criterion() + } + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => { + value.measurement().value().criterion() + } + } + } + + pub fn foreground_luminance(self) -> Wcag22LuminanceBoundsQ55V1 { + let measurement = match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => { + value.measurement().value().measurement() + } + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => { + value.measurement().value().measurement() } }; - revision.value() + measurement.foreground_luminance } - #[cfg(test)] - pub(crate) fn observation_backing_ptr_for_test(self) -> *const () { + pub fn background_luminance(self) -> Wcag22LuminanceBoundsQ55V1 { + let measurement = match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => { + value.measurement().value().measurement() + } + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => { + value.measurement().value().measurement() + } + }; + measurement.background_luminance + } + + pub fn numerical_evidence(self) -> &'a NumericalDecisionEvidenceV1 { match self.inner { - PackageProgramCertificateRefV1::Verified(value) => { - value.report().observation().backing_ptr_for_test() + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => { + value.measurement().value().evidence() + } + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => { + value.measurement().value().evidence() + } + } + } + + pub fn binding(self) -> PackageProgramPointBindingV1<'a> { + let value = match self.inner { + PackageProgramWcag22Srgb8EvidenceRefV1::Pass(value) => value.binding(), + PackageProgramWcag22Srgb8EvidenceRefV1::Violation(value) => value.binding(), + }; + PackageProgramPointBindingV1 { inner: value } + } +} + +/// Retained physical composition and modeled tristimulus/context shared by an +/// evaluator witness. +#[derive(Clone, Copy)] +pub struct PackageProgramPointBindingV1<'a> { + inner: &'a ProgramVisiblePointBindingV1, +} + +impl<'a> PackageProgramPointBindingV1<'a> { + pub const fn physical(self) -> PackageProgramPhysicalPointV1<'a> { + match self.inner.physical().occurrence().profile() { + CompositionProfileV1::EncodedSrgb8SourceOverV1 => { + PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver( + PackageProgramEncodedSrgb8SourceOverV1 { inner: self.inner }, + ) } - PackageProgramCertificateRefV1::Conflict(value) => { - value.report().observation().backing_ptr_for_test() + } + } + + pub const fn modeled(self) -> PackageProgramModeledPointV1<'a> { + match self.inner.modeled_lcs().provenance().binding() { + AdmittedSrgb8TristimulusBindingV1::Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1V1 => { + PackageProgramModeledPointV1::Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1( + PackageProgramModeledTristimulusV1 { inner: self.inner }, + ) } } } } +/// Closed exact physical-composition family. +#[derive(Clone, Copy)] +pub enum PackageProgramPhysicalPointV1<'a> { + EncodedSrgb8SourceOver(PackageProgramEncodedSrgb8SourceOverV1<'a>), +} + +#[derive(Clone, Copy)] +pub struct PackageProgramEncodedSrgb8SourceOverV1<'a> { + inner: &'a ProgramVisiblePointBindingV1, +} + +impl PackageProgramEncodedSrgb8SourceOverV1<'_> { + pub const fn subject_paint(self) -> PackageProgramPaintIdV1 { + PackageProgramPaintIdV1::from_core(self.inner.physical().program_occurrence().subject()) + } + + pub const fn backdrop_surface(self) -> PackageProgramSurfaceIdV1 { + PackageProgramSurfaceIdV1::from_core( + self.inner + .physical() + .program_occurrence() + .backdrop_surface(), + ) + } + + pub const fn subject(self) -> Srgb8 { + Srgb8::new(self.inner.physical().occurrence().subject_rgb()) + } + + pub const fn opacity(self) -> f64 { + f64::from_bits(self.inner.physical().occurrence().subject_opacity_bits()) + } + + pub const fn backdrop(self) -> Srgb8 { + Srgb8::new(self.inner.physical().occurrence().backdrop_rgb()) + } + + pub const fn visible(self) -> Srgb8 { + Srgb8::new(self.inner.physical().occurrence().output_rgb()) + } +} + +/// Closed modeled-tristimulus provenance family. +#[derive(Clone, Copy)] +pub enum PackageProgramModeledPointV1<'a> { + Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1(PackageProgramModeledTristimulusV1<'a>), +} + +#[derive(Clone, Copy)] +pub struct PackageProgramModeledTristimulusV1<'a> { + inner: &'a ProgramVisiblePointBindingV1, +} + +impl PackageProgramModeledTristimulusV1<'_> { + pub fn xyz(self) -> [f64; 3] { + self.inner.modeled_lcs().derivation().sample().xyz() + } + + pub const fn appearance_context(self) -> PackageProgramAppearanceContextV1 { + PackageProgramAppearanceContextV1(self.inner.modeled_lcs().occurrence().context()) + } +} + +/// One Core-certified output Paint. No output exists for Conflict. +#[derive(Clone, Copy)] +pub struct PackageProgramCertifiedOutputV1<'a> { + inner: &'a ProgramOutputV1, +} + +impl<'a> PackageProgramCertifiedOutputV1<'a> { + const fn from_core(inner: &'a ProgramOutputV1) -> Self { + Self { inner } + } + + pub const fn output_slot(self) -> PackageProgramOutputSlotIdV1 { + PackageProgramOutputSlotIdV1::from_core((*self.inner).output()) + } + + pub const fn paint(self) -> PackageProgramPaintIdV1 { + PackageProgramPaintIdV1::from_core((*self.inner).paint().id()) + } + + pub const fn source(self) -> Srgb8 { + (*self.inner).paint().source() + } + + pub const fn opacity(self) -> f64 { + (*self.inner).paint().opacity().value() + } +} + +/// A Set operation is structurally tied to the exact Verified certificate. +#[derive(Clone, Copy)] +pub struct PackageProgramSetV1<'a> { + output: &'a ProgramOutputV1, + certificate: PackageProgramVerifiedCertificateV1<'a>, +} + +impl<'a> PackageProgramSetV1<'a> { + pub const fn output_slot(self) -> PackageProgramOutputSlotIdV1 { + PackageProgramOutputSlotIdV1::from_core((*self.output).output()) + } + + pub const fn source(self) -> Srgb8 { + (*self.output).paint().source() + } + + pub const fn opacity(self) -> f64 { + (*self.output).paint().opacity().value() + } + + pub const fn certificate(self) -> PackageProgramVerifiedCertificateV1<'a> { + self.certificate + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PackageProgramRemoveV1 { + output_slot: PackageProgramOutputSlotIdV1, +} + +impl PackageProgramRemoveV1 { + pub const fn output_slot(self) -> PackageProgramOutputSlotIdV1 { + self.output_slot + } +} + +/// A Hold operation is structurally tied to the retained Verified certificate. +#[derive(Clone, Copy)] +pub struct PackageProgramHoldV1<'a> { + output_slot: PackageProgramOutputSlotIdV1, + certificate: PackageProgramVerifiedCertificateV1<'a>, +} + +impl<'a> PackageProgramHoldV1<'a> { + pub const fn output_slot(self) -> PackageProgramOutputSlotIdV1 { + self.output_slot + } + + pub const fn certificate(self) -> PackageProgramVerifiedCertificateV1<'a> { + self.certificate + } +} + /// Closed total operation union over opaque output slots. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum PackageProgramOperationV1 { - Set { - output_slot: PackageProgramOutputSlotIdV1, - source: Srgb8, - opacity: f64, - certificate_index: usize, - }, - Remove { - output_slot: PackageProgramOutputSlotIdV1, - }, - Hold { - output_slot: PackageProgramOutputSlotIdV1, - certificate_index: usize, - }, +#[derive(Clone, Copy)] +pub enum PackageProgramOperationV1<'a> { + Set(PackageProgramSetV1<'a>), + Remove(PackageProgramRemoveV1), + Hold(PackageProgramHoldV1<'a>), } struct PackageProgramCertificatesV1<'a> { @@ -1346,10 +1979,13 @@ impl FusedIterator for PackageProgramCertificatesV1<'_> {} enum PackageProgramOperationSourceV1<'a> { Empty, - Set(slice::Iter<'a, ProgramOutputV1>), + Set { + outputs: slice::Iter<'a, ProgramOutputV1>, + certificate: PackageProgramVerifiedCertificateV1<'a>, + }, Hold { slots: slice::Iter<'a, PackageProgramOutputSlotIdV1>, - certificate_index: usize, + certificate: PackageProgramVerifiedCertificateV1<'a>, }, Remove(slice::Iter<'a, PackageProgramOutputSlotIdV1>), } @@ -1358,33 +1994,32 @@ struct PackageProgramOperationsV1<'a> { inner: PackageProgramOperationSourceV1<'a>, } -impl Iterator for PackageProgramOperationsV1<'_> { - type Item = PackageProgramOperationV1; +impl<'a> Iterator for PackageProgramOperationsV1<'a> { + type Item = PackageProgramOperationV1<'a>; fn next(&mut self) -> Option { match &mut self.inner { PackageProgramOperationSourceV1::Empty => None, - PackageProgramOperationSourceV1::Set(outputs) => { - let output = *outputs.next()?; - let paint = output.paint(); - Some(PackageProgramOperationV1::Set { - output_slot: PackageProgramOutputSlotIdV1::from_core(output.output()), - source: paint.source(), - opacity: paint.opacity().value(), - certificate_index: 0, - }) - } - PackageProgramOperationSourceV1::Hold { - slots, - certificate_index, - } => Some(PackageProgramOperationV1::Hold { - output_slot: *slots.next()?, - certificate_index: *certificate_index, - }), + PackageProgramOperationSourceV1::Set { + outputs, + certificate, + } => { + let output = outputs.next()?; + Some(PackageProgramOperationV1::Set(PackageProgramSetV1 { + output, + certificate: *certificate, + })) + } + PackageProgramOperationSourceV1::Hold { slots, certificate } => { + Some(PackageProgramOperationV1::Hold(PackageProgramHoldV1 { + output_slot: *slots.next()?, + certificate: *certificate, + })) + } PackageProgramOperationSourceV1::Remove(slots) => { - Some(PackageProgramOperationV1::Remove { + Some(PackageProgramOperationV1::Remove(PackageProgramRemoveV1 { output_slot: *slots.next()?, - }) + })) } } } @@ -1392,7 +2027,7 @@ impl Iterator for PackageProgramOperationsV1<'_> { fn size_hint(&self) -> (usize, Option) { let remaining = match &self.inner { PackageProgramOperationSourceV1::Empty => 0, - PackageProgramOperationSourceV1::Set(outputs) => outputs.len(), + PackageProgramOperationSourceV1::Set { outputs, .. } => outputs.len(), PackageProgramOperationSourceV1::Hold { slots, .. } | PackageProgramOperationSourceV1::Remove(slots) => slots.len(), }; diff --git a/crates/labcolors-core/src/program_mixed_evaluator_tests.rs b/crates/labcolors-core/src/program_mixed_evaluator_tests.rs index 7b30e193..7a3a91a7 100644 --- a/crates/labcolors-core/src/program_mixed_evaluator_tests.rs +++ b/crates/labcolors-core/src/program_mixed_evaluator_tests.rs @@ -1,28 +1,36 @@ +use core::iter::FusedIterator; + use crate::Srgb8; -use crate::appearance::{OccurrenceId, PaintId, SurfaceId, SurfaceInputPortId}; +use crate::appearance::{OccurrenceId, OpacityInputId, PaintId, SurfaceId, SurfaceInputPortId}; use crate::constraints::{ ExactConstraintIdentityV1, ExactIdentityCapabilityV1, ExactIdentityReleaseV1, + ProgramVisiblePointBindingV1, }; use crate::lcs_occurrence::{ AdaptingLuminanceCdM2, AppearanceContextId, AppearanceContextSchemaReleaseId, - BackgroundLuminanceRatio, ColorSignal, IEC_SRGB_D65_XYZ_FRAME_V1, SurroundProfileId, + BackgroundLuminanceRatio, ColorSignal, IEC_SRGB_D65_XYZ_FRAME_V1, + MODELED_TRISTIMULUS_DERIVATION_CALLS, SurroundProfileId, }; use crate::observation::{ ObservationGroupId, ObservationPayloadInput, ObservationStreamId, ObservationUpdateInput, ObservedScenarioSetInput, Revision, ScenarioId, ScenarioInput, SurfaceInputBinding, }; use crate::package_bridge::{ - PackageProgramCertificateKindV1, PackageProgramOperationV1, PackageProgramOutputSlotIdV1, - PackageProgramOwnerV1, PackageProgramScenarioV1, PackageProgramStateKindV1, - PackageProgramUpdateErrorKindV1, PackageProgramUpdateV1, + PackageProgramAssessmentV1, PackageProgramCertificateV1, PackageProgramConflictCellV1, + PackageProgramModeledPointV1, PackageProgramObservationV1, PackageProgramOperationV1, + PackageProgramOutputSlotIdV1, PackageProgramOwnerV1, PackageProgramPhysicalPointV1, + PackageProgramScenarioV1, PackageProgramSignalV1, PackageProgramStateKindV1, + PackageProgramStateViewV1, PackageProgramSurroundV1, PackageProgramUpdateErrorKindV1, + PackageProgramUpdateV1, PackageProgramVerdictV1, PackageProgramVerifiedCellV1, }; use crate::program_session::{ - CompiledCoreProgramV1, CompositionProfile, ConstraintId, ConstraintInvocation, ConstraintSet, - CoreProgramConstraintInvocationV1, CoreProgramEvaluatorsV1, CoreProgramPassEvidenceV1, - CoreProgramV1, CoreProgramViolationEvidenceV1, DeclaredJointSelectionV1, JointCandidateStateV1, - ObservationGroup, Occurrence, OutputBinding, OutputSlotId, Paint, Program, - ProgramConstraintResultV1, Source, SourceId, Surface, Target, TargetCandidateChoiceV1, - TargetCandidateId, TargetCandidateV1, TargetId, + CORE_PROGRAM_ASSESSMENT_CALLS, CompiledCoreProgramV1, CompositionProfile, ConstraintId, + ConstraintInvocation, ConstraintSet, CoreProgramConstraintInvocationV1, + CoreProgramEvaluatorsV1, CoreProgramPassEvidenceV1, CoreProgramV1, + CoreProgramViolationEvidenceV1, DeclaredJointSelectionV1, JointCandidateStateV1, + ObservationGroup, Occurrence, OpacityInput, OutputBinding, OutputSlotId, Paint, Program, + ProgramConstraintCellV1, ProgramConstraintResultV1, Source, SourceId, Surface, Target, + TargetCandidateChoiceV1, TargetCandidateId, TargetCandidateV1, TargetId, }; use crate::session::SessionState; use crate::wcag22::{Wcag22CriterionV1, wcag22_profile_v1}; @@ -137,6 +145,523 @@ fn finite_program(candidate_signals: [[u8; 3]; 2]) -> CompiledCoreProgramV1 { .unwrap() } +fn fixed_translucent_program() -> CompiledCoreProgramV1 { + const OPACITY: OpacityInputId = OpacityInputId::new(12); + const TRANSLUCENT_PAINT: PaintId = PaintId::new(13); + Program::new( + vec![Source::new(SOURCE, signal([0; 3]))], + vec![Target::fixed(TARGET, SOURCE)], + ObservationGroup::new(GROUP, vec![SURFACE_PORT]), + vec![OpacityInput::new(OPACITY, 0.5)], + vec![ + Paint::Solid { + id: PAINT, + target: TARGET, + }, + Paint::Opacity { + id: TRANSLUCENT_PAINT, + source: PAINT, + opacity: OPACITY, + }, + ], + vec![Surface::Input { + id: SURFACE, + input: SURFACE_PORT, + }], + vec![Occurrence::new( + OCCURRENCE, + TRANSLUCENT_PAINT, + SURFACE, + CompositionProfile::EncodedSrgb8SourceOverV1, + context(), + )], + ConstraintSet::new( + vec![ConstraintInvocation::hard( + EXACT_CONSTRAINT, + OCCURRENCE, + CoreProgramConstraintInvocationV1::ExactSrgb8(Srgb8::new([0x80; 3])), + )], + vec![], + ), + vec![OutputBinding::new(OUTPUT, TRANSLUCENT_PAINT)], + CoreProgramEvaluatorsV1, + ) + .compile() + .unwrap() +} + +fn assert_package_observation_matches_core( + package: PackageProgramObservationV1<'_>, + core: &crate::observation::RevisionBoundObservationV1, +) { + assert_eq!(package.stream().value(), core.stream().value()); + assert_eq!(package.revision(), core.revision().value()); + assert_eq!( + package + .surface_input_ports() + .map(|port| port.value()) + .collect::>(), + core.schema() + .iter() + .map(|port| port.value()) + .collect::>(), + ); + + let package_cases = package + .physical_cases() + .map(|case| { + let values = case + .values() + .map(|value| match value { + PackageProgramSignalV1::Iec61966Srgb8D65(value) => value, + }) + .collect::>(); + let provenance = case + .provenance() + .map(|scenario| scenario.value()) + .collect::>(); + (values, provenance) + }) + .collect::>(); + let core_cases = (0..core.physical_case_count()) + .map(|case_index| { + let values = core + .physical_values(case_index) + .unwrap() + .iter() + .map(|signal| signal.srgb8()) + .collect::>(); + let provenance = core + .provenance(case_index) + .unwrap() + .iter() + .map(|scenario| scenario.value()) + .collect::>(); + (values, provenance) + }) + .collect::>(); + assert_eq!(package_cases, core_cases); +} + +fn assert_package_binding_matches_core( + package: PackageProgramAssessmentV1<'_>, + core: &ProgramVisiblePointBindingV1, + expected_occurrence: OccurrenceId, +) -> (Srgb8, Srgb8) { + let core_physical = core.physical(); + let core_occurrence = core_physical.occurrence(); + let core_program_occurrence = core_physical.program_occurrence(); + assert_eq!(core_program_occurrence.occurrence(), expected_occurrence); + let PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver(package_physical) = + package.binding().physical(); + assert_eq!( + package_physical.subject_paint().value(), + core_program_occurrence.subject().value() + ); + assert_eq!( + package_physical.backdrop_surface().value(), + core_program_occurrence.backdrop_surface().value() + ); + assert_eq!( + package_physical.subject(), + Srgb8::new(core_occurrence.subject_rgb()) + ); + assert_eq!( + package_physical.opacity().to_bits(), + core_occurrence.subject_opacity_bits() + ); + assert_eq!( + package_physical.backdrop(), + Srgb8::new(core_occurrence.backdrop_rgb()) + ); + assert_eq!( + package_physical.visible(), + Srgb8::new(core_occurrence.output_rgb()) + ); + + let PackageProgramModeledPointV1::Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1( + package_modeled, + ) = package.binding().modeled(); + assert_eq!( + package_modeled.xyz().map(f64::to_bits), + core.modeled_lcs() + .derivation() + .sample() + .xyz() + .map(f64::to_bits) + ); + let package_context = package_modeled.appearance_context(); + let core_context = core.modeled_lcs().occurrence().context(); + assert_eq!( + package_context.adapting_luminance_cd_m2().to_bits(), + core_context.adapting_luminance_cd_m2().to_bits() + ); + assert_eq!( + package_context.background_luminance_ratio_yb_yw().to_bits(), + core_context.background_luminance_ratio().to_bits() + ); + let core_surround = match core_context.surround_profile() { + SurroundProfileId::AverageV1 => PackageProgramSurroundV1::Average, + SurroundProfileId::DimV1 => PackageProgramSurroundV1::Dim, + SurroundProfileId::DarkV1 => PackageProgramSurroundV1::Dark, + }; + assert_eq!(package_context.surround(), core_surround); + + (package_physical.visible(), package_physical.backdrop()) +} + +fn assert_package_assessment_matches_core( + package: PackageProgramAssessmentV1<'_>, + core: &ProgramConstraintResultV1, + expected_occurrence: OccurrenceId, +) { + match (package, core) { + ( + PackageProgramAssessmentV1::ExactSrgb8(package), + ProgramConstraintResultV1::Pass(CoreProgramPassEvidenceV1::ExactSrgb8(core)), + ) => { + assert_eq!(package.verdict(), PackageProgramVerdictV1::Pass); + assert_eq!(package.expected(), core.target()); + let (visible, _) = assert_package_binding_matches_core( + PackageProgramAssessmentV1::ExactSrgb8(package), + core.binding(), + expected_occurrence, + ); + assert_eq!(visible, core.actual()); + } + ( + PackageProgramAssessmentV1::ExactSrgb8(package), + ProgramConstraintResultV1::Violation(CoreProgramViolationEvidenceV1::ExactSrgb8(core)), + ) => { + assert_eq!(package.verdict(), PackageProgramVerdictV1::Violation); + assert_eq!(package.expected(), core.target()); + let (visible, _) = assert_package_binding_matches_core( + PackageProgramAssessmentV1::ExactSrgb8(package), + core.binding(), + expected_occurrence, + ); + assert_eq!(visible, core.actual()); + } + ( + PackageProgramAssessmentV1::Wcag22Srgb8(package), + ProgramConstraintResultV1::Pass(CoreProgramPassEvidenceV1::Wcag22Srgb8(core)), + ) => { + assert_eq!(package.verdict(), PackageProgramVerdictV1::Pass); + let measurement = core.measurement().value(); + assert_eq!(package.profile_id(), measurement.profile_id()); + assert_eq!(package.criterion(), measurement.criterion()); + assert_eq!( + package.foreground_luminance(), + measurement.measurement().foreground_luminance + ); + assert_eq!( + package.background_luminance(), + measurement.measurement().background_luminance + ); + assert_eq!(package.numerical_evidence(), measurement.evidence()); + let (visible, backdrop) = assert_package_binding_matches_core( + PackageProgramAssessmentV1::Wcag22Srgb8(package), + core.binding(), + expected_occurrence, + ); + assert_eq!(visible, Srgb8::new(measurement.measurement().foreground)); + assert_eq!(backdrop, Srgb8::new(measurement.measurement().background)); + } + ( + PackageProgramAssessmentV1::Wcag22Srgb8(package), + ProgramConstraintResultV1::Violation(CoreProgramViolationEvidenceV1::Wcag22Srgb8(core)), + ) => { + assert_eq!(package.verdict(), PackageProgramVerdictV1::Violation); + let measurement = core.measurement().value(); + assert_eq!(package.profile_id(), measurement.profile_id()); + assert_eq!(package.criterion(), measurement.criterion()); + assert_eq!( + package.foreground_luminance(), + measurement.measurement().foreground_luminance + ); + assert_eq!( + package.background_luminance(), + measurement.measurement().background_luminance + ); + assert_eq!(package.numerical_evidence(), measurement.evidence()); + let (visible, backdrop) = assert_package_binding_matches_core( + PackageProgramAssessmentV1::Wcag22Srgb8(package), + core.binding(), + expected_occurrence, + ); + assert_eq!(visible, Srgb8::new(measurement.measurement().foreground)); + assert_eq!(backdrop, Srgb8::new(measurement.measurement().background)); + } + _ => panic!("package assessment family or verdict drifted from Core"), + } +} + +fn assert_verified_cell_matches_core( + package: PackageProgramVerifiedCellV1<'_>, + core: &ProgramConstraintCellV1, + selected_state_index: usize, +) { + assert_eq!(core.candidate_state_index(), selected_state_index); + assert_eq!(package.case_index(), core.case_index()); + assert_eq!(package.constraint().value(), core.constraint().value()); + assert_eq!(package.occurrence().value(), core.target().value()); + assert_eq!( + matches!( + package.mode(), + crate::package_bridge::PackageProgramConstraintModeV1::Hard + ), + core.is_hard() + ); + assert_package_assessment_matches_core(package.assessment(), core.result(), core.target()); +} + +fn assert_conflict_cell_matches_core( + package: PackageProgramConflictCellV1<'_>, + core: &ProgramConstraintCellV1, +) { + assert_eq!(package.state_index(), core.candidate_state_index()); + assert_eq!(package.case_index(), core.case_index()); + assert_eq!(package.constraint().value(), core.constraint().value()); + assert_eq!(package.occurrence().value(), core.target().value()); + assert_eq!( + matches!( + package.mode(), + crate::package_bridge::PackageProgramConstraintModeV1::Hard + ), + core.is_hard() + ); + assert_package_assessment_matches_core(package.assessment(), core.result(), core.target()); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct ProjectionProbe { + iterators: usize, + certificates: usize, + cases: usize, + values: usize, + provenance: usize, + cells: usize, + outputs: usize, + operations: usize, + exact_assessments: usize, + wcag_assessments: usize, + iterator_laws_hold: bool, + checksum: u64, +} + +impl ProjectionProbe { + const fn new() -> Self { + Self { + iterators: 0, + certificates: 0, + cases: 0, + values: 0, + provenance: 0, + cells: 0, + outputs: 0, + operations: 0, + exact_assessments: 0, + wcag_assessments: 0, + iterator_laws_hold: true, + checksum: 0, + } + } + + fn mix(&mut self, value: u64) { + self.checksum = self.checksum.rotate_left(1) ^ value; + } + + fn mix_bytes(&mut self, bytes: &[u8]) { + for byte in bytes { + self.mix(u64::from(*byte)); + } + } + + fn mix_srgb8(&mut self, value: Srgb8) { + self.mix_bytes(&value.bytes()); + } +} + +fn consume_exact_fused( + mut iterator: I, + probe: &mut ProjectionProbe, + mut consume: impl FnMut(I::Item, &mut ProjectionProbe), +) where + I: ExactSizeIterator + FusedIterator, +{ + probe.iterators += 1; + let mut remaining = iterator.len(); + let initial_len = remaining; + probe.iterator_laws_hold &= iterator.size_hint() == (remaining, Some(remaining)); + for _ in 0..initial_len { + let Some(value) = iterator.next() else { + probe.iterator_laws_hold = false; + break; + }; + remaining -= 1; + probe.iterator_laws_hold &= iterator.len() == remaining; + probe.iterator_laws_hold &= iterator.size_hint() == (remaining, Some(remaining)); + consume(value, probe); + } + probe.iterator_laws_hold &= remaining == 0; + probe.iterator_laws_hold &= iterator.len() == 0; + probe.iterator_laws_hold &= iterator.next().is_none(); + probe.iterator_laws_hold &= iterator.next().is_none(); +} + +fn consume_package_assessment( + assessment: PackageProgramAssessmentV1<'_>, + probe: &mut ProjectionProbe, +) { + probe.mix(match assessment.verdict() { + PackageProgramVerdictV1::Pass => 1, + PackageProgramVerdictV1::Violation => 2, + }); + match assessment { + PackageProgramAssessmentV1::ExactSrgb8(evidence) => { + probe.exact_assessments += 1; + probe.mix_srgb8(evidence.expected()); + } + PackageProgramAssessmentV1::Wcag22Srgb8(evidence) => { + probe.wcag_assessments += 1; + probe.mix_bytes(evidence.profile_id().key().as_bytes()); + probe.mix_bytes(evidence.criterion().key().as_bytes()); + probe.mix(evidence.foreground_luminance().lower()); + probe.mix(evidence.foreground_luminance().upper()); + probe.mix(evidence.background_luminance().lower()); + probe.mix(evidence.background_luminance().upper()); + probe.mix_bytes(evidence.numerical_evidence().class_key().as_bytes()); + } + } + + let PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver(physical) = + assessment.binding().physical(); + probe.mix(u64::from(physical.subject_paint().value())); + probe.mix(u64::from(physical.backdrop_surface().value())); + probe.mix_srgb8(physical.subject()); + probe.mix(physical.opacity().to_bits()); + probe.mix_srgb8(physical.backdrop()); + probe.mix_srgb8(physical.visible()); + + let PackageProgramModeledPointV1::Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1(modeled) = + assessment.binding().modeled(); + for coordinate in modeled.xyz() { + probe.mix(coordinate.to_bits()); + } + let context = modeled.appearance_context(); + probe.mix(context.adapting_luminance_cd_m2().to_bits()); + probe.mix(context.background_luminance_ratio_yb_yw().to_bits()); + probe.mix(match context.surround() { + PackageProgramSurroundV1::Average => 1, + PackageProgramSurroundV1::Dim => 2, + PackageProgramSurroundV1::Dark => 3, + }); +} + +fn consume_package_projection(view: PackageProgramStateViewV1<'_>) -> ProjectionProbe { + let mut probe = ProjectionProbe::new(); + probe.mix(match view.kind() { + PackageProgramStateKindV1::Waiting => 1, + PackageProgramStateKindV1::Ready => 2, + PackageProgramStateKindV1::Failed => 3, + PackageProgramStateKindV1::Stale => 4, + }); + probe.mix(view.revision().unwrap_or_default()); + probe.mix( + view.cause_certificate_index() + .map_or(0, |index| index as u64 + 1), + ); + + consume_exact_fused(view.certificates(), &mut probe, |certificate, probe| { + probe.certificates += 1; + probe.mix_bytes(certificate.content_identity().as_bytes()); + let observation = certificate.observation(); + probe.mix(u64::from(observation.stream().value())); + probe.mix(observation.revision()); + consume_exact_fused(observation.surface_input_ports(), probe, |port, probe| { + probe.mix(u64::from(port.value())); + }); + consume_exact_fused(observation.physical_cases(), probe, |case, probe| { + probe.cases += 1; + consume_exact_fused(case.values(), probe, |value, probe| { + probe.values += 1; + let PackageProgramSignalV1::Iec61966Srgb8D65(value) = value; + probe.mix_srgb8(value); + }); + consume_exact_fused(case.provenance(), probe, |scenario, probe| { + probe.provenance += 1; + probe.mix(u64::from(scenario.value())); + }); + }); + match certificate { + PackageProgramCertificateV1::Verified(verified) => { + probe.mix( + verified + .selected_state_index() + .map_or(0, |index| index as u64 + 1), + ); + consume_exact_fused(verified.cells(), probe, |cell, probe| { + probe.cells += 1; + probe.mix(cell.case_index() as u64); + probe.mix(u64::from(cell.constraint().value())); + probe.mix(u64::from(cell.occurrence().value())); + probe.mix(match cell.mode() { + crate::package_bridge::PackageProgramConstraintModeV1::Hard => 1, + crate::package_bridge::PackageProgramConstraintModeV1::ReportOnly => 2, + }); + consume_package_assessment(cell.assessment(), probe); + }); + consume_exact_fused(verified.outputs(), probe, |output, probe| { + probe.outputs += 1; + probe.mix(u64::from(output.output_slot().value())); + probe.mix(u64::from(output.paint().value())); + probe.mix_srgb8(output.source()); + probe.mix(output.opacity().to_bits()); + }); + } + PackageProgramCertificateV1::Conflict(conflict) => { + probe.mix(conflict.considered_state_count() as u64); + consume_exact_fused(conflict.cells(), probe, |cell, probe| { + probe.cells += 1; + probe.mix(cell.state_index() as u64); + probe.mix(cell.case_index() as u64); + probe.mix(u64::from(cell.constraint().value())); + probe.mix(u64::from(cell.occurrence().value())); + probe.mix(match cell.mode() { + crate::package_bridge::PackageProgramConstraintModeV1::Hard => 1, + crate::package_bridge::PackageProgramConstraintModeV1::ReportOnly => 2, + }); + consume_package_assessment(cell.assessment(), probe); + }); + } + } + }); + consume_exact_fused(view.operations(), &mut probe, |operation, probe| { + probe.operations += 1; + match operation { + PackageProgramOperationV1::Set(set) => { + probe.mix(1); + probe.mix(u64::from(set.output_slot().value())); + probe.mix_srgb8(set.source()); + probe.mix(set.opacity().to_bits()); + probe.mix_bytes(set.certificate().content_identity().as_bytes()); + probe.mix(set.certificate().observation().revision()); + } + PackageProgramOperationV1::Remove(remove) => { + probe.mix(2); + probe.mix(u64::from(remove.output_slot().value())); + } + PackageProgramOperationV1::Hold(hold) => { + probe.mix(3); + probe.mix(u64::from(hold.output_slot().value())); + probe.mix_bytes(hold.certificate().content_identity().as_bytes()); + probe.mix(hold.certificate().observation().revision()); + } + } + }); + std::hint::black_box(probe) +} + #[test] fn one_program_retains_typed_exact_and_wcag22_outcomes() { let program: CoreProgramV1 = Program::new( @@ -224,6 +749,42 @@ fn one_program_retains_typed_exact_and_wcag22_outcomes() { ); } +#[test] +fn fixed_package_certificate_retains_none_selection_and_nonunit_output_opacity() { + let owner = PackageProgramOwnerV1::from_compiled(fixed_translucent_program()); + let mut session = owner.instantiate(STREAM.value()).unwrap(); + let white = [Srgb8::new([0xFF; 3])]; + let scenarios = [PackageProgramScenarioV1::new(1, &white)]; + let state = session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &scenarios, + }) + .unwrap(); + let Some(PackageProgramCertificateV1::Verified(certificate)) = state.certificates().next() + else { + panic!("the exact translucent midpoint must be verified"); + }; + assert_eq!(certificate.selected_state_index(), None); + let PackageProgramAssessmentV1::ExactSrgb8(assessment) = + certificate.cells().next().unwrap().assessment() + else { + panic!("the fixed Program has one Exact certificate cell"); + }; + let PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver(physical) = + assessment.binding().physical(); + assert_eq!(physical.opacity().to_bits(), 0.5_f64.to_bits()); + assert_eq!(physical.visible(), Srgb8::new([0x80; 3])); + assert_eq!( + certificate.outputs().next().unwrap().opacity().to_bits(), + physical.opacity().to_bits() + ); + let Some(PackageProgramOperationV1::Set(set)) = state.operations().next() else { + panic!("Verified must emit one Set"); + }; + assert_eq!(set.opacity().to_bits(), physical.opacity().to_bits()); +} + #[test] fn mixed_families_select_only_a_state_that_passes_every_case_then_recheck_it() { let compiled = finite_program([[0x80; 3], [0; 3]]); @@ -302,6 +863,438 @@ fn mixed_family_conflict_is_exhaustive_and_keeps_report_only_non_gating() { )); } +#[test] +fn package_projection_preserves_every_exposed_ready_and_conflict_field_against_core() { + let ready_core_owner = finite_program([[0x80; 3], [0; 3]]); + let ready_identity = ready_core_owner.content_identity(); + let ready_package_owner = + PackageProgramOwnerV1::from_compiled(finite_program([[0x80; 3], [0; 3]])); + let mut ready_core_session = ready_core_owner.instantiate(STREAM).unwrap(); + let mut ready_package_session = ready_package_owner.instantiate(STREAM.value()).unwrap(); + let ready_backdrops = [[0xFF; 3], [0xFF; 3], [0x80; 3]]; + let SessionState::Ready { + current: core_verified, + } = ready_core_session + .update(observed_backdrops(&ready_backdrops)) + .unwrap() + else { + panic!("black is the first state that passes both canonical physical cases"); + }; + let ready_white = [Srgb8::new([0xFF; 3])]; + let ready_gray = [Srgb8::new([0x80; 3])]; + let ready_scenarios = [ + PackageProgramScenarioV1::new(1, &ready_white), + PackageProgramScenarioV1::new(2, &ready_white), + PackageProgramScenarioV1::new(3, &ready_gray), + ]; + let package_ready = ready_package_session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &ready_scenarios, + }) + .unwrap(); + let mut package_certificates = package_ready.certificates(); + assert_eq!(package_certificates.len(), 1); + let Some(PackageProgramCertificateV1::Verified(package_verified)) = package_certificates.next() + else { + panic!("Ready must retain exactly one Verified certificate"); + }; + assert!(package_certificates.next().is_none()); + assert!(package_certificates.next().is_none()); + assert_eq!( + package_verified.content_identity().as_bytes(), + ready_identity.as_bytes() + ); + assert_package_observation_matches_core( + package_verified.observation(), + core_verified.report().observation(), + ); + assert_eq!( + package_verified.selected_state_index(), + core_verified.selected_state_index() + ); + let selected_state_index = core_verified.selected_state_index().unwrap(); + let mut package_cells = package_verified.cells(); + let mut core_cells = core_verified.report().cells().iter(); + assert_eq!(package_cells.len(), core_cells.len()); + while let (Some(package), Some(core)) = (package_cells.next(), core_cells.next()) { + assert_verified_cell_matches_core(package, core, selected_state_index); + assert_eq!(package_cells.len(), core_cells.len()); + } + assert!(package_cells.next().is_none()); + assert!(package_cells.next().is_none()); + assert!(core_cells.next().is_none()); + + let mut package_outputs = package_verified.outputs(); + let mut core_outputs = core_verified.outputs().iter(); + assert_eq!(package_outputs.len(), core_outputs.len()); + while let (Some(package), Some(core)) = (package_outputs.next(), core_outputs.next()) { + assert_eq!(package.output_slot().value(), core.output().value()); + assert_eq!(package.paint().value(), core.paint().id().value()); + assert_eq!(package.source(), core.paint().source()); + assert_eq!( + package.opacity().to_bits(), + core.paint().opacity().value().to_bits() + ); + assert_eq!(package_outputs.len(), core_outputs.len()); + } + assert!(package_outputs.next().is_none()); + assert!(package_outputs.next().is_none()); + assert!(core_outputs.next().is_none()); + let mut ready_operations = package_ready.operations(); + assert_eq!(ready_operations.len(), core_verified.outputs().len()); + for core_output in core_verified.outputs() { + let Some(PackageProgramOperationV1::Set(set)) = ready_operations.next() else { + panic!("every certified output must become exactly one Set"); + }; + assert_eq!(set.output_slot().value(), core_output.output().value()); + assert_eq!(set.source(), core_output.paint().source()); + assert_eq!( + set.opacity().to_bits(), + core_output.paint().opacity().value().to_bits() + ); + assert_eq!( + set.certificate().content_identity(), + package_verified.content_identity() + ); + assert_eq!( + set.certificate().observation().revision(), + package_verified.observation().revision() + ); + } + assert!(ready_operations.next().is_none()); + assert!(ready_operations.next().is_none()); + + let conflict_core_owner = finite_program([[0; 3], [0xFF; 3]]); + let conflict_identity = conflict_core_owner.content_identity(); + let conflict_package_owner = + PackageProgramOwnerV1::from_compiled(finite_program([[0; 3], [0xFF; 3]])); + let mut conflict_core_session = conflict_core_owner.instantiate(STREAM).unwrap(); + let mut conflict_package_session = conflict_package_owner.instantiate(STREAM.value()).unwrap(); + let conflict_backdrops = [[0xFF; 3], [0; 3]]; + let SessionState::Failed { + cause: core_conflict, + previous: None, + } = conflict_core_session + .update(observed_backdrops(&conflict_backdrops)) + .unwrap() + else { + panic!("neither black nor white passes both opposing physical cases"); + }; + let conflict_white = [Srgb8::new([0xFF; 3])]; + let conflict_black = [Srgb8::new([0; 3])]; + let conflict_scenarios = [ + PackageProgramScenarioV1::new(1, &conflict_white), + PackageProgramScenarioV1::new(2, &conflict_black), + ]; + let package_failed = conflict_package_session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &conflict_scenarios, + }) + .unwrap(); + let mut package_certificates = package_failed.certificates(); + assert_eq!(package_certificates.len(), 1); + let Some(PackageProgramCertificateV1::Conflict(package_conflict)) = package_certificates.next() + else { + panic!("Failed without previous state must retain one Conflict certificate"); + }; + assert!(package_certificates.next().is_none()); + assert!(package_certificates.next().is_none()); + assert_eq!( + package_conflict.content_identity().as_bytes(), + conflict_identity.as_bytes() + ); + assert_package_observation_matches_core( + package_conflict.observation(), + core_conflict.report().observation(), + ); + assert_eq!( + package_conflict.considered_state_count(), + core_conflict.considered_state_count() + ); + let core_passes = core_conflict + .report() + .cells() + .iter() + .filter(|cell| !cell.result().is_violation()) + .count(); + assert!(core_passes > 0); + assert!(core_passes < core_conflict.report().cells().len()); + let mut package_cells = package_conflict.cells(); + let mut core_cells = core_conflict.report().cells().iter(); + assert_eq!(package_cells.len(), core_cells.len()); + while let (Some(package), Some(core)) = (package_cells.next(), core_cells.next()) { + assert_conflict_cell_matches_core(package, core); + assert_eq!(package_cells.len(), core_cells.len()); + } + assert!(package_cells.next().is_none()); + assert!(package_cells.next().is_none()); + assert!(core_cells.next().is_none()); + let mut failed_operations = package_failed.operations(); + assert_eq!(failed_operations.len(), 1); + assert!(matches!( + failed_operations.next(), + Some(PackageProgramOperationV1::Remove(_)) + )); + assert!(failed_operations.next().is_none()); + assert!(failed_operations.next().is_none()); +} + +#[test] +fn committed_projection_is_zero_alloc_and_repeats_no_composite_transform_or_evaluator_dispatch() { + crate::composition::reset_source_over_evaluation_count(); + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(|calls| calls.set(0)); + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + + let owner = PackageProgramOwnerV1::from_compiled(finite_program([[0; 3], [0xFF; 3]])); + let mut session = owner.instantiate(STREAM.value()).unwrap(); + let white = [Srgb8::new([0xFF; 3])]; + let white_only = [PackageProgramScenarioV1::new(1, &white)]; + session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &white_only, + }) + .unwrap(); + let Some(PackageProgramCertificateV1::Verified(ready_certificate)) = + session.state().certificates().next() + else { + panic!("black must be selected for the white-only physical support"); + }; + assert_eq!(ready_certificate.selected_state_index(), Some(0)); + + let ready_compositions = crate::composition::source_over_evaluation_count(); + let ready_derivations = MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get); + let ready_assessments = CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get); + assert!(ready_compositions > 0); + assert!(ready_derivations > 0); + assert!(ready_assessments > 0); + let (ready_probe, ready_allocations) = crate::test_support::measured_allocations(|| { + consume_package_projection(std::hint::black_box(session.state())) + }); + assert_eq!(ready_allocations, 0); + assert!(ready_probe.iterator_laws_hold); + assert_eq!(ready_probe.certificates, 1); + assert_eq!(ready_probe.cases, 1); + assert_eq!(ready_probe.values, 1); + assert_eq!(ready_probe.provenance, 1); + assert_eq!(ready_probe.cells, 2); + assert_eq!(ready_probe.outputs, 1); + assert_eq!(ready_probe.operations, 1); + assert_eq!(ready_probe.exact_assessments, 1); + assert_eq!(ready_probe.wcag_assessments, 1); + assert_ne!(ready_probe.checksum, 0); + assert_eq!( + crate::composition::source_over_evaluation_count(), + ready_compositions + ); + assert_eq!( + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get), + ready_derivations + ); + assert_eq!( + CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get), + ready_assessments + ); + + session + .update(PackageProgramUpdateV1::Unknown { + revision: 2, + reason_id: 7, + }) + .unwrap(); + let stale_compositions = crate::composition::source_over_evaluation_count(); + let stale_derivations = MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get); + let stale_assessments = CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get); + let (stale_probe, stale_allocations) = crate::test_support::measured_allocations(|| { + consume_package_projection(std::hint::black_box(session.state())) + }); + assert_eq!(stale_allocations, 0); + assert!(stale_probe.iterator_laws_hold); + assert_eq!(stale_probe.certificates, 1); + assert_eq!(stale_probe.cells, 2); + assert_eq!(stale_probe.outputs, 1); + assert_eq!(stale_probe.operations, 1); + assert_ne!(stale_probe.checksum, ready_probe.checksum); + assert_eq!( + crate::composition::source_over_evaluation_count(), + stale_compositions + ); + assert_eq!( + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get), + stale_derivations + ); + assert_eq!( + CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get), + stale_assessments + ); + + let black = [Srgb8::new([0; 3])]; + let opposing_backdrops = [ + PackageProgramScenarioV1::new(1, &white), + PackageProgramScenarioV1::new(2, &black), + ]; + session + .update(PackageProgramUpdateV1::Observed { + revision: 3, + scenarios: &opposing_backdrops, + }) + .unwrap(); + let failed_compositions = crate::composition::source_over_evaluation_count(); + let failed_derivations = MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get); + let failed_assessments = CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get); + let (failed_probe, failed_allocations) = crate::test_support::measured_allocations(|| { + consume_package_projection(std::hint::black_box(session.state())) + }); + assert_eq!(failed_allocations, 0); + assert!(failed_probe.iterator_laws_hold); + assert_eq!(failed_probe.certificates, 2); + assert_eq!(failed_probe.cases, 3); + assert_eq!(failed_probe.values, 3); + assert_eq!(failed_probe.provenance, 3); + assert_eq!(failed_probe.cells, 10); + assert_eq!(failed_probe.outputs, 1); + assert_eq!(failed_probe.operations, 1); + assert_eq!(failed_probe.exact_assessments, 5); + assert_eq!(failed_probe.wcag_assessments, 5); + assert_ne!(failed_probe.checksum, stale_probe.checksum); + assert_eq!( + crate::composition::source_over_evaluation_count(), + failed_compositions + ); + assert_eq!( + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get), + failed_derivations + ); + assert_eq!( + CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get), + failed_assessments + ); +} + +#[test] +fn observation_projection_is_invariant_under_every_scenario_permutation_and_keeps_provenance() { + const PERMUTATIONS: [[usize; 3]; 6] = [ + [0, 1, 2], + [0, 2, 1], + [1, 0, 2], + [1, 2, 0], + [2, 0, 1], + [2, 1, 0], + ]; + const IDS: [u32; 3] = [9, 4, 3]; + const BACKDROPS: [[u8; 3]; 3] = [[0xFF; 3], [0x80; 3], [0xFF; 3]]; + + crate::composition::reset_source_over_evaluation_count(); + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(|calls| calls.set(0)); + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(0)); + + let core_owner = finite_program([[0x80; 3], [0; 3]]); + let content_identity = core_owner.content_identity(); + let package_owner = PackageProgramOwnerV1::from_compiled(finite_program([[0x80; 3], [0; 3]])); + let mut core_session = core_owner.instantiate(STREAM).unwrap(); + let mut package_session = package_owner.instantiate(STREAM.value()).unwrap(); + let package_values = BACKDROPS.map(|value| [Srgb8::new(value)]); + let mut first_package_backing = None; + let mut evaluation_counts_after_first = None; + + for permutation in PERMUTATIONS { + let core_update = ObservationUpdateInput { + stream: STREAM, + revision: Revision::new(1), + payload: ObservationPayloadInput::Scenarios(ObservedScenarioSetInput { + scenarios: permutation + .iter() + .map(|index| ScenarioInput { + id: ScenarioId::new(IDS[*index]), + bindings: vec![SurfaceInputBinding::new( + SURFACE_PORT, + signal(BACKDROPS[*index]), + )], + }) + .collect(), + }), + }; + let SessionState::Ready { + current: core_verified, + } = core_session.update(core_update).unwrap() + else { + panic!("black must pass both deduplicated physical cases"); + }; + let package_scenarios = permutation + .map(|index| PackageProgramScenarioV1::new(IDS[index], &package_values[index])); + let package_state = package_session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &package_scenarios, + }) + .unwrap(); + let Some(PackageProgramCertificateV1::Verified(package_verified)) = + package_state.certificates().next() + else { + panic!("the canonical observation must keep one Verified certificate"); + }; + assert_eq!( + package_verified.content_identity().as_bytes(), + content_identity.as_bytes() + ); + assert_package_observation_matches_core( + package_verified.observation(), + core_verified.report().observation(), + ); + + let projected_cases = package_verified + .observation() + .physical_cases() + .map(|case| { + let values = case + .values() + .map(|value| match value { + PackageProgramSignalV1::Iec61966Srgb8D65(value) => value, + }) + .collect::>(); + let provenance = case + .provenance() + .map(|scenario| scenario.value()) + .collect::>(); + (values, provenance) + }) + .collect::>(); + assert_eq!( + projected_cases, + [ + (vec![Srgb8::new([0x80; 3])], vec![4]), + (vec![Srgb8::new([0xFF; 3])], vec![3, 9]), + ] + ); + + let backing = PackageProgramCertificateV1::Verified(package_verified) + .observation_backing_ptr_for_test(); + match first_package_backing { + None => { + first_package_backing = Some(backing); + evaluation_counts_after_first = Some(( + crate::composition::source_over_evaluation_count(), + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get), + CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get), + )); + } + Some(first) => { + assert_eq!(backing, first); + assert_eq!( + evaluation_counts_after_first, + Some(( + crate::composition::source_over_evaluation_count(), + MODELED_TRISTIMULUS_DERIVATION_CALLS.with(core::cell::Cell::get), + CORE_PROGRAM_ASSESSMENT_CALLS.with(core::cell::Cell::get), + )) + ); + } + } + } +} + #[test] fn concrete_package_bridge_projects_total_ready_and_stale_operations() { let owner = PackageProgramOwnerV1::from_compiled(finite_program([[0x80; 3], [0; 3]])); @@ -335,21 +1328,37 @@ fn concrete_package_bridge_projects_total_ready_and_stale_operations() { assert_eq!(ready.cause_certificate_index(), None); let certificates = ready.certificates().collect::>(); assert_eq!(certificates.len(), 1); + assert!(matches!( + certificates[0], + PackageProgramCertificateV1::Verified(_) + )); + assert_eq!(certificates[0].observation().revision(), 1); + let ready_backing = certificates[0].observation_backing_ptr_for_test(); + let mut operations = ready.operations(); + let Some(PackageProgramOperationV1::Set(set)) = operations.next() else { + panic!("Ready must emit one Set operation"); + }; assert_eq!( - certificates[0].kind(), - PackageProgramCertificateKindV1::Verified + set.output_slot(), + PackageProgramOutputSlotIdV1::new(OUTPUT.value()) ); - assert_eq!(certificates[0].revision(), 1); - let ready_backing = certificates[0].observation_backing_ptr_for_test(); + assert_eq!(set.source(), Srgb8::new([0; 3])); + assert_eq!(set.opacity(), 1.0); assert_eq!( - ready.operations().collect::>(), - [PackageProgramOperationV1::Set { - output_slot: PackageProgramOutputSlotIdV1::new(OUTPUT.value()), - source: Srgb8::new([0; 3]), - opacity: 1.0, - certificate_index: 0, - }] + set.certificate().observation().revision(), + certificates[0].observation().revision() ); + assert_eq!( + set.certificate().content_identity(), + certificates[0].content_identity() + ); + assert_eq!( + PackageProgramCertificateV1::Verified(set.certificate()).observation_backing_ptr_for_test(), + ready_backing + ); + assert!(operations.next().is_none()); + drop(operations); + drop(certificates); let reordered = [ PackageProgramScenarioV1::new(1, &white), @@ -393,18 +1402,33 @@ fn concrete_package_bridge_projects_total_ready_and_stale_operations() { assert_eq!(stale.revision(), Some(2)); let certificates = stale.certificates().collect::>(); assert_eq!(certificates.len(), 1); + assert!(matches!( + certificates[0], + PackageProgramCertificateV1::Verified(_) + )); + assert_eq!(certificates[0].observation().revision(), 1); + let mut operations = stale.operations(); + let Some(PackageProgramOperationV1::Hold(hold)) = operations.next() else { + panic!("Stale must emit one Hold operation"); + }; + assert_eq!( + hold.output_slot(), + PackageProgramOutputSlotIdV1::new(OUTPUT.value()) + ); + assert_eq!( + hold.certificate().observation().revision(), + certificates[0].observation().revision() + ); assert_eq!( - certificates[0].kind(), - PackageProgramCertificateKindV1::Verified + hold.certificate().content_identity(), + certificates[0].content_identity() ); - assert_eq!(certificates[0].revision(), 1); assert_eq!( - stale.operations().collect::>(), - [PackageProgramOperationV1::Hold { - output_slot: PackageProgramOutputSlotIdV1::new(OUTPUT.value()), - certificate_index: 0, - }] + PackageProgramCertificateV1::Verified(hold.certificate()) + .observation_backing_ptr_for_test(), + ready_backing ); + assert!(operations.next().is_none()); } #[test] @@ -425,26 +1449,34 @@ fn concrete_package_bridge_distinguishes_failed_remove_from_failed_hold() { assert_eq!(failed.cause_certificate_index(), Some(0)); let certificates = failed.certificates().collect::>(); assert_eq!(certificates.len(), 1); + assert!(matches!( + certificates[0], + PackageProgramCertificateV1::Conflict(_) + )); + assert_eq!(certificates[0].observation().revision(), 1); + let mut operations = failed.operations(); + let Some(PackageProgramOperationV1::Remove(remove)) = operations.next() else { + panic!("Failed without previous evidence must emit one Remove operation"); + }; assert_eq!( - certificates[0].kind(), - PackageProgramCertificateKindV1::Conflict - ); - assert_eq!(certificates[0].revision(), 1); - assert_eq!( - failed.operations().collect::>(), - [PackageProgramOperationV1::Remove { - output_slot: PackageProgramOutputSlotIdV1::new(OUTPUT.value()), - }] + remove.output_slot(), + PackageProgramOutputSlotIdV1::new(OUTPUT.value()) ); + assert!(operations.next().is_none()); let owner = PackageProgramOwnerV1::from_compiled(finite_program([[0; 3], [0xFF; 3]])); let mut session = owner.instantiate(12).unwrap(); - session + let previous = session .update(PackageProgramUpdateV1::Observed { revision: 1, scenarios: &white_only, }) .unwrap(); + let previous_backing = previous + .certificates() + .next() + .unwrap() + .observation_backing_ptr_for_test(); let both = [ PackageProgramScenarioV1::new(1, &white), PackageProgramScenarioV1::new(2, &black), @@ -461,20 +1493,36 @@ fn concrete_package_bridge_distinguishes_failed_remove_from_failed_hold() { assert_eq!( certificates .iter() - .map(|certificate| (certificate.kind(), certificate.revision())) + .map(|certificate| match certificate { + PackageProgramCertificateV1::Verified(value) => { + ("verified", value.observation().revision()) + } + PackageProgramCertificateV1::Conflict(value) => { + ("conflict", value.observation().revision()) + } + }) .collect::>(), - [ - (PackageProgramCertificateKindV1::Conflict, 2), - (PackageProgramCertificateKindV1::Verified, 1), - ] + [("conflict", 2), ("verified", 1)] + ); + let mut operations = failed.operations(); + let Some(PackageProgramOperationV1::Hold(hold)) = operations.next() else { + panic!("Failed with previous evidence must emit one Hold operation"); + }; + assert_eq!( + hold.output_slot(), + PackageProgramOutputSlotIdV1::new(OUTPUT.value()) + ); + assert_eq!(hold.certificate().observation().revision(), 1); + assert_eq!( + hold.certificate().content_identity(), + certificates[1].content_identity() ); assert_eq!( - failed.operations().collect::>(), - [PackageProgramOperationV1::Hold { - output_slot: PackageProgramOutputSlotIdV1::new(OUTPUT.value()), - certificate_index: 1, - }] + PackageProgramCertificateV1::Verified(hold.certificate()) + .observation_backing_ptr_for_test(), + previous_backing ); + assert!(operations.next().is_none()); } #[test] diff --git a/crates/labcolors-core/src/program_session.rs b/crates/labcolors-core/src/program_session.rs index a755d754..a017e6b9 100644 --- a/crates/labcolors-core/src/program_session.rs +++ b/crates/labcolors-core/src/program_session.rs @@ -524,6 +524,15 @@ where } } +#[cfg(test)] +thread_local! { + /// Counts the concrete production evaluator dispatch itself, so certificate + /// projection cannot accidentally recompute a verdict while still reusing + /// the stored physical and modeled witnesses. + pub(crate) static CORE_PROGRAM_ASSESSMENT_CALLS: core::cell::Cell = + const { core::cell::Cell::new(0) }; +} + /// Generates the code-owned heterogeneous evaluator set as parallel closed /// unions. Each evidence variant retains the concrete evaluator's physical + /// LCS binding, identity, release, capability, invocation, measurement, and @@ -572,6 +581,8 @@ macro_rules! define_core_program_evaluators_v1 { modeled_lcs: ModeledLcsOccurrenceV1, invocation: Self::Invocation, ) -> ProgramConstraintAssessmentResultV1 { + #[cfg(test)] + CORE_PROGRAM_ASSESSMENT_CALLS.with(|calls| calls.set(calls.get() + 1)); match invocation { $(CoreProgramConstraintInvocationV1::$variant(invocation) => { let evaluator: $evaluator = $evaluator_value; diff --git a/crates/labcolors-core/tests/package_bridge_red.rs b/crates/labcolors-core/tests/program_boundary.rs similarity index 69% rename from crates/labcolors-core/tests/package_bridge_red.rs rename to crates/labcolors-core/tests/program_boundary.rs index 0e7b6469..a804b279 100644 --- a/crates/labcolors-core/tests/package_bridge_red.rs +++ b/crates/labcolors-core/tests/program_boundary.rs @@ -1,29 +1,31 @@ -//! RED contract for the sole concrete Core package seam. +//! External compile-and-runtime contract for the sole concrete Core Program seam. //! //! This integration crate deliberately has no access to Core-private generic -//! evaluator/session machinery. It must compile using only one hidden, -//! concrete package module once that seam is linked after the P3 + weak-owner -//! rebase. +//! evaluator/session machinery. Every reachable path uses only the closed +//! concrete boundary types. + +use core::iter::FusedIterator; use labcolors_core::Srgb8; use labcolors_core::package_bridge::{ PackageProgramAppearanceContextErrorKindV1, PackageProgramAppearanceContextFieldV1, - PackageProgramAppearanceContextV1, PackageProgramCertificateV1, + PackageProgramAppearanceContextV1, PackageProgramAssessmentV1, PackageProgramCertificateV1, PackageProgramCompileErrorHandleV1, PackageProgramCompileErrorKindV1, PackageProgramCompileErrorV1, PackageProgramConstraintIdV1, PackageProgramDraftErrorV1, PackageProgramDraftV1, PackageProgramInstantiateErrorV1, PackageProgramJointChoiceV1, - PackageProgramJointOrderErrorV1, PackageProgramJointStateV1, + PackageProgramJointOrderErrorV1, PackageProgramJointStateV1, PackageProgramModeledPointV1, PackageProgramNumericDomainErrorV1, PackageProgramOccurrenceIdV1, PackageProgramOpacityInputIdV1, PackageProgramOperationV1, PackageProgramOutputSlotIdV1, - PackageProgramOwnerV1, PackageProgramPaintIdV1, PackageProgramScenarioV1, - PackageProgramSessionV1, PackageProgramSourceIdV1, PackageProgramStateKindV1, - PackageProgramStateViewV1, PackageProgramSurfaceIdV1, PackageProgramSurfaceInputPortIdV1, - PackageProgramSurroundV1, PackageProgramTargetCandidateIdV1, PackageProgramTargetCandidateV1, - PackageProgramTargetIdV1, PackageProgramUpdateErrorKindV1, PackageProgramUpdateV1, + PackageProgramOwnerV1, PackageProgramPaintIdV1, PackageProgramPhysicalPointV1, + PackageProgramScenarioV1, PackageProgramSessionV1, PackageProgramSignalV1, + PackageProgramSourceIdV1, PackageProgramStateKindV1, PackageProgramStateViewV1, + PackageProgramSurfaceIdV1, PackageProgramSurfaceInputPortIdV1, PackageProgramSurroundV1, + PackageProgramTargetCandidateIdV1, PackageProgramTargetCandidateV1, PackageProgramTargetIdV1, + PackageProgramUpdateErrorKindV1, PackageProgramUpdateV1, PackageProgramVerdictV1, }; use labcolors_core::wcag22::Wcag22CriterionV1; -fn exact_size(iterator: I) -> I { +fn exact_size(iterator: I) -> I { iterator } @@ -56,33 +58,102 @@ fn assert_projection_is_linear(view: PackageProgramStateViewV1<'_>) { let certificates = exact_size(view.certificates()); let certificate_count = certificates.len(); for certificate in certificates { - let _: PackageProgramCertificateV1<'_> = certificate; + let _: &[u8; 32] = certificate.content_identity().as_bytes(); + let observation = certificate.observation(); + let _stream_id = observation.stream().value(); + let _revision = observation.revision(); + for port in exact_size(observation.surface_input_ports()) { + let _: PackageProgramSurfaceInputPortIdV1 = port; + } + for case in exact_size(observation.physical_cases()) { + for value in exact_size(case.values()) { + let PackageProgramSignalV1::Iec61966Srgb8D65(value) = value; + let _: Srgb8 = value; + } + for scenario in exact_size(case.provenance()) { + let _ = scenario.value(); + } + } + macro_rules! inspect_cell { + ($cell:expr) => {{ + let cell = $cell; + let _ = cell.case_index(); + let _ = cell.constraint().value(); + let _ = cell.occurrence().value(); + let _ = cell.mode(); + let assessment = cell.assessment(); + let _: PackageProgramVerdictV1 = assessment.verdict(); + match assessment { + PackageProgramAssessmentV1::ExactSrgb8(evidence) => { + let _: Srgb8 = evidence.expected(); + } + PackageProgramAssessmentV1::Wcag22Srgb8(evidence) => { + let _ = evidence.profile_id(); + let _ = evidence.criterion(); + let _ = evidence.foreground_luminance(); + let _ = evidence.background_luminance(); + let _ = evidence.numerical_evidence(); + } + } + let binding = assessment.binding(); + let PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver(physical) = + binding.physical(); + let _ = physical.subject_paint().value(); + let _ = physical.backdrop_surface().value(); + let _: Srgb8 = physical.subject(); + let _ = physical.opacity(); + let _: Srgb8 = physical.backdrop(); + let _: Srgb8 = physical.visible(); + let PackageProgramModeledPointV1::Iec61966Srgb8ToCie1931TwoDegreeXyzD65RelativeY1( + modeled, + ) = binding.modeled(); + let _: [f64; 3] = modeled.xyz(); + let context = modeled.appearance_context(); + let _ = context.adapting_luminance_cd_m2(); + let _ = context.background_luminance_ratio_yb_yw(); + let _ = context.surround(); + }}; + } + match certificate { + PackageProgramCertificateV1::Verified(verified) => { + let _ = verified.selected_state_index(); + for cell in exact_size(verified.cells()) { + inspect_cell!(cell); + } + for output in exact_size(verified.outputs()) { + let _ = output.output_slot().value(); + let _ = output.paint().value(); + let _: Srgb8 = output.source(); + let _ = output.opacity(); + } + } + PackageProgramCertificateV1::Conflict(conflict) => { + let _ = conflict.considered_state_count(); + for cell in exact_size(conflict.cells()) { + let _ = cell.state_index(); + inspect_cell!(cell); + } + } + } } for operation in exact_size(view.operations()) { match operation { - PackageProgramOperationV1::Set { - output_slot, - source, - opacity, - certificate_index, - } => { - let _: PackageProgramOutputSlotIdV1 = output_slot; - let _: Srgb8 = source; - assert!(opacity.is_finite() && (0.0..=1.0).contains(&opacity)); - assert!(certificate_index < certificate_count); + PackageProgramOperationV1::Set(set) => { + let _: PackageProgramOutputSlotIdV1 = set.output_slot(); + let _: Srgb8 = set.source(); + assert!(set.opacity().is_finite() && (0.0..=1.0).contains(&set.opacity())); + let _ = set.certificate().content_identity(); } - PackageProgramOperationV1::Remove { output_slot } => { - let _: PackageProgramOutputSlotIdV1 = output_slot; + PackageProgramOperationV1::Remove(remove) => { + let _: PackageProgramOutputSlotIdV1 = remove.output_slot(); } - PackageProgramOperationV1::Hold { - output_slot, - certificate_index, - } => { - let _: PackageProgramOutputSlotIdV1 = output_slot; - assert!(certificate_index < certificate_count); + PackageProgramOperationV1::Hold(hold) => { + let _: PackageProgramOutputSlotIdV1 = hold.output_slot(); + let _ = hold.certificate().content_identity(); } } } + let _ = certificate_count; } #[allow(dead_code)] @@ -104,7 +175,7 @@ fn owner_expiry_is_a_closed_package_error( } #[test] -fn red_contract_is_linked_by_the_concrete_package_module() { +fn external_boundary_uses_only_closed_concrete_types() { // Reaching this test means the external crate compiled without importing // Program, evaluator traits, Session, or numeric generations. assert_eq!(core::mem::size_of::(), 3); @@ -244,15 +315,15 @@ fn external_authoring_lowers_the_actual_closed_program_and_returns_canonical_inp }) .unwrap(); assert_eq!(ready.kind(), PackageProgramStateKindV1::Ready); - assert_eq!( - ready.operations().collect::>(), - [PackageProgramOperationV1::Set { - output_slot: output, - source: Srgb8::new([0; 3]), - opacity: 1.0, - certificate_index: 0, - }] - ); + let mut operations = ready.operations(); + let Some(PackageProgramOperationV1::Set(set)) = operations.next() else { + panic!("Ready must emit one Set operation"); + }; + assert_eq!(set.output_slot(), output); + assert_eq!(set.source(), Srgb8::new([0; 3])); + assert_eq!(set.opacity(), 1.0); + assert_eq!(set.certificate().observation().revision(), 1); + assert!(operations.next().is_none()); } #[test] @@ -294,15 +365,81 @@ fn every_physical_constructor_and_both_remaining_constraint_modes_execute() { }) .unwrap(); assert_eq!(state.kind(), PackageProgramStateKindV1::Ready); + let Some(PackageProgramCertificateV1::Verified(certificate)) = state.certificates().next() + else { + panic!("a fixed target must produce one Verified certificate"); + }; + assert_eq!(certificate.selected_state_index(), None); + let mut operations = state.operations(); + let Some(PackageProgramOperationV1::Set(set)) = operations.next() else { + panic!("Ready must emit one Set operation"); + }; + assert_eq!(set.output_slot(), PackageProgramOutputSlotIdV1::new(12)); + assert_eq!(set.source(), Srgb8::new([0; 3])); + assert_eq!(set.opacity(), 1.0); + assert_eq!(set.certificate().observation().revision(), 1); + assert!(operations.next().is_none()); +} + +#[test] +fn certificate_and_set_retain_the_same_nonunit_opacity() { + let source = PackageProgramSourceIdV1::new(1); + let target = PackageProgramTargetIdV1::new(2); + let opacity = PackageProgramOpacityInputIdV1::new(3); + let solid = PackageProgramPaintIdV1::new(4); + let translucent = PackageProgramPaintIdV1::new(5); + let input = PackageProgramSurfaceInputPortIdV1::new(6); + let surface = PackageProgramSurfaceIdV1::new(7); + let occurrence = PackageProgramOccurrenceIdV1::new(8); + let constraint = PackageProgramConstraintIdV1::new(9); + let output = PackageProgramOutputSlotIdV1::new(10); + let context = + PackageProgramAppearanceContextV1::try_new(64.0, 0.2, PackageProgramSurroundV1::Average) + .unwrap(); + let mut draft = PackageProgramDraftV1::new(); + draft.push_source(source, Srgb8::new([0; 3])); + draft.push_fixed_target(target, source); + draft.push_surface_input_port(input); + draft.push_opacity_input(opacity, 0.5); + draft.push_solid_paint(solid, target); + draft.push_opacity_paint(translucent, solid, opacity); + draft.push_input_surface(surface, input); + draft.push_source_over_occurrence(occurrence, translucent, surface, context); + draft.push_exact_hard(constraint, occurrence, Srgb8::new([0x80; 3])); + draft.push_output(output, translucent); + + let owner = draft.compile().unwrap(); + let mut session = owner.instantiate(17).unwrap(); + let white = [Srgb8::new([0xFF; 3])]; + let scenarios = [PackageProgramScenarioV1::new(1, &white)]; + let state = session + .update(PackageProgramUpdateV1::Observed { + revision: 1, + scenarios: &scenarios, + }) + .unwrap(); + let Some(PackageProgramCertificateV1::Verified(certificate)) = state.certificates().next() + else { + panic!("the exact emitted midpoint must be verified"); + }; + let PackageProgramAssessmentV1::ExactSrgb8(assessment) = + certificate.cells().next().unwrap().assessment() + else { + panic!("the authored exact constraint must retain Exact evidence"); + }; + let PackageProgramPhysicalPointV1::EncodedSrgb8SourceOver(physical) = + assessment.binding().physical(); + assert_eq!(physical.opacity().to_bits(), 0.5_f64.to_bits()); + assert_eq!(physical.visible(), Srgb8::new([0x80; 3])); assert_eq!( - state.operations().collect::>(), - [PackageProgramOperationV1::Set { - output_slot: PackageProgramOutputSlotIdV1::new(12), - source: Srgb8::new([0; 3]), - opacity: 1.0, - certificate_index: 0, - }] + certificate.outputs().next().unwrap().opacity().to_bits(), + physical.opacity().to_bits() ); + + let Some(PackageProgramOperationV1::Set(set)) = state.operations().next() else { + panic!("the verified output must emit one Set"); + }; + assert_eq!(set.opacity().to_bits(), physical.opacity().to_bits()); } #[test] From ab463f261e73e971511025f4038d63eee7f4c46f Mon Sep 17 00:00:00 2001 From: Daniel from Labpics <63733699+lemone112@users.noreply.github.com> Date: Sun, 26 Jul 2026 03:14:32 +0300 Subject: [PATCH 2/2] fix(ci): re-bind the point-support source capsule to this slice's cone This slice moves files inside the point-support semantic cone, so the capsule digest and the committed surplus proof move with it. Both are now regenerated in the same commit that causes the drift, matching the convention the rest of the stack follows; previously the re-bind was batched at #460, which left #457-#459 fail-closed on their own heads and made the stack unmergeable in order. Numerical review: every proof field is unchanged. Only the source-binding identities move -- the file hashes of the cone files this slice edits, the resulting closure digest, the verifier hash and the rolled-up payload hash. The surplus mathematics is byte-identical. Co-Authored-By: Claude --- .../point-support-reference-surplus-q55-bps-proof-v1.json | 2 +- scripts/verify_point_support_surplus.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json b/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json index 6cd37887..b4e47788 100644 --- a/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json +++ b/crates/labcolors-core/contracts/point-support-reference-surplus-q55-bps-proof-v1.json @@ -1 +1 @@ -{"artifact_id":"wcag22-srgb8-luminance-q55-v1","basis_point_proof":{"checks":30,"drop_all_semantics":"zero required surplus; current must still meet the anchor","drop_domain_inclusive":[0,10000],"nonpositive_baseline_semantics":"zero required surplus; current must meet the anchor"},"bound_id":"point-support-reference-surplus-q55-bps-v1","certified_claim":"for every successfully evaluated enabled stability cell, decision is Retained iff current_lower_surplus >= (10000-drop_bps)/10000 * max(baseline_lower_surplus,0); the declared anchor remains a separate hard floor","comparator_proof":{"algorithm":"euclidean-continued-fraction-ordering-v1","dense_denominator_inclusive":[1,31],"dense_numerator_inclusive":[0,31],"dense_small_cases":984064,"invariant":"equal integer parts; reciprocal proper fractions reverse order","largest_fibonacci_index":186,"oracle":"unbounded-integer-cross-product","random_cases":250000,"random_corpus_sha256":"97c4af7b452b31a4ab92645f70c17acb38bf57ca55484e32ad9d7d79d97a333d","random_seed":210583930,"termination":"each nonterminal denominator becomes a strictly smaller remainder","u128_adversarial_cases":190},"declared_operation_law":"q55-lower-reference-distance-explicit-anchor-bps-retention-v1","excluded_claim":"does not certify retention against the unknown exact baseline surplus, renderer equivalence outside encoded-sRGB8 source-over, or a successful result when evaluation fails","integer_replay_envelope":{"assumption":"every Q55 luminance upper <= scale + 3","i128_max":170141183460469231731687303715884105727,"offset_cleared_denominator_max":756604737398243388,"positive_baseline_numerator_max":1188950301625811064,"rational_denominator_max":1513209474796486776,"required_denominator_max":15132094747964867760000,"required_numerator_max":11889503016258110640000,"signed_anchor_abs_coarse_max":5296233161787703716,"u128_max":340282366920938463463374607431768211455,"u64_max":18446744073709551615},"profile_id":"srgb8-q55-retained-reference-surplus-bps-v1","proof_id":"point-support-reference-surplus-integer-v1","proof_payload_sha256":"78669986ea1a7a75f40e76c19beba4ff9c72abcbf1a240d4437846fc8d28519c","q55_dependency":{"artifact_id":"wcag22-srgb8-luminance-q55-v1","artifact_sha256":"7ff239d9052b346f3c50da01ca65ca2330892ed1a3ff30e190797fcef6f03604","maximum_luminance_upper":36028797018963971,"outward_interval_width_bound":3,"proof_id":"wcag22-srgb8-full-domain-q55-v1","proof_payload_sha256":"3c639a7c875046c46b56b51ecdd67d5ecaf14a1134490c88a222e7037b63c0f2","proof_sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd","q55_scale":36028797018963968},"reference_and_anchor_proof":{"anchor_identity_checks":75,"orientation_law":"distance-magnitude-symmetric-orientation-reported-separately","overlap_lower_distance":"0/1","separated_endpoint_checks":504},"schema_version":2,"site_id":"point-support-retained-reference-surplus-v1","source_binding_exclusions":["whole-crate compilation or compiler/toolchain attestation","binary, package, FFI, renderer, or browser transport attestation","unrelated Lab Colors modules outside the declared point-support semantic cone"],"source_binding_law":"point-support-rust-whole-file-semantic-cone-v2","source_binding_schema_version":2,"source_binding_scope":"exact bytes of the private point-support Rust semantic cone and its two WCAG include_str inputs; comments and cfg(test) text are intentionally significant","source_closure_sha256":"246c77dbf4923aca4cdaedb12be910ceb2885c94e4e42f652cdcba9b9417a427","source_files":[{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json","sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"},{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-v1.json","sha256":"b4bb7e5f17a99f2c911fdbe3da23a48b049277b796291094950f14680cc3cc7b"},{"kind":"rust-source","path":"crates/labcolors-core/src/appearance.rs","sha256":"455f94bdc0064765214e21ec38e49939e9ebbf765d18bb709512f7210c986953"},{"kind":"rust-source","path":"crates/labcolors-core/src/composition.rs","sha256":"195a67327a3bd86d7816b634481389930bf68577bb1202fad14c2ea152df8625"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/exact.rs","sha256":"892576a8621185352583e63dc0a1aacac32e32a8063b6fe24ae16d4ff9dce7cb"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/mod.rs","sha256":"3a4c2911781f91c91c12fc23929843865a8e41d5630f4df41f77130434b5f228"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/wcag22.rs","sha256":"40f288b222fbd102970916437d3f99c33ec2dfb77041b1c8361d27f08ff018ca"},{"kind":"rust-source","path":"crates/labcolors-core/src/hash.rs","sha256":"f97a0fd7d6ad3162f0f1dfb326fccfb7ed40da9a8fa67a5b8a239a1ae2ae49c3"},{"kind":"rust-source","path":"crates/labcolors-core/src/lcs_occurrence.rs","sha256":"9ad998d3b7ac01a03afa398a6750ab71dc1278da991202933cf9006c3cedf5f5"},{"kind":"rust-source","path":"crates/labcolors-core/src/lib.rs","sha256":"b3edb3764119c0b4fd50f52b62cbc07fa81c4bfdf1246b91f20eb3d8d4eebd31"},{"kind":"rust-source","path":"crates/labcolors-core/src/numerics.rs","sha256":"e73a12136494f2ef9aca4e943ab38302c1439f054cecab36a552d35252c164f9"},{"kind":"rust-source","path":"crates/labcolors-core/src/observation.rs","sha256":"f1d6c7a66885326caea2f7f469061c723b826ff99b294324e5a478724f8981f6"},{"kind":"rust-source","path":"crates/labcolors-core/src/point_support.rs","sha256":"0755210e3e591d7049f293a0f0b7647681631f32feee5ad7d3b3309cffca8f9d"},{"kind":"rust-source","path":"crates/labcolors-core/src/session.rs","sha256":"6cd73600267b148c3e9ecc8d2d623f7f8576aed0e1c4c7c50071e297810b8d4b"},{"kind":"rust-source","path":"crates/labcolors-core/src/srgb8.rs","sha256":"6c95324eb05476f35f75375a9af0b2b4a41b8b2978c46e67d2ce1aea5adde342"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22.rs","sha256":"7ba7864eb7e73789bad6c63c64a4dc2dcc08c2da6921375fb9564fca230c2780"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/kernel.rs","sha256":"c97980c1ca2c7ea9cabff9c8d2fb7282773cca180ae15948391c29c9d6196040"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/q55_data.rs","sha256":"af4d23d6b70c45ce6efa839e7dda4bb0a61f6aae43cb805af6fa9b29e6c3bae2"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22_evidence.rs","sha256":"3c5a75b07254c6071a64700af208a64987d0f0ea9698eadc54a9e74585ce1f72"}],"source_negative_controls":43,"universal_algebraic_certificate":{"basis_point_scale_instantiation":10000,"domain":"integers; Q55 scale Q>0; anchor L>=D>=0; lighter monotonicity L2>=L1>D>=0; darker monotonicity L>D2>=D1>=0; current/baseline denominators b,q>0; basis-point scale B>0 instantiated as 10000; p>0; a>=0; 0<=drop_bps<=B","identities":["three explicit anchor-surplus formulas after denominator clearing","reference distance is monotone increasing in lighter L","reference distance is monotone decreasing in darker D","positive-baseline retained threshold is p*(B-drop)/(q*B)","a/b >= p*(B-drop)/(q*B) iff a*q*B >= p*(B-drop)*b"],"method":"exact-sparse-integer-polynomial-identities-plus-positive-denominator-order-lemma-v1","nonpositive_baseline_case":"max(baseline,0)=0; retained threshold is exactly zero","symbolic_mutation_controls":{"anchor_coefficients_and_denominator":6,"retained_cross_product":5},"wolfram_language_cross_check":{"query":"FullSimplify[{20 g/d - 0 == 20 g/d, 20 g/d - 2 == (20 g - 2 d)/d, 20 g/d - 7/2 == (40 g - 7 d)/(2 d), Equivalent[a/b >= p (s-x)/(q s), a q s >= p (s-x) b], Max[p/q, 0] (s-x)/s == Piecewise[{{0, p <= 0}}, p (s-x)/(q s)]}, Assumptions -> Element[{a,b,p,q,s,x,g,d}, Integers] && a >= 0 && b > 0 && q > 0 && s > 0 && 0 <= x <= s && d > 0 && g >= 0]","query_sha256":"8cdbb9964583030c8b92498961896cb2a98613f1cb31eb7c54acdf8e16beff10","result":"{True, True, True, True, True}","result_sha256":"13a8f2ee8d0fde335a638e46d7cc8a8427b9a1437c77d22cfcf925bb87fa6303"}},"verifier_sha256":"3c067cb4ce2af0d9ff514ba0937650ebd62514bb8d6bafb7fc1bc319e0e1816a"} +{"artifact_id":"wcag22-srgb8-luminance-q55-v1","basis_point_proof":{"checks":30,"drop_all_semantics":"zero required surplus; current must still meet the anchor","drop_domain_inclusive":[0,10000],"nonpositive_baseline_semantics":"zero required surplus; current must meet the anchor"},"bound_id":"point-support-reference-surplus-q55-bps-v1","certified_claim":"for every successfully evaluated enabled stability cell, decision is Retained iff current_lower_surplus >= (10000-drop_bps)/10000 * max(baseline_lower_surplus,0); the declared anchor remains a separate hard floor","comparator_proof":{"algorithm":"euclidean-continued-fraction-ordering-v1","dense_denominator_inclusive":[1,31],"dense_numerator_inclusive":[0,31],"dense_small_cases":984064,"invariant":"equal integer parts; reciprocal proper fractions reverse order","largest_fibonacci_index":186,"oracle":"unbounded-integer-cross-product","random_cases":250000,"random_corpus_sha256":"97c4af7b452b31a4ab92645f70c17acb38bf57ca55484e32ad9d7d79d97a333d","random_seed":210583930,"termination":"each nonterminal denominator becomes a strictly smaller remainder","u128_adversarial_cases":190},"declared_operation_law":"q55-lower-reference-distance-explicit-anchor-bps-retention-v1","excluded_claim":"does not certify retention against the unknown exact baseline surplus, renderer equivalence outside encoded-sRGB8 source-over, or a successful result when evaluation fails","integer_replay_envelope":{"assumption":"every Q55 luminance upper <= scale + 3","i128_max":170141183460469231731687303715884105727,"offset_cleared_denominator_max":756604737398243388,"positive_baseline_numerator_max":1188950301625811064,"rational_denominator_max":1513209474796486776,"required_denominator_max":15132094747964867760000,"required_numerator_max":11889503016258110640000,"signed_anchor_abs_coarse_max":5296233161787703716,"u128_max":340282366920938463463374607431768211455,"u64_max":18446744073709551615},"profile_id":"srgb8-q55-retained-reference-surplus-bps-v1","proof_id":"point-support-reference-surplus-integer-v1","proof_payload_sha256":"491f909d707f002a8fe2b6b04337ba6f7d044fd5e2b85585a8c978feb930b36c","q55_dependency":{"artifact_id":"wcag22-srgb8-luminance-q55-v1","artifact_sha256":"7ff239d9052b346f3c50da01ca65ca2330892ed1a3ff30e190797fcef6f03604","maximum_luminance_upper":36028797018963971,"outward_interval_width_bound":3,"proof_id":"wcag22-srgb8-full-domain-q55-v1","proof_payload_sha256":"3c639a7c875046c46b56b51ecdd67d5ecaf14a1134490c88a222e7037b63c0f2","proof_sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd","q55_scale":36028797018963968},"reference_and_anchor_proof":{"anchor_identity_checks":75,"orientation_law":"distance-magnitude-symmetric-orientation-reported-separately","overlap_lower_distance":"0/1","separated_endpoint_checks":504},"schema_version":2,"site_id":"point-support-retained-reference-surplus-v1","source_binding_exclusions":["whole-crate compilation or compiler/toolchain attestation","binary, package, FFI, renderer, or browser transport attestation","unrelated Lab Colors modules outside the declared point-support semantic cone"],"source_binding_law":"point-support-rust-whole-file-semantic-cone-v2","source_binding_schema_version":2,"source_binding_scope":"exact bytes of the private point-support Rust semantic cone and its two WCAG include_str inputs; comments and cfg(test) text are intentionally significant","source_closure_sha256":"b25636d4ae969d4a93a82a1324fb4445ba4861a51f18a85c4a0a062afcfbbfda","source_files":[{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-q55-proof-v1.json","sha256":"ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"},{"kind":"compile-time-input","path":"crates/labcolors-core/contracts/wcag22-srgb8-v1.json","sha256":"b4bb7e5f17a99f2c911fdbe3da23a48b049277b796291094950f14680cc3cc7b"},{"kind":"rust-source","path":"crates/labcolors-core/src/appearance.rs","sha256":"09be54900efe29ffdac8705efd0d6d613055c90d634446ca4b228f51a63997d0"},{"kind":"rust-source","path":"crates/labcolors-core/src/composition.rs","sha256":"195a67327a3bd86d7816b634481389930bf68577bb1202fad14c2ea152df8625"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/exact.rs","sha256":"892576a8621185352583e63dc0a1aacac32e32a8063b6fe24ae16d4ff9dce7cb"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/mod.rs","sha256":"e73b9c0b8c3a4112cb53987753d5a6b5f639774b0b872afaea9836e6647a2f7d"},{"kind":"rust-source","path":"crates/labcolors-core/src/constraints/wcag22.rs","sha256":"856093c91159d8b3faab001f2d6524d33d7b16458a5a4e98ea65f8c62ab2694c"},{"kind":"rust-source","path":"crates/labcolors-core/src/hash.rs","sha256":"f97a0fd7d6ad3162f0f1dfb326fccfb7ed40da9a8fa67a5b8a239a1ae2ae49c3"},{"kind":"rust-source","path":"crates/labcolors-core/src/lcs_occurrence.rs","sha256":"6f202ad7425a235b9d18caba0c817fc33a2b8e042050a34f5ddff3fd09efc53d"},{"kind":"rust-source","path":"crates/labcolors-core/src/lib.rs","sha256":"b3edb3764119c0b4fd50f52b62cbc07fa81c4bfdf1246b91f20eb3d8d4eebd31"},{"kind":"rust-source","path":"crates/labcolors-core/src/numerics.rs","sha256":"e73a12136494f2ef9aca4e943ab38302c1439f054cecab36a552d35252c164f9"},{"kind":"rust-source","path":"crates/labcolors-core/src/observation.rs","sha256":"0fa29533dab84d51af7993bb04dbef7fb978ca55499ea5e1ade282cbc84e6818"},{"kind":"rust-source","path":"crates/labcolors-core/src/point_support.rs","sha256":"0755210e3e591d7049f293a0f0b7647681631f32feee5ad7d3b3309cffca8f9d"},{"kind":"rust-source","path":"crates/labcolors-core/src/session.rs","sha256":"6cd73600267b148c3e9ecc8d2d623f7f8576aed0e1c4c7c50071e297810b8d4b"},{"kind":"rust-source","path":"crates/labcolors-core/src/srgb8.rs","sha256":"6c95324eb05476f35f75375a9af0b2b4a41b8b2978c46e67d2ce1aea5adde342"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22.rs","sha256":"7ba7864eb7e73789bad6c63c64a4dc2dcc08c2da6921375fb9564fca230c2780"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/kernel.rs","sha256":"c97980c1ca2c7ea9cabff9c8d2fb7282773cca180ae15948391c29c9d6196040"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22/q55_data.rs","sha256":"af4d23d6b70c45ce6efa839e7dda4bb0a61f6aae43cb805af6fa9b29e6c3bae2"},{"kind":"rust-source","path":"crates/labcolors-core/src/wcag22_evidence.rs","sha256":"3c5a75b07254c6071a64700af208a64987d0f0ea9698eadc54a9e74585ce1f72"}],"source_negative_controls":43,"universal_algebraic_certificate":{"basis_point_scale_instantiation":10000,"domain":"integers; Q55 scale Q>0; anchor L>=D>=0; lighter monotonicity L2>=L1>D>=0; darker monotonicity L>D2>=D1>=0; current/baseline denominators b,q>0; basis-point scale B>0 instantiated as 10000; p>0; a>=0; 0<=drop_bps<=B","identities":["three explicit anchor-surplus formulas after denominator clearing","reference distance is monotone increasing in lighter L","reference distance is monotone decreasing in darker D","positive-baseline retained threshold is p*(B-drop)/(q*B)","a/b >= p*(B-drop)/(q*B) iff a*q*B >= p*(B-drop)*b"],"method":"exact-sparse-integer-polynomial-identities-plus-positive-denominator-order-lemma-v1","nonpositive_baseline_case":"max(baseline,0)=0; retained threshold is exactly zero","symbolic_mutation_controls":{"anchor_coefficients_and_denominator":6,"retained_cross_product":5},"wolfram_language_cross_check":{"query":"FullSimplify[{20 g/d - 0 == 20 g/d, 20 g/d - 2 == (20 g - 2 d)/d, 20 g/d - 7/2 == (40 g - 7 d)/(2 d), Equivalent[a/b >= p (s-x)/(q s), a q s >= p (s-x) b], Max[p/q, 0] (s-x)/s == Piecewise[{{0, p <= 0}}, p (s-x)/(q s)]}, Assumptions -> Element[{a,b,p,q,s,x,g,d}, Integers] && a >= 0 && b > 0 && q > 0 && s > 0 && 0 <= x <= s && d > 0 && g >= 0]","query_sha256":"8cdbb9964583030c8b92498961896cb2a98613f1cb31eb7c54acdf8e16beff10","result":"{True, True, True, True, True}","result_sha256":"13a8f2ee8d0fde335a638e46d7cc8a8427b9a1437c77d22cfcf925bb87fa6303"}},"verifier_sha256":"16c7fa1a3b1dee795b4434329ebf329066468eeb309213960e35698255b0dd9c"} diff --git a/scripts/verify_point_support_surplus.py b/scripts/verify_point_support_surplus.py index 608c6cdb..084cfd3b 100755 --- a/scripts/verify_point_support_surplus.py +++ b/scripts/verify_point_support_surplus.py @@ -58,7 +58,7 @@ SOURCE_BINDING_LAW = "point-support-rust-whole-file-semantic-cone-v2" SOURCE_BINDING_DOMAIN = b"labcolors.point-support.rust-whole-file-semantic-cone.v2" EXPECTED_SOURCE_CAPSULE_SHA256 = ( - "246c77dbf4923aca4cdaedb12be910ceb2885c94e4e42f652cdcba9b9417a427" + "b25636d4ae969d4a93a82a1324fb4445ba4861a51f18a85c4a0a062afcfbbfda" ) EXPECTED_Q55_PROOF_SHA256 = ( "ac59cf89503170c789223b91d775213a19d4e571ef930f2ea609fcd51b14defd"