1use std::{collections::BTreeSet, marker::PhantomData};
11
12use omena_abstract_value::{AbstractPropertyValueV0, CascadeValueFamilyV0};
13use omena_cascade::{
14 CascadeDeclaration, CascadeRefinementContextV0,
15 refine_declaration_in_context as refine_cascade_declaration_in_context,
16};
17use omena_refinement_trait::{
18 PropertyIndexV0, REFINEMENT_FEATURE_GATE_V0, REFINEMENT_LAYER_MARKER_V0,
19 REFINEMENT_SCHEMA_VERSION_V0, RefinementPredicateV0, RefinementVerdictV0, RefinementWitnessV0,
20 refinement_provenance_v0, refinement_witness_v0,
21};
22use serde::Serialize;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
25#[serde(rename_all = "camelCase")]
26pub enum AbstractValueShapeV0 {
27 Bottom,
28 Exact,
29 FiniteSet,
30 CustomPropertyReference,
31 Top,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
35#[serde(rename_all = "camelCase")]
36pub struct TopPredicateV0 {
37 pub schema_version: &'static str,
38 pub product: &'static str,
39 pub layer_marker: &'static str,
40 pub feature_gate: &'static str,
41}
42
43impl Default for TopPredicateV0 {
44 fn default() -> Self {
45 Self {
46 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
47 product: "omena-refinement.top-predicate",
48 layer_marker: REFINEMENT_LAYER_MARKER_V0,
49 feature_gate: REFINEMENT_FEATURE_GATE_V0,
50 }
51 }
52}
53
54impl RefinementPredicateV0 for TopPredicateV0 {
55 const PREDICATE_ID: &'static str = "top";
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct AnyPropertyIndexV0 {
61 pub schema_version: &'static str,
62 pub product: &'static str,
63 pub layer_marker: &'static str,
64 pub feature_gate: &'static str,
65}
66
67impl Default for AnyPropertyIndexV0 {
68 fn default() -> Self {
69 Self {
70 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
71 product: "omena-refinement.any-property-index",
72 layer_marker: REFINEMENT_LAYER_MARKER_V0,
73 feature_gate: REFINEMENT_FEATURE_GATE_V0,
74 }
75 }
76}
77
78impl PropertyIndexV0 for AnyPropertyIndexV0 {
79 const PROPERTY_NAME: &'static str = "*";
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
83#[serde(rename_all = "camelCase", bound = "")]
84pub struct RefinedAbstractPropertyValueV0<P: PropertyIndexV0, R: RefinementPredicateV0> {
85 pub schema_version: &'static str,
86 pub product: &'static str,
87 pub layer_marker: &'static str,
88 pub feature_gate: &'static str,
89 pub property_name: &'static str,
90 pub predicate_id: &'static str,
91 pub value_shape: AbstractValueShapeV0,
92 pub legacy_value: AbstractPropertyValueV0,
93 pub strict_superset_of_legacy_v0: bool,
94 #[serde(skip)]
95 marker: PhantomData<(P, R)>,
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
99#[serde(rename_all = "camelCase")]
100pub enum RefinementPropertyPredicateV0 {
101 Any,
102 ExactValue {
103 property_name: String,
104 value: String,
105 },
106 OneOfValues {
107 property_name: String,
108 values: Vec<String>,
109 },
110 CustomPropertyReference {
111 property_name: String,
112 custom_property_name: String,
113 },
114 NumericRange {
115 property_name: String,
116 min_inclusive: Option<i64>,
117 max_inclusive: Option<i64>,
118 unit: Option<String>,
119 },
120 HasPseudoState {
121 property_name: String,
122 pseudo_state: String,
123 },
124 And {
125 predicates: Vec<RefinementPropertyPredicateV0>,
126 },
127 Or {
128 predicates: Vec<RefinementPropertyPredicateV0>,
129 },
130 Not {
131 predicate: Box<RefinementPropertyPredicateV0>,
132 },
133}
134
135#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
136#[serde(rename_all = "camelCase")]
137pub struct RefinementPredicateEvaluationV0 {
138 pub schema_version: &'static str,
139 pub product: &'static str,
140 pub layer_marker: &'static str,
141 pub feature_gate: &'static str,
142 pub predicate_expression_id: String,
143 pub value_shape: AbstractValueShapeV0,
144 pub verdict: RefinementVerdictV0,
145 pub matched_clause_count: usize,
146 pub witness: RefinementWitnessV0,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
150#[serde(rename_all = "camelCase")]
151pub struct RefinementContextSummaryV0 {
152 pub schema_version: &'static str,
153 pub product: &'static str,
154 pub layer_marker: &'static str,
155 pub feature_gate: &'static str,
156 pub predicate_count: usize,
157 pub context_digest: u64,
158 pub witness_provenance_count: usize,
159 pub downstream_invalidation_required: bool,
160}
161
162#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
168#[serde(rename_all = "camelCase")]
169pub struct CascadeDimensionalRefinementBridgeV0 {
170 pub schema_version: &'static str,
171 pub product: &'static str,
172 pub layer_marker: &'static str,
173 pub feature_gate: &'static str,
174 pub claim_level: &'static str,
175 pub property_name: String,
176 pub cascade_family_product: &'static str,
177 pub predicate_count: usize,
178 pub context_value_count: usize,
179 pub restriction_map_count: usize,
180 pub context_evaluation_count: usize,
181 pub satisfied_all_context_count: usize,
182 pub satisfied_some_context_count: usize,
183 pub unknown_context_count: usize,
184 pub unsatisfiable_context_count: usize,
185 pub witness_provenance_count: usize,
186 pub property_consistent: bool,
187 pub uses_existing_abstract_property_value_substrate: bool,
188 pub uses_existing_cascade_family_substrate: bool,
189 pub uses_existing_refinement_predicate_substrate: bool,
190 pub forks_unit_system: bool,
191 pub liquid_haskell_complete: bool,
192 pub smt_backend_available: bool,
193 pub smt_complete: bool,
194 pub theorem_claimed: bool,
195 pub product_path_evidence_ready: bool,
196 pub stronger_type_safety_claim_ready: bool,
197 pub evaluations: Vec<CascadeDimensionalRefinementContextEvaluationV0>,
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
201#[serde(rename_all = "camelCase")]
202pub struct CascadeDimensionalRefinementContextEvaluationV0 {
203 pub schema_version: &'static str,
204 pub product: &'static str,
205 pub layer_marker: &'static str,
206 pub feature_gate: &'static str,
207 pub context_id: String,
208 pub selector_count: usize,
209 pub condition_count: usize,
210 pub layer_count: usize,
211 pub value_shape: AbstractValueShapeV0,
212 pub combined_verdict: RefinementVerdictV0,
213 pub predicate_evaluation_count: usize,
214 pub matched_clause_count: usize,
215 pub witness_provenance_count: usize,
216 pub predicate_expression_ids: Vec<String>,
217}
218
219pub fn project_legacy_to_refined_v0<P, R>(
220 legacy_value: AbstractPropertyValueV0,
221) -> RefinedAbstractPropertyValueV0<P, R>
222where
223 P: PropertyIndexV0,
224 R: RefinementPredicateV0,
225{
226 let mut refined = RefinedAbstractPropertyValueV0 {
227 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
228 product: "omena-refinement.refined-abstract-property-value",
229 layer_marker: REFINEMENT_LAYER_MARKER_V0,
230 feature_gate: REFINEMENT_FEATURE_GATE_V0,
231 property_name: P::PROPERTY_NAME,
232 predicate_id: R::PREDICATE_ID,
233 value_shape: abstract_property_value_shape_v0(&legacy_value),
234 legacy_value,
235 strict_superset_of_legacy_v0: false,
236 marker: PhantomData,
237 };
238 refined.strict_superset_of_legacy_v0 =
239 refined_projection_preserves_legacy_value_v0::<P, R>(&refined);
240 refined
241}
242
243pub fn project_refined_to_legacy_v0<P, R>(
244 refined: &RefinedAbstractPropertyValueV0<P, R>,
245) -> AbstractPropertyValueV0
246where
247 P: PropertyIndexV0,
248 R: RefinementPredicateV0,
249{
250 refined.legacy_value.clone()
251}
252
253pub fn refined_projection_preserves_legacy_value_v0<P, R>(
254 refined: &RefinedAbstractPropertyValueV0<P, R>,
255) -> bool
256where
257 P: PropertyIndexV0,
258 R: RefinementPredicateV0,
259{
260 refined.schema_version == REFINEMENT_SCHEMA_VERSION_V0
261 && refined.layer_marker == REFINEMENT_LAYER_MARKER_V0
262 && refined.feature_gate == REFINEMENT_FEATURE_GATE_V0
263 && refined.property_name == P::PROPERTY_NAME
264 && refined.predicate_id == R::PREDICATE_ID
265 && abstract_property_value_shape_v0(&project_refined_to_legacy_v0(refined))
266 == refined.value_shape
267}
268
269pub fn evaluate_refinement_property_predicate_v0(
270 predicate: &RefinementPropertyPredicateV0,
271 value: &AbstractPropertyValueV0,
272) -> RefinementPredicateEvaluationV0 {
273 let verdict = evaluate_refinement_predicate_verdict_v0(predicate, value);
274 let matched_clause_count = count_satisfied_refinement_clauses_v0(predicate, value);
275 let predicate_expression_id = refinement_predicate_expression_id_v0(predicate);
276 let witness = refinement_witness_v0(
277 "property-grammar",
278 verdict,
279 refinement_predicate_provenance_v0(predicate),
280 );
281
282 RefinementPredicateEvaluationV0 {
283 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
284 product: "omena-refinement.property-predicate-evaluation",
285 layer_marker: REFINEMENT_LAYER_MARKER_V0,
286 feature_gate: REFINEMENT_FEATURE_GATE_V0,
287 predicate_expression_id,
288 value_shape: abstract_property_value_shape_v0(value),
289 verdict,
290 matched_clause_count,
291 witness,
292 }
293}
294
295pub fn refine_declaration_in_context(
296 declaration: &CascadeDeclaration,
297 context: &CascadeRefinementContextV0,
298) -> RefinementWitnessV0 {
299 refine_cascade_declaration_in_context(declaration, context)
300}
301
302pub fn summarize_refinement_context_v0(
303 predicates: &[RefinementPropertyPredicateV0],
304) -> RefinementContextSummaryV0 {
305 let mut expression_ids = predicates
306 .iter()
307 .map(refinement_predicate_expression_id_v0)
308 .collect::<Vec<_>>();
309 expression_ids.sort();
310
311 let witness_provenance_count = predicates
312 .iter()
313 .flat_map(refinement_predicate_provenance_v0)
314 .map(|provenance| provenance.source)
315 .collect::<std::collections::BTreeSet<_>>()
316 .len();
317 let context_digest = deterministic_refinement_digest_v0(expression_ids.join("\n").as_bytes());
318
319 RefinementContextSummaryV0 {
320 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
321 product: "omena-refinement.context-summary",
322 layer_marker: REFINEMENT_LAYER_MARKER_V0,
323 feature_gate: REFINEMENT_FEATURE_GATE_V0,
324 predicate_count: predicates.len(),
325 context_digest,
326 witness_provenance_count,
327 downstream_invalidation_required: !predicates.is_empty(),
328 }
329}
330
331pub fn summarize_cascade_dimensional_refinement_bridge_v0(
332 family: &CascadeValueFamilyV0,
333 predicates: &[RefinementPropertyPredicateV0],
334) -> CascadeDimensionalRefinementBridgeV0 {
335 let mut global_provenance_sources = BTreeSet::new();
336 let mut evaluations = family
337 .members
338 .iter()
339 .map(|member| {
340 let predicate_evaluations = predicates
341 .iter()
342 .map(|predicate| {
343 evaluate_refinement_property_predicate_v0(predicate, &member.value)
344 })
345 .collect::<Vec<_>>();
346 let verdicts = predicate_evaluations
347 .iter()
348 .map(|evaluation| evaluation.verdict)
349 .collect::<Vec<_>>();
350 let combined_verdict = combine_and_refinement_verdicts_v0(&verdicts);
351 let mut context_provenance_sources = BTreeSet::new();
352 for evaluation in &predicate_evaluations {
353 for provenance in &evaluation.witness.provenance {
354 context_provenance_sources.insert(provenance.source);
355 global_provenance_sources.insert(provenance.source);
356 }
357 }
358
359 CascadeDimensionalRefinementContextEvaluationV0 {
360 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
361 product: "omena-refinement.cascade-dimensional-refinement-context-evaluation",
362 layer_marker: REFINEMENT_LAYER_MARKER_V0,
363 feature_gate: REFINEMENT_FEATURE_GATE_V0,
364 context_id: member.context.id.clone(),
365 selector_count: member.context.selectors.len(),
366 condition_count: member.context.conditions.len(),
367 layer_count: member.context.layers.len(),
368 value_shape: abstract_property_value_shape_v0(&member.value),
369 combined_verdict,
370 predicate_evaluation_count: predicate_evaluations.len(),
371 matched_clause_count: predicate_evaluations
372 .iter()
373 .map(|evaluation| evaluation.matched_clause_count)
374 .sum(),
375 witness_provenance_count: context_provenance_sources.len(),
376 predicate_expression_ids: predicate_evaluations
377 .into_iter()
378 .map(|evaluation| evaluation.predicate_expression_id)
379 .collect(),
380 }
381 })
382 .collect::<Vec<_>>();
383 evaluations.sort_by(|left, right| left.context_id.cmp(&right.context_id));
384
385 CascadeDimensionalRefinementBridgeV0 {
386 schema_version: REFINEMENT_SCHEMA_VERSION_V0,
387 product: "omena-refinement.cascade-dimensional-refinement-bridge",
388 layer_marker: REFINEMENT_LAYER_MARKER_V0,
389 feature_gate: REFINEMENT_FEATURE_GATE_V0,
390 claim_level: "m6DimensionalRefinementBridgeSubstrate",
391 property_name: family.property_name.clone(),
392 cascade_family_product: family.product,
393 predicate_count: predicates.len(),
394 context_value_count: family.context_value_count,
395 restriction_map_count: family.restriction_map_count,
396 context_evaluation_count: evaluations.len(),
397 satisfied_all_context_count: count_context_verdicts_v0(
398 &evaluations,
399 RefinementVerdictV0::SatisfiedAll,
400 ),
401 satisfied_some_context_count: count_context_verdicts_v0(
402 &evaluations,
403 RefinementVerdictV0::SatisfiedSome,
404 ),
405 unknown_context_count: count_context_verdicts_v0(
406 &evaluations,
407 RefinementVerdictV0::Unknown,
408 ),
409 unsatisfiable_context_count: count_context_verdicts_v0(
410 &evaluations,
411 RefinementVerdictV0::Unsatisfiable,
412 ),
413 witness_provenance_count: global_provenance_sources.len(),
414 property_consistent: family.property_consistent,
415 uses_existing_abstract_property_value_substrate: true,
416 uses_existing_cascade_family_substrate: true,
417 uses_existing_refinement_predicate_substrate: true,
418 forks_unit_system: false,
419 liquid_haskell_complete: false,
420 smt_backend_available: refinement_smt_backend_available_v0(),
421 smt_complete: false,
422 theorem_claimed: false,
423 product_path_evidence_ready: true,
424 stronger_type_safety_claim_ready: false,
425 evaluations,
426 }
427}
428
429pub fn abstract_property_value_shape_v0(value: &AbstractPropertyValueV0) -> AbstractValueShapeV0 {
430 match value {
431 AbstractPropertyValueV0::Bottom { .. } => AbstractValueShapeV0::Bottom,
432 AbstractPropertyValueV0::Exact { .. } => AbstractValueShapeV0::Exact,
433 AbstractPropertyValueV0::FiniteSet { .. } => AbstractValueShapeV0::FiniteSet,
434 AbstractPropertyValueV0::CustomPropertyReference { .. } => {
435 AbstractValueShapeV0::CustomPropertyReference
436 }
437 AbstractPropertyValueV0::Top { .. } => AbstractValueShapeV0::Top,
438 }
439}
440
441fn count_context_verdicts_v0(
442 evaluations: &[CascadeDimensionalRefinementContextEvaluationV0],
443 verdict: RefinementVerdictV0,
444) -> usize {
445 evaluations
446 .iter()
447 .filter(|evaluation| evaluation.combined_verdict == verdict)
448 .count()
449}
450
451fn evaluate_refinement_predicate_verdict_v0(
452 predicate: &RefinementPropertyPredicateV0,
453 value: &AbstractPropertyValueV0,
454) -> RefinementVerdictV0 {
455 match predicate {
456 RefinementPropertyPredicateV0::Any => RefinementVerdictV0::SatisfiedAll,
457 RefinementPropertyPredicateV0::ExactValue {
458 property_name,
459 value: expected,
460 } => evaluate_exact_value_predicate_v0(property_name, expected, value),
461 RefinementPropertyPredicateV0::OneOfValues {
462 property_name,
463 values,
464 } => evaluate_one_of_values_predicate_v0(property_name, values, value),
465 RefinementPropertyPredicateV0::CustomPropertyReference {
466 property_name,
467 custom_property_name,
468 } => evaluate_custom_property_reference_predicate_v0(
469 property_name,
470 custom_property_name,
471 value,
472 ),
473 RefinementPropertyPredicateV0::NumericRange {
474 property_name,
475 min_inclusive,
476 max_inclusive,
477 unit,
478 } => evaluate_numeric_range_predicate_v0(
479 property_name,
480 *min_inclusive,
481 *max_inclusive,
482 unit.as_deref(),
483 value,
484 ),
485 RefinementPropertyPredicateV0::HasPseudoState {
486 property_name,
487 pseudo_state,
488 } => evaluate_pseudo_state_predicate_v0(property_name, pseudo_state, value),
489 RefinementPropertyPredicateV0::And { predicates } => combine_and_refinement_verdicts_v0(
490 &predicates
491 .iter()
492 .map(|predicate| evaluate_refinement_predicate_verdict_v0(predicate, value))
493 .collect::<Vec<_>>(),
494 ),
495 RefinementPropertyPredicateV0::Or { predicates } => combine_or_refinement_verdicts_v0(
496 &predicates
497 .iter()
498 .map(|predicate| evaluate_refinement_predicate_verdict_v0(predicate, value))
499 .collect::<Vec<_>>(),
500 ),
501 RefinementPropertyPredicateV0::Not { predicate } => {
502 match evaluate_refinement_predicate_verdict_v0(predicate, value) {
503 RefinementVerdictV0::SatisfiedAll => RefinementVerdictV0::Unsatisfiable,
504 RefinementVerdictV0::Unsatisfiable => RefinementVerdictV0::SatisfiedAll,
505 RefinementVerdictV0::SatisfiedSome | RefinementVerdictV0::Unknown => {
506 RefinementVerdictV0::Unknown
507 }
508 }
509 }
510 }
511}
512
513fn evaluate_numeric_range_predicate_v0(
514 property_name: &str,
515 min_inclusive: Option<i64>,
516 max_inclusive: Option<i64>,
517 unit: Option<&str>,
518 value: &AbstractPropertyValueV0,
519) -> RefinementVerdictV0 {
520 match value {
521 AbstractPropertyValueV0::Exact {
522 property_name: actual_property,
523 value: actual_value,
524 ..
525 } if actual_property == property_name => {
526 if numeric_range_contains_value_v0(actual_value, min_inclusive, max_inclusive, unit) {
527 RefinementVerdictV0::SatisfiedAll
528 } else {
529 RefinementVerdictV0::Unsatisfiable
530 }
531 }
532 AbstractPropertyValueV0::FiniteSet {
533 property_name: actual_property,
534 values,
535 ..
536 } if actual_property == property_name => {
537 let matched = values
538 .iter()
539 .filter(|candidate| {
540 numeric_range_contains_value_v0(candidate, min_inclusive, max_inclusive, unit)
541 })
542 .count();
543 if matched == values.len() {
544 RefinementVerdictV0::SatisfiedAll
545 } else if matched > 0 {
546 RefinementVerdictV0::SatisfiedSome
547 } else {
548 RefinementVerdictV0::Unsatisfiable
549 }
550 }
551 AbstractPropertyValueV0::Top {
552 property_name: actual_property,
553 }
554 | AbstractPropertyValueV0::CustomPropertyReference {
555 property_name: actual_property,
556 ..
557 } if actual_property == property_name => RefinementVerdictV0::Unknown,
558 _ => RefinementVerdictV0::Unsatisfiable,
559 }
560}
561
562fn evaluate_pseudo_state_predicate_v0(
563 property_name: &str,
564 expected_pseudo_state: &str,
565 value: &AbstractPropertyValueV0,
566) -> RefinementVerdictV0 {
567 match value {
568 AbstractPropertyValueV0::Exact {
569 property_name: actual_property,
570 pseudo_state,
571 ..
572 }
573 | AbstractPropertyValueV0::CustomPropertyReference {
574 property_name: actual_property,
575 pseudo_state,
576 ..
577 } if actual_property == property_name => {
578 if pseudo_state.as_deref() == Some(expected_pseudo_state) {
579 RefinementVerdictV0::SatisfiedAll
580 } else {
581 RefinementVerdictV0::Unsatisfiable
582 }
583 }
584 AbstractPropertyValueV0::FiniteSet {
585 property_name: actual_property,
586 pseudo_states,
587 ..
588 } if actual_property == property_name => {
589 if pseudo_states.len() == 1
590 && pseudo_states
591 .iter()
592 .any(|pseudo_state| pseudo_state == expected_pseudo_state)
593 {
594 RefinementVerdictV0::SatisfiedAll
595 } else if pseudo_states
596 .iter()
597 .any(|pseudo_state| pseudo_state == expected_pseudo_state)
598 {
599 RefinementVerdictV0::SatisfiedSome
600 } else {
601 RefinementVerdictV0::Unsatisfiable
602 }
603 }
604 AbstractPropertyValueV0::Top {
605 property_name: actual_property,
606 } if actual_property == property_name => RefinementVerdictV0::Unknown,
607 _ => RefinementVerdictV0::Unsatisfiable,
608 }
609}
610
611fn evaluate_exact_value_predicate_v0(
612 property_name: &str,
613 expected: &str,
614 value: &AbstractPropertyValueV0,
615) -> RefinementVerdictV0 {
616 match value {
617 AbstractPropertyValueV0::Exact {
618 property_name: actual_property,
619 value: actual_value,
620 ..
621 } if actual_property == property_name && actual_value == expected => {
622 RefinementVerdictV0::SatisfiedAll
623 }
624 AbstractPropertyValueV0::FiniteSet {
625 property_name: actual_property,
626 values,
627 ..
628 } if actual_property == property_name && values.iter().any(|value| value == expected) => {
629 if values.len() == 1 {
630 RefinementVerdictV0::SatisfiedAll
631 } else {
632 RefinementVerdictV0::SatisfiedSome
633 }
634 }
635 AbstractPropertyValueV0::Top {
636 property_name: actual_property,
637 }
638 | AbstractPropertyValueV0::CustomPropertyReference {
639 property_name: actual_property,
640 ..
641 } if actual_property == property_name => RefinementVerdictV0::Unknown,
642 _ => RefinementVerdictV0::Unsatisfiable,
643 }
644}
645
646fn evaluate_one_of_values_predicate_v0(
647 property_name: &str,
648 expected_values: &[String],
649 value: &AbstractPropertyValueV0,
650) -> RefinementVerdictV0 {
651 match value {
652 AbstractPropertyValueV0::Exact {
653 property_name: actual_property,
654 value: actual_value,
655 ..
656 } if actual_property == property_name => {
657 if expected_values.contains(actual_value) {
658 RefinementVerdictV0::SatisfiedAll
659 } else {
660 RefinementVerdictV0::Unsatisfiable
661 }
662 }
663 AbstractPropertyValueV0::FiniteSet {
664 property_name: actual_property,
665 values,
666 ..
667 } if actual_property == property_name => {
668 let matched = values
669 .iter()
670 .filter(|value| expected_values.contains(*value))
671 .count();
672 if matched == values.len() {
673 RefinementVerdictV0::SatisfiedAll
674 } else if matched > 0 {
675 RefinementVerdictV0::SatisfiedSome
676 } else {
677 RefinementVerdictV0::Unsatisfiable
678 }
679 }
680 AbstractPropertyValueV0::Top {
681 property_name: actual_property,
682 }
683 | AbstractPropertyValueV0::CustomPropertyReference {
684 property_name: actual_property,
685 ..
686 } if actual_property == property_name => RefinementVerdictV0::Unknown,
687 _ => RefinementVerdictV0::Unsatisfiable,
688 }
689}
690
691fn evaluate_custom_property_reference_predicate_v0(
692 property_name: &str,
693 expected_custom_property: &str,
694 value: &AbstractPropertyValueV0,
695) -> RefinementVerdictV0 {
696 match value {
697 AbstractPropertyValueV0::CustomPropertyReference {
698 property_name: actual_property,
699 custom_property_name,
700 ..
701 } if actual_property == property_name
702 && custom_property_name == expected_custom_property =>
703 {
704 RefinementVerdictV0::SatisfiedAll
705 }
706 AbstractPropertyValueV0::Top {
707 property_name: actual_property,
708 } if actual_property == property_name => RefinementVerdictV0::Unknown,
709 _ => RefinementVerdictV0::Unsatisfiable,
710 }
711}
712
713fn combine_and_refinement_verdicts_v0(verdicts: &[RefinementVerdictV0]) -> RefinementVerdictV0 {
714 if verdicts.is_empty()
715 || verdicts
716 .iter()
717 .all(|verdict| *verdict == RefinementVerdictV0::SatisfiedAll)
718 {
719 RefinementVerdictV0::SatisfiedAll
720 } else if verdicts.contains(&RefinementVerdictV0::Unsatisfiable) {
721 RefinementVerdictV0::Unsatisfiable
722 } else if verdicts.contains(&RefinementVerdictV0::SatisfiedAll)
723 || verdicts.contains(&RefinementVerdictV0::SatisfiedSome)
724 {
725 RefinementVerdictV0::SatisfiedSome
726 } else {
727 RefinementVerdictV0::Unknown
728 }
729}
730
731fn combine_or_refinement_verdicts_v0(verdicts: &[RefinementVerdictV0]) -> RefinementVerdictV0 {
732 if verdicts.is_empty() || verdicts.contains(&RefinementVerdictV0::SatisfiedAll) {
733 RefinementVerdictV0::SatisfiedAll
734 } else if verdicts.contains(&RefinementVerdictV0::SatisfiedSome) {
735 RefinementVerdictV0::SatisfiedSome
736 } else if verdicts
737 .iter()
738 .all(|verdict| *verdict == RefinementVerdictV0::Unsatisfiable)
739 {
740 RefinementVerdictV0::Unsatisfiable
741 } else {
742 RefinementVerdictV0::Unknown
743 }
744}
745
746fn count_satisfied_refinement_clauses_v0(
747 predicate: &RefinementPropertyPredicateV0,
748 value: &AbstractPropertyValueV0,
749) -> usize {
750 match predicate {
751 RefinementPropertyPredicateV0::And { predicates }
752 | RefinementPropertyPredicateV0::Or { predicates } => predicates
753 .iter()
754 .map(|predicate| count_satisfied_refinement_clauses_v0(predicate, value))
755 .sum(),
756 RefinementPropertyPredicateV0::Not { predicate } => usize::from(matches!(
757 evaluate_refinement_predicate_verdict_v0(predicate, value),
758 RefinementVerdictV0::Unsatisfiable
759 )),
760 _ => usize::from(matches!(
761 evaluate_refinement_predicate_verdict_v0(predicate, value),
762 RefinementVerdictV0::SatisfiedAll | RefinementVerdictV0::SatisfiedSome
763 )),
764 }
765}
766
767fn refinement_predicate_expression_id_v0(predicate: &RefinementPropertyPredicateV0) -> String {
768 match predicate {
769 RefinementPropertyPredicateV0::Any => "any".to_string(),
770 RefinementPropertyPredicateV0::ExactValue {
771 property_name,
772 value,
773 } => format!("exact:{property_name}:{value}"),
774 RefinementPropertyPredicateV0::OneOfValues {
775 property_name,
776 values,
777 } => format!("one-of:{property_name}:{}", values.join("|")),
778 RefinementPropertyPredicateV0::CustomPropertyReference {
779 property_name,
780 custom_property_name,
781 } => format!("custom-ref:{property_name}:{custom_property_name}"),
782 RefinementPropertyPredicateV0::NumericRange {
783 property_name,
784 min_inclusive,
785 max_inclusive,
786 unit,
787 } => format!(
788 "numeric-range:{property_name}:{}..{}:{}",
789 min_inclusive
790 .map(|value| value.to_string())
791 .unwrap_or_else(|| "-inf".to_string()),
792 max_inclusive
793 .map(|value| value.to_string())
794 .unwrap_or_else(|| "inf".to_string()),
795 unit.as_deref().unwrap_or("*")
796 ),
797 RefinementPropertyPredicateV0::HasPseudoState {
798 property_name,
799 pseudo_state,
800 } => format!("pseudo-state:{property_name}:{pseudo_state}"),
801 RefinementPropertyPredicateV0::And { predicates } => format!(
802 "and({})",
803 predicates
804 .iter()
805 .map(refinement_predicate_expression_id_v0)
806 .collect::<Vec<_>>()
807 .join(",")
808 ),
809 RefinementPropertyPredicateV0::Or { predicates } => format!(
810 "or({})",
811 predicates
812 .iter()
813 .map(refinement_predicate_expression_id_v0)
814 .collect::<Vec<_>>()
815 .join(",")
816 ),
817 RefinementPropertyPredicateV0::Not { predicate } => {
818 format!("not({})", refinement_predicate_expression_id_v0(predicate))
819 }
820 }
821}
822
823fn refinement_predicate_provenance_v0(
824 predicate: &RefinementPropertyPredicateV0,
825) -> Vec<omena_refinement_trait::RefinementProvenanceV0> {
826 let mut provenance = Vec::new();
827 collect_refinement_predicate_provenance_v0(predicate, &mut provenance);
828 provenance
829}
830
831fn collect_refinement_predicate_provenance_v0(
832 predicate: &RefinementPropertyPredicateV0,
833 provenance: &mut Vec<omena_refinement_trait::RefinementProvenanceV0>,
834) {
835 push_refinement_provenance_v0(provenance, "property-grammar", None);
836 match predicate {
837 RefinementPropertyPredicateV0::Any => {}
838 RefinementPropertyPredicateV0::ExactValue { .. }
839 | RefinementPropertyPredicateV0::OneOfValues { .. } => {
840 push_refinement_provenance_v0(provenance, "finite-property-domain", None);
841 }
842 RefinementPropertyPredicateV0::CustomPropertyReference { .. } => {
843 push_refinement_provenance_v0(provenance, "custom-property-reference", None);
844 }
845 RefinementPropertyPredicateV0::NumericRange { .. } => {
846 push_refinement_provenance_v0(provenance, "numeric-range-interval", None);
847 }
848 RefinementPropertyPredicateV0::HasPseudoState { .. } => {
849 push_refinement_provenance_v0(provenance, "pseudo-state-refinement", None);
850 }
851 RefinementPropertyPredicateV0::And { predicates }
852 | RefinementPropertyPredicateV0::Or { predicates } => {
853 push_refinement_provenance_v0(provenance, "predicate-composition", None);
854 for predicate in predicates {
855 collect_refinement_predicate_provenance_v0(predicate, provenance);
856 }
857 }
858 RefinementPropertyPredicateV0::Not { predicate } => {
859 push_refinement_provenance_v0(provenance, "predicate-composition", None);
860 collect_refinement_predicate_provenance_v0(predicate, provenance);
861 }
862 }
863}
864
865fn push_refinement_provenance_v0(
866 provenance: &mut Vec<omena_refinement_trait::RefinementProvenanceV0>,
867 source: &'static str,
868 legacy_proof_primitive: Option<&'static str>,
869) {
870 if provenance.iter().any(|entry| {
871 entry.source == source && entry.legacy_proof_primitive == legacy_proof_primitive
872 }) {
873 return;
874 }
875 provenance.push(refinement_provenance_v0(source, legacy_proof_primitive));
876}
877
878fn numeric_range_contains_value_v0(
879 value: &str,
880 min_inclusive: Option<i64>,
881 max_inclusive: Option<i64>,
882 expected_unit: Option<&str>,
883) -> bool {
884 let Some((magnitude, unit)) = parse_css_integer_with_unit_v0(value) else {
885 return false;
886 };
887 if let Some(expected_unit) = expected_unit
888 && unit != expected_unit
889 {
890 return false;
891 }
892 if let Some(min_inclusive) = min_inclusive
893 && magnitude < min_inclusive
894 {
895 return false;
896 }
897 if let Some(max_inclusive) = max_inclusive
898 && magnitude > max_inclusive
899 {
900 return false;
901 }
902 true
903}
904
905fn parse_css_integer_with_unit_v0(value: &str) -> Option<(i64, &str)> {
906 let trimmed = value.trim();
907 let mut end = 0;
908 for (index, ch) in trimmed.char_indices() {
909 if ch.is_ascii_digit() || (index == 0 && (ch == '-' || ch == '+')) {
910 end = index + ch.len_utf8();
911 } else {
912 break;
913 }
914 }
915 if end == 0 || trimmed[..end].ends_with(['-', '+']) {
916 return None;
917 }
918 let magnitude = trimmed[..end].parse::<i64>().ok()?;
919 Some((magnitude, trimmed[end..].trim()))
920}
921
922fn deterministic_refinement_digest_v0(bytes: &[u8]) -> u64 {
923 bytes.iter().fold(0xcbf29ce484222325, |hash, byte| {
924 (hash ^ u64::from(*byte)).wrapping_mul(0x100000001b3)
925 })
926}
927
928#[cfg(feature = "refinement-smt")]
929pub fn refinement_smt_backend_available_v0() -> bool {
930 let _ = omena_smt::cascade_theory_signature_v0();
931 true
932}
933
934#[cfg(not(feature = "refinement-smt"))]
935pub fn refinement_smt_backend_available_v0() -> bool {
936 false
937}
938
939#[cfg(test)]
940mod tests {
941 use super::*;
942 use omena_abstract_value::{
943 CascadeContextV0, CascadeValueFamilyMemberV0, derive_cascade_restriction_maps_v0,
944 summarize_cascade_value_family_v0,
945 };
946
947 #[test]
948 fn refined_value_round_trips_to_legacy_without_mutating_v0() {
949 let top = TopPredicateV0::default();
950 let any = AnyPropertyIndexV0::default();
951 assert_eq!(top.schema_version, "0");
952 assert_eq!(any.layer_marker, "refinement-cascade");
953
954 let legacy = AbstractPropertyValueV0::Top {
955 property_name: "color".to_string(),
956 };
957 let refined =
958 project_legacy_to_refined_v0::<AnyPropertyIndexV0, TopPredicateV0>(legacy.clone());
959 assert_eq!(refined.schema_version, "0");
960 assert_eq!(refined.layer_marker, "refinement-cascade");
961 assert!(refined.strict_superset_of_legacy_v0);
962 assert!(refined_projection_preserves_legacy_value_v0::<
963 AnyPropertyIndexV0,
964 TopPredicateV0,
965 >(&refined));
966 assert_eq!(project_refined_to_legacy_v0(&refined), legacy);
967 }
968
969 #[test]
970 fn refinement_property_grammar_evaluates_exact_and_one_of_values() {
971 let exact = AbstractPropertyValueV0::Exact {
972 property_name: "display".to_string(),
973 value: "grid".to_string(),
974 pseudo_state: None,
975 };
976 let predicate = RefinementPropertyPredicateV0::OneOfValues {
977 property_name: "display".to_string(),
978 values: vec!["grid".to_string(), "flex".to_string()],
979 };
980 let evaluation = evaluate_refinement_property_predicate_v0(&predicate, &exact);
981
982 assert_eq!(evaluation.schema_version, "0");
983 assert_eq!(
984 evaluation.product,
985 "omena-refinement.property-predicate-evaluation"
986 );
987 assert_eq!(evaluation.value_shape, AbstractValueShapeV0::Exact);
988 assert_eq!(evaluation.verdict, RefinementVerdictV0::SatisfiedAll);
989 assert_eq!(evaluation.matched_clause_count, 1);
990 assert_eq!(evaluation.witness.predicate_id, "property-grammar");
991 assert!(evaluation.witness.legacy_proofs_byte_untouched);
992 }
993
994 #[test]
995 fn refinement_predicate_composition_tracks_partial_and_negative_witnesses() {
996 let finite = AbstractPropertyValueV0::FiniteSet {
997 property_name: "color".to_string(),
998 values: vec!["red".to_string(), "blue".to_string()],
999 pseudo_states: Vec::new(),
1000 };
1001 let predicate = RefinementPropertyPredicateV0::And {
1002 predicates: vec![
1003 RefinementPropertyPredicateV0::OneOfValues {
1004 property_name: "color".to_string(),
1005 values: vec!["red".to_string()],
1006 },
1007 RefinementPropertyPredicateV0::Not {
1008 predicate: Box::new(RefinementPropertyPredicateV0::ExactValue {
1009 property_name: "color".to_string(),
1010 value: "green".to_string(),
1011 }),
1012 },
1013 ],
1014 };
1015 let evaluation = evaluate_refinement_property_predicate_v0(&predicate, &finite);
1016
1017 assert_eq!(evaluation.value_shape, AbstractValueShapeV0::FiniteSet);
1018 assert_eq!(evaluation.verdict, RefinementVerdictV0::SatisfiedSome);
1019 assert_eq!(evaluation.matched_clause_count, 2);
1020 assert_eq!(
1021 evaluation.predicate_expression_id,
1022 "and(one-of:color:red,not(exact:color:green))"
1023 );
1024 assert_eq!(evaluation.witness.provenance[0].source, "property-grammar");
1025 }
1026
1027 #[test]
1028 fn refinement_custom_property_reference_predicate_is_not_wrapper_only() {
1029 let reference = AbstractPropertyValueV0::CustomPropertyReference {
1030 property_name: "color".to_string(),
1031 custom_property_name: "--brand".to_string(),
1032 pseudo_state: None,
1033 };
1034 let predicate = RefinementPropertyPredicateV0::CustomPropertyReference {
1035 property_name: "color".to_string(),
1036 custom_property_name: "--brand".to_string(),
1037 };
1038 let evaluation = evaluate_refinement_property_predicate_v0(&predicate, &reference);
1039
1040 assert_eq!(evaluation.verdict, RefinementVerdictV0::SatisfiedAll);
1041 assert_eq!(
1042 evaluation.predicate_expression_id,
1043 "custom-ref:color:--brand"
1044 );
1045 }
1046
1047 #[test]
1048 fn refinement_numeric_range_and_pseudo_state_predicates_are_evaluated() {
1049 let finite = AbstractPropertyValueV0::FiniteSet {
1050 property_name: "opacity".to_string(),
1051 values: vec!["0".to_string(), "50%".to_string(), "100%".to_string()],
1052 pseudo_states: vec![":hover".to_string(), ":focus".to_string()],
1053 };
1054 let predicate = RefinementPropertyPredicateV0::And {
1055 predicates: vec![
1056 RefinementPropertyPredicateV0::NumericRange {
1057 property_name: "opacity".to_string(),
1058 min_inclusive: Some(0),
1059 max_inclusive: Some(100),
1060 unit: Some("%".to_string()),
1061 },
1062 RefinementPropertyPredicateV0::HasPseudoState {
1063 property_name: "opacity".to_string(),
1064 pseudo_state: ":hover".to_string(),
1065 },
1066 ],
1067 };
1068 let evaluation = evaluate_refinement_property_predicate_v0(&predicate, &finite);
1069
1070 assert_eq!(evaluation.value_shape, AbstractValueShapeV0::FiniteSet);
1071 assert_eq!(evaluation.verdict, RefinementVerdictV0::SatisfiedSome);
1072 assert_eq!(
1073 evaluation.predicate_expression_id,
1074 "and(numeric-range:opacity:0..100:%,pseudo-state:opacity::hover)"
1075 );
1076 assert!(
1077 evaluation
1078 .witness
1079 .provenance
1080 .iter()
1081 .any(|entry| entry.source == "numeric-range-interval")
1082 );
1083 assert!(
1084 evaluation
1085 .witness
1086 .provenance
1087 .iter()
1088 .any(|entry| entry.source == "pseudo-state-refinement")
1089 );
1090 assert!(
1091 evaluation
1092 .witness
1093 .provenance
1094 .iter()
1095 .any(|entry| entry.source == "predicate-composition")
1096 );
1097 }
1098
1099 #[test]
1100 fn refinement_context_digest_is_order_stable_and_invalidation_sensitive() {
1101 let range = RefinementPropertyPredicateV0::NumericRange {
1102 property_name: "z-index".to_string(),
1103 min_inclusive: Some(0),
1104 max_inclusive: Some(10),
1105 unit: None,
1106 };
1107 let exact = RefinementPropertyPredicateV0::ExactValue {
1108 property_name: "display".to_string(),
1109 value: "grid".to_string(),
1110 };
1111 let first = summarize_refinement_context_v0(&[range.clone(), exact.clone()]);
1112 let reordered = summarize_refinement_context_v0(&[exact.clone(), range.clone()]);
1113 let changed = summarize_refinement_context_v0(&[
1114 exact,
1115 RefinementPropertyPredicateV0::NumericRange {
1116 property_name: "z-index".to_string(),
1117 min_inclusive: Some(0),
1118 max_inclusive: Some(11),
1119 unit: None,
1120 },
1121 ]);
1122
1123 assert_eq!(first.schema_version, "0");
1124 assert_eq!(first.product, "omena-refinement.context-summary");
1125 assert_eq!(first.predicate_count, 2);
1126 assert!(first.downstream_invalidation_required);
1127 assert_eq!(first.context_digest, reordered.context_digest);
1128 assert_ne!(first.context_digest, changed.context_digest);
1129 assert!(first.witness_provenance_count >= 3);
1130 }
1131
1132 #[test]
1133 fn cascade_dimensional_refinement_bridge_reuses_existing_substrates() {
1134 let members = vec![
1135 CascadeValueFamilyMemberV0 {
1136 context: CascadeContextV0 {
1137 id: "base".to_string(),
1138 parent_id: None,
1139 selectors: vec![":root".to_string()],
1140 conditions: Vec::new(),
1141 layers: vec!["tokens".to_string()],
1142 },
1143 value: AbstractPropertyValueV0::Exact {
1144 property_name: "width".to_string(),
1145 value: "12px".to_string(),
1146 pseudo_state: None,
1147 },
1148 },
1149 CascadeValueFamilyMemberV0 {
1150 context: CascadeContextV0 {
1151 id: "fluid".to_string(),
1152 parent_id: Some("base".to_string()),
1153 selectors: vec![":root".to_string()],
1154 conditions: vec!["@media (orientation: portrait)".to_string()],
1155 layers: vec!["tokens".to_string()],
1156 },
1157 value: AbstractPropertyValueV0::Exact {
1158 property_name: "width".to_string(),
1159 value: "50%".to_string(),
1160 pseudo_state: None,
1161 },
1162 },
1163 CascadeValueFamilyMemberV0 {
1164 context: CascadeContextV0 {
1165 id: "unknown".to_string(),
1166 parent_id: Some("base".to_string()),
1167 selectors: vec![":root".to_string()],
1168 conditions: vec!["@container card".to_string()],
1169 layers: vec!["tokens".to_string()],
1170 },
1171 value: AbstractPropertyValueV0::Top {
1172 property_name: "width".to_string(),
1173 },
1174 },
1175 ];
1176 let restrictions = derive_cascade_restriction_maps_v0(&members);
1177 let family = summarize_cascade_value_family_v0("width", members, restrictions);
1178 let predicate = RefinementPropertyPredicateV0::NumericRange {
1179 property_name: "width".to_string(),
1180 min_inclusive: Some(0),
1181 max_inclusive: Some(100),
1182 unit: Some("px".to_string()),
1183 };
1184
1185 let bridge = summarize_cascade_dimensional_refinement_bridge_v0(&family, &[predicate]);
1186
1187 assert_eq!(
1188 bridge.product,
1189 "omena-refinement.cascade-dimensional-refinement-bridge"
1190 );
1191 assert_eq!(bridge.claim_level, "m6DimensionalRefinementBridgeSubstrate");
1192 assert_eq!(bridge.cascade_family_product, family.product);
1193 assert_eq!(bridge.context_evaluation_count, 3);
1194 assert_eq!(bridge.restriction_map_count, 2);
1195 assert_eq!(bridge.satisfied_all_context_count, 1);
1196 assert_eq!(bridge.unsatisfiable_context_count, 1);
1197 assert_eq!(bridge.unknown_context_count, 1);
1198 assert_eq!(bridge.witness_provenance_count, 2);
1199 assert!(bridge.uses_existing_abstract_property_value_substrate);
1200 assert!(bridge.uses_existing_cascade_family_substrate);
1201 assert!(bridge.uses_existing_refinement_predicate_substrate);
1202 assert!(!bridge.forks_unit_system);
1203 assert!(!bridge.liquid_haskell_complete);
1204 assert!(!bridge.smt_complete);
1205 assert!(!bridge.theorem_claimed);
1206 assert!(bridge.product_path_evidence_ready);
1207 assert!(!bridge.stronger_type_safety_claim_ready);
1208 assert_eq!(bridge.evaluations[0].context_id, "base");
1209 assert_eq!(
1210 bridge.evaluations[0].combined_verdict,
1211 RefinementVerdictV0::SatisfiedAll
1212 );
1213 assert_eq!(
1214 bridge.evaluations[0].predicate_expression_ids,
1215 vec!["numeric-range:width:0..100:px".to_string()]
1216 );
1217 }
1218}