Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/UTILMD_AHB_Strom_2_1_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T10:15:42Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14// HAND-EDITED: instances of one SG4 compared with each other.
15use crate::eval::periods;
16use crate::eval::sg4_instances;
17use crate::eval::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
18// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
19// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
20use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
21
22/// Generated condition evaluator for UTILMD_Strom FV2504.
23pub struct UtilmdStromConditionEvaluatorFV2504 {
24    // External condition IDs that require runtime context.
25    external_conditions: std::collections::HashSet<u32>,
26}
27
28impl Default for UtilmdStromConditionEvaluatorFV2504 {
29    fn default() -> Self {
30        let mut external_conditions = std::collections::HashSet::new();
31        external_conditions.insert(1);
32        external_conditions.insert(4);
33        external_conditions.insert(5);
34        external_conditions.insert(6);
35        external_conditions.insert(8);
36        external_conditions.insert(9);
37        external_conditions.insert(14);
38        external_conditions.insert(31);
39        external_conditions.insert(33);
40        external_conditions.insert(36);
41        external_conditions.insert(38);
42        external_conditions.insert(39);
43        external_conditions.insert(40);
44        external_conditions.insert(75);
45        external_conditions.insert(98);
46        external_conditions.insert(99);
47        external_conditions.insert(151);
48        external_conditions.insert(233);
49        external_conditions.insert(241);
50        external_conditions.insert(251);
51        external_conditions.insert(292);
52        external_conditions.insert(314);
53        external_conditions.insert(315);
54        external_conditions.insert(323);
55        external_conditions.insert(412);
56        external_conditions.insert(425);
57        external_conditions.insert(426);
58        external_conditions.insert(427);
59        external_conditions.insert(428);
60        external_conditions.insert(429);
61        external_conditions.insert(433);
62        external_conditions.insert(435);
63        external_conditions.insert(453);
64        external_conditions.insert(459);
65        external_conditions.insert(460);
66        external_conditions.insert(706);
67        external_conditions.insert(717);
68        external_conditions.insert(2011);
69        Self {
70            external_conditions,
71        }
72    }
73}
74
75impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
76    fn message_type(&self) -> &str {
77        "UTILMD_Strom"
78    }
79
80    fn format_version(&self) -> &str {
81        "FV2504"
82    }
83
84    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
85        match condition {
86            1 => self.evaluate_1(ctx),
87            4 => self.evaluate_4(ctx),
88            5 => self.evaluate_5(ctx),
89            6 => self.evaluate_6(ctx),
90            7 => self.evaluate_7(ctx),
91            8 => self.evaluate_8(ctx),
92            9 => self.evaluate_9(ctx),
93            10 => self.evaluate_10(ctx),
94            11 => self.evaluate_11(ctx),
95            12 => self.evaluate_12(ctx),
96            13 => self.evaluate_13(ctx),
97            14 => self.evaluate_14(ctx),
98            15 => self.evaluate_15(ctx),
99            16 => self.evaluate_16(ctx),
100            17 => self.evaluate_17(ctx),
101            18 => self.evaluate_18(ctx),
102            19 => self.evaluate_19(ctx),
103            20 => self.evaluate_20(ctx),
104            21 => self.evaluate_21(ctx),
105            22 => self.evaluate_22(ctx),
106            23 => self.evaluate_23(ctx),
107            24 => self.evaluate_24(ctx),
108            25 => self.evaluate_25(ctx),
109            27 => self.evaluate_27(ctx),
110            30 => self.evaluate_30(ctx),
111            31 => self.evaluate_31(ctx),
112            32 => self.evaluate_32(ctx),
113            33 => self.evaluate_33(ctx),
114            34 => self.evaluate_34(ctx),
115            35 => self.evaluate_35(ctx),
116            36 => self.evaluate_36(ctx),
117            37 => self.evaluate_37(ctx),
118            38 => self.evaluate_38(ctx),
119            39 => self.evaluate_39(ctx),
120            40 => self.evaluate_40(ctx),
121            41 => self.evaluate_41(ctx),
122            42 => self.evaluate_42(ctx),
123            43 => self.evaluate_43(ctx),
124            44 => self.evaluate_44(ctx),
125            45 => self.evaluate_45(ctx),
126            46 => self.evaluate_46(ctx),
127            47 => self.evaluate_47(ctx),
128            48 => self.evaluate_48(ctx),
129            49 => self.evaluate_49(ctx),
130            50 => self.evaluate_50(ctx),
131            51 => self.evaluate_51(ctx),
132            52 => self.evaluate_52(ctx),
133            53 => self.evaluate_53(ctx),
134            54 => self.evaluate_54(ctx),
135            55 => self.evaluate_55(ctx),
136            56 => self.evaluate_56(ctx),
137            57 => self.evaluate_57(ctx),
138            58 => self.evaluate_58(ctx),
139            60 => self.evaluate_60(ctx),
140            61 => self.evaluate_61(ctx),
141            62 => self.evaluate_62(ctx),
142            63 => self.evaluate_63(ctx),
143            64 => self.evaluate_64(ctx),
144            65 => self.evaluate_65(ctx),
145            66 => self.evaluate_66(ctx),
146            67 => self.evaluate_67(ctx),
147            68 => self.evaluate_68(ctx),
148            69 => self.evaluate_69(ctx),
149            70 => self.evaluate_70(ctx),
150            71 => self.evaluate_71(ctx),
151            72 => self.evaluate_72(ctx),
152            73 => self.evaluate_73(ctx),
153            74 => self.evaluate_74(ctx),
154            75 => self.evaluate_75(ctx),
155            76 => self.evaluate_76(ctx),
156            77 => self.evaluate_77(ctx),
157            78 => self.evaluate_78(ctx),
158            83 => self.evaluate_83(ctx),
159            84 => self.evaluate_84(ctx),
160            85 => self.evaluate_85(ctx),
161            86 => self.evaluate_86(ctx),
162            87 => self.evaluate_87(ctx),
163            88 => self.evaluate_88(ctx),
164            89 => self.evaluate_89(ctx),
165            90 => self.evaluate_90(ctx),
166            91 => self.evaluate_91(ctx),
167            92 => self.evaluate_92(ctx),
168            93 => self.evaluate_93(ctx),
169            94 => self.evaluate_94(ctx),
170            95 => self.evaluate_95(ctx),
171            96 => self.evaluate_96(ctx),
172            97 => self.evaluate_97(ctx),
173            98 => self.evaluate_98(ctx),
174            99 => self.evaluate_99(ctx),
175            100 => self.evaluate_100(ctx),
176            101 => self.evaluate_101(ctx),
177            102 => self.evaluate_102(ctx),
178            103 => self.evaluate_103(ctx),
179            104 => self.evaluate_104(ctx),
180            105 => self.evaluate_105(ctx),
181            106 => self.evaluate_106(ctx),
182            107 => self.evaluate_107(ctx),
183            108 => self.evaluate_108(ctx),
184            110 => self.evaluate_110(ctx),
185            111 => self.evaluate_111(ctx),
186            112 => self.evaluate_112(ctx),
187            113 => self.evaluate_113(ctx),
188            114 => self.evaluate_114(ctx),
189            115 => self.evaluate_115(ctx),
190            116 => self.evaluate_116(ctx),
191            117 => self.evaluate_117(ctx),
192            118 => self.evaluate_118(ctx),
193            119 => self.evaluate_119(ctx),
194            120 => self.evaluate_120(ctx),
195            121 => self.evaluate_121(ctx),
196            122 => self.evaluate_122(ctx),
197            123 => self.evaluate_123(ctx),
198            124 => self.evaluate_124(ctx),
199            125 => self.evaluate_125(ctx),
200            126 => self.evaluate_126(ctx),
201            127 => self.evaluate_127(ctx),
202            128 => self.evaluate_128(ctx),
203            129 => self.evaluate_129(ctx),
204            130 => self.evaluate_130(ctx),
205            131 => self.evaluate_131(ctx),
206            132 => self.evaluate_132(ctx),
207            133 => self.evaluate_133(ctx),
208            134 => self.evaluate_134(ctx),
209            135 => self.evaluate_135(ctx),
210            136 => self.evaluate_136(ctx),
211            137 => self.evaluate_137(ctx),
212            138 => self.evaluate_138(ctx),
213            139 => self.evaluate_139(ctx),
214            140 => self.evaluate_140(ctx),
215            141 => self.evaluate_141(ctx),
216            142 => self.evaluate_142(ctx),
217            143 => self.evaluate_143(ctx),
218            144 => self.evaluate_144(ctx),
219            145 => self.evaluate_145(ctx),
220            146 => self.evaluate_146(ctx),
221            147 => self.evaluate_147(ctx),
222            148 => self.evaluate_148(ctx),
223            149 => self.evaluate_149(ctx),
224            150 => self.evaluate_150(ctx),
225            151 => self.evaluate_151(ctx),
226            152 => self.evaluate_152(ctx),
227            153 => self.evaluate_153(ctx),
228            156 => self.evaluate_156(ctx),
229            161 => self.evaluate_161(ctx),
230            162 => self.evaluate_162(ctx),
231            163 => self.evaluate_163(ctx),
232            164 => self.evaluate_164(ctx),
233            165 => self.evaluate_165(ctx),
234            166 => self.evaluate_166(ctx),
235            167 => self.evaluate_167(ctx),
236            170 => self.evaluate_170(ctx),
237            172 => self.evaluate_172(ctx),
238            173 => self.evaluate_173(ctx),
239            174 => self.evaluate_174(ctx),
240            175 => self.evaluate_175(ctx),
241            176 => self.evaluate_176(ctx),
242            177 => self.evaluate_177(ctx),
243            178 => self.evaluate_178(ctx),
244            179 => self.evaluate_179(ctx),
245            180 => self.evaluate_180(ctx),
246            184 => self.evaluate_184(ctx),
247            190 => self.evaluate_190(ctx),
248            191 => self.evaluate_191(ctx),
249            192 => self.evaluate_192(ctx),
250            193 => self.evaluate_193(ctx),
251            194 => self.evaluate_194(ctx),
252            195 => self.evaluate_195(ctx),
253            196 => self.evaluate_196(ctx),
254            197 => self.evaluate_197(ctx),
255            198 => self.evaluate_198(ctx),
256            199 => self.evaluate_199(ctx),
257            201 => self.evaluate_201(ctx),
258            202 => self.evaluate_202(ctx),
259            203 => self.evaluate_203(ctx),
260            204 => self.evaluate_204(ctx),
261            205 => self.evaluate_205(ctx),
262            206 => self.evaluate_206(ctx),
263            209 => self.evaluate_209(ctx),
264            210 => self.evaluate_210(ctx),
265            212 => self.evaluate_212(ctx),
266            213 => self.evaluate_213(ctx),
267            215 => self.evaluate_215(ctx),
268            216 => self.evaluate_216(ctx),
269            217 => self.evaluate_217(ctx),
270            219 => self.evaluate_219(ctx),
271            220 => self.evaluate_220(ctx),
272            221 => self.evaluate_221(ctx),
273            223 => self.evaluate_223(ctx),
274            224 => self.evaluate_224(ctx),
275            227 => self.evaluate_227(ctx),
276            229 => self.evaluate_229(ctx),
277            232 => self.evaluate_232(ctx),
278            233 => self.evaluate_233(ctx),
279            234 => self.evaluate_234(ctx),
280            237 => self.evaluate_237(ctx),
281            238 => self.evaluate_238(ctx),
282            239 => self.evaluate_239(ctx),
283            240 => self.evaluate_240(ctx),
284            241 => self.evaluate_241(ctx),
285            243 => self.evaluate_243(ctx),
286            244 => self.evaluate_244(ctx),
287            248 => self.evaluate_248(ctx),
288            249 => self.evaluate_249(ctx),
289            251 => self.evaluate_251(ctx),
290            252 => self.evaluate_252(ctx),
291            254 => self.evaluate_254(ctx),
292            255 => self.evaluate_255(ctx),
293            256 => self.evaluate_256(ctx),
294            257 => self.evaluate_257(ctx),
295            259 => self.evaluate_259(ctx),
296            261 => self.evaluate_261(ctx),
297            262 => self.evaluate_262(ctx),
298            265 => self.evaluate_265(ctx),
299            266 => self.evaluate_266(ctx),
300            267 => self.evaluate_267(ctx),
301            268 => self.evaluate_268(ctx),
302            269 => self.evaluate_269(ctx),
303            270 => self.evaluate_270(ctx),
304            273 => self.evaluate_273(ctx),
305            279 => self.evaluate_279(ctx),
306            280 => self.evaluate_280(ctx),
307            282 => self.evaluate_282(ctx),
308            284 => self.evaluate_284(ctx),
309            285 => self.evaluate_285(ctx),
310            286 => self.evaluate_286(ctx),
311            287 => self.evaluate_287(ctx),
312            288 => self.evaluate_288(ctx),
313            291 => self.evaluate_291(ctx),
314            292 => self.evaluate_292(ctx),
315            293 => self.evaluate_293(ctx),
316            294 => self.evaluate_294(ctx),
317            295 => self.evaluate_295(ctx),
318            296 => self.evaluate_296(ctx),
319            297 => self.evaluate_297(ctx),
320            298 => self.evaluate_298(ctx),
321            299 => self.evaluate_299(ctx),
322            300 => self.evaluate_300(ctx),
323            301 => self.evaluate_301(ctx),
324            302 => self.evaluate_302(ctx),
325            303 => self.evaluate_303(ctx),
326            304 => self.evaluate_304(ctx),
327            305 => self.evaluate_305(ctx),
328            306 => self.evaluate_306(ctx),
329            307 => self.evaluate_307(ctx),
330            309 => self.evaluate_309(ctx),
331            314 => self.evaluate_314(ctx),
332            315 => self.evaluate_315(ctx),
333            316 => self.evaluate_316(ctx),
334            317 => self.evaluate_317(ctx),
335            318 => self.evaluate_318(ctx),
336            321 => self.evaluate_321(ctx),
337            322 => self.evaluate_322(ctx),
338            323 => self.evaluate_323(ctx),
339            327 => self.evaluate_327(ctx),
340            329 => self.evaluate_329(ctx),
341            331 => self.evaluate_331(ctx),
342            332 => self.evaluate_332(ctx),
343            333 => self.evaluate_333(ctx),
344            334 => self.evaluate_334(ctx),
345            335 => self.evaluate_335(ctx),
346            336 => self.evaluate_336(ctx),
347            337 => self.evaluate_337(ctx),
348            338 => self.evaluate_338(ctx),
349            339 => self.evaluate_339(ctx),
350            340 => self.evaluate_340(ctx),
351            341 => self.evaluate_341(ctx),
352            342 => self.evaluate_342(ctx),
353            344 => self.evaluate_344(ctx),
354            345 => self.evaluate_345(ctx),
355            346 => self.evaluate_346(ctx),
356            347 => self.evaluate_347(ctx),
357            348 => self.evaluate_348(ctx),
358            349 => self.evaluate_349(ctx),
359            350 => self.evaluate_350(ctx),
360            351 => self.evaluate_351(ctx),
361            352 => self.evaluate_352(ctx),
362            355 => self.evaluate_355(ctx),
363            356 => self.evaluate_356(ctx),
364            357 => self.evaluate_357(ctx),
365            358 => self.evaluate_358(ctx),
366            359 => self.evaluate_359(ctx),
367            360 => self.evaluate_360(ctx),
368            363 => self.evaluate_363(ctx),
369            365 => self.evaluate_365(ctx),
370            366 => self.evaluate_366(ctx),
371            367 => self.evaluate_367(ctx),
372            368 => self.evaluate_368(ctx),
373            370 => self.evaluate_370(ctx),
374            371 => self.evaluate_371(ctx),
375            372 => self.evaluate_372(ctx),
376            373 => self.evaluate_373(ctx),
377            375 => self.evaluate_375(ctx),
378            376 => self.evaluate_376(ctx),
379            377 => self.evaluate_377(ctx),
380            378 => self.evaluate_378(ctx),
381            379 => self.evaluate_379(ctx),
382            380 => self.evaluate_380(ctx),
383            384 => self.evaluate_384(ctx),
384            386 => self.evaluate_386(ctx),
385            387 => self.evaluate_387(ctx),
386            388 => self.evaluate_388(ctx),
387            391 => self.evaluate_391(ctx),
388            392 => self.evaluate_392(ctx),
389            393 => self.evaluate_393(ctx),
390            394 => self.evaluate_394(ctx),
391            395 => self.evaluate_395(ctx),
392            396 => self.evaluate_396(ctx),
393            397 => self.evaluate_397(ctx),
394            398 => self.evaluate_398(ctx),
395            399 => self.evaluate_399(ctx),
396            401 => self.evaluate_401(ctx),
397            402 => self.evaluate_402(ctx),
398            403 => self.evaluate_403(ctx),
399            404 => self.evaluate_404(ctx),
400            405 => self.evaluate_405(ctx),
401            406 => self.evaluate_406(ctx),
402            407 => self.evaluate_407(ctx),
403            408 => self.evaluate_408(ctx),
404            409 => self.evaluate_409(ctx),
405            410 => self.evaluate_410(ctx),
406            411 => self.evaluate_411(ctx),
407            412 => self.evaluate_412(ctx),
408            413 => self.evaluate_413(ctx),
409            414 => self.evaluate_414(ctx),
410            415 => self.evaluate_415(ctx),
411            416 => self.evaluate_416(ctx),
412            417 => self.evaluate_417(ctx),
413            419 => self.evaluate_419(ctx),
414            420 => self.evaluate_420(ctx),
415            421 => self.evaluate_421(ctx),
416            425 => self.evaluate_425(ctx),
417            426 => self.evaluate_426(ctx),
418            427 => self.evaluate_427(ctx),
419            428 => self.evaluate_428(ctx),
420            429 => self.evaluate_429(ctx),
421            430 => self.evaluate_430(ctx),
422            431 => self.evaluate_431(ctx),
423            432 => self.evaluate_432(ctx),
424            433 => self.evaluate_433(ctx),
425            435 => self.evaluate_435(ctx),
426            436 => self.evaluate_436(ctx),
427            437 => self.evaluate_437(ctx),
428            438 => self.evaluate_438(ctx),
429            440 => self.evaluate_440(ctx),
430            441 => self.evaluate_441(ctx),
431            442 => self.evaluate_442(ctx),
432            444 => self.evaluate_444(ctx),
433            445 => self.evaluate_445(ctx),
434            446 => self.evaluate_446(ctx),
435            447 => self.evaluate_447(ctx),
436            448 => self.evaluate_448(ctx),
437            449 => self.evaluate_449(ctx),
438            450 => self.evaluate_450(ctx),
439            451 => self.evaluate_451(ctx),
440            452 => self.evaluate_452(ctx),
441            453 => self.evaluate_453(ctx),
442            454 => self.evaluate_454(ctx),
443            455 => self.evaluate_455(ctx),
444            456 => self.evaluate_456(ctx),
445            457 => self.evaluate_457(ctx),
446            458 => self.evaluate_458(ctx),
447            459 => self.evaluate_459(ctx),
448            460 => self.evaluate_460(ctx),
449            461 => self.evaluate_461(ctx),
450            462 => self.evaluate_462(ctx),
451            463 => self.evaluate_463(ctx),
452            465 => self.evaluate_465(ctx),
453            466 => self.evaluate_466(ctx),
454            467 => self.evaluate_467(ctx),
455            468 => self.evaluate_468(ctx),
456            469 => self.evaluate_469(ctx),
457            470 => self.evaluate_470(ctx),
458            471 => self.evaluate_471(ctx),
459            472 => self.evaluate_472(ctx),
460            473 => self.evaluate_473(ctx),
461            474 => self.evaluate_474(ctx),
462            475 => self.evaluate_475(ctx),
463            476 => self.evaluate_476(ctx),
464            477 => self.evaluate_477(ctx),
465            478 => self.evaluate_478(ctx),
466            479 => self.evaluate_479(ctx),
467            480 => self.evaluate_480(ctx),
468            481 => self.evaluate_481(ctx),
469            483 => self.evaluate_483(ctx),
470            484 => self.evaluate_484(ctx),
471            487 => self.evaluate_487(ctx),
472            489 => self.evaluate_489(ctx),
473            490 => self.evaluate_490(ctx),
474            491 => self.evaluate_491(ctx),
475            494 => self.evaluate_494(ctx),
476            500 => self.evaluate_500(ctx),
477            501 => self.evaluate_501(ctx),
478            502 => self.evaluate_502(ctx),
479            503 => self.evaluate_503(ctx),
480            504 => self.evaluate_504(ctx),
481            505 => self.evaluate_505(ctx),
482            506 => self.evaluate_506(ctx),
483            507 => self.evaluate_507(ctx),
484            508 => self.evaluate_508(ctx),
485            509 => self.evaluate_509(ctx),
486            510 => self.evaluate_510(ctx),
487            511 => self.evaluate_511(ctx),
488            512 => self.evaluate_512(ctx),
489            513 => self.evaluate_513(ctx),
490            514 => self.evaluate_514(ctx),
491            515 => self.evaluate_515(ctx),
492            516 => self.evaluate_516(ctx),
493            517 => self.evaluate_517(ctx),
494            518 => self.evaluate_518(ctx),
495            519 => self.evaluate_519(ctx),
496            520 => self.evaluate_520(ctx),
497            521 => self.evaluate_521(ctx),
498            522 => self.evaluate_522(ctx),
499            523 => self.evaluate_523(ctx),
500            524 => self.evaluate_524(ctx),
501            525 => self.evaluate_525(ctx),
502            526 => self.evaluate_526(ctx),
503            527 => self.evaluate_527(ctx),
504            528 => self.evaluate_528(ctx),
505            529 => self.evaluate_529(ctx),
506            530 => self.evaluate_530(ctx),
507            531 => self.evaluate_531(ctx),
508            532 => self.evaluate_532(ctx),
509            533 => self.evaluate_533(ctx),
510            555 => self.evaluate_555(ctx),
511            556 => self.evaluate_556(ctx),
512            558 => self.evaluate_558(ctx),
513            559 => self.evaluate_559(ctx),
514            563 => self.evaluate_563(ctx),
515            566 => self.evaluate_566(ctx),
516            567 => self.evaluate_567(ctx),
517            568 => self.evaluate_568(ctx),
518            569 => self.evaluate_569(ctx),
519            570 => self.evaluate_570(ctx),
520            572 => self.evaluate_572(ctx),
521            576 => self.evaluate_576(ctx),
522            577 => self.evaluate_577(ctx),
523            579 => self.evaluate_579(ctx),
524            580 => self.evaluate_580(ctx),
525            581 => self.evaluate_581(ctx),
526            586 => self.evaluate_586(ctx),
527            590 => self.evaluate_590(ctx),
528            594 => self.evaluate_594(ctx),
529            599 => self.evaluate_599(ctx),
530            601 => self.evaluate_601(ctx),
531            606 => self.evaluate_606(ctx),
532            609 => self.evaluate_609(ctx),
533            611 => self.evaluate_611(ctx),
534            614 => self.evaluate_614(ctx),
535            617 => self.evaluate_617(ctx),
536            618 => self.evaluate_618(ctx),
537            619 => self.evaluate_619(ctx),
538            621 => self.evaluate_621(ctx),
539            622 => self.evaluate_622(ctx),
540            623 => self.evaluate_623(ctx),
541            630 => self.evaluate_630(ctx),
542            631 => self.evaluate_631(ctx),
543            632 => self.evaluate_632(ctx),
544            637 => self.evaluate_637(ctx),
545            638 => self.evaluate_638(ctx),
546            639 => self.evaluate_639(ctx),
547            640 => self.evaluate_640(ctx),
548            641 => self.evaluate_641(ctx),
549            642 => self.evaluate_642(ctx),
550            643 => self.evaluate_643(ctx),
551            645 => self.evaluate_645(ctx),
552            646 => self.evaluate_646(ctx),
553            647 => self.evaluate_647(ctx),
554            648 => self.evaluate_648(ctx),
555            651 => self.evaluate_651(ctx),
556            653 => self.evaluate_653(ctx),
557            655 => self.evaluate_655(ctx),
558            659 => self.evaluate_659(ctx),
559            660 => self.evaluate_660(ctx),
560            662 => self.evaluate_662(ctx),
561            663 => self.evaluate_663(ctx),
562            664 => self.evaluate_664(ctx),
563            665 => self.evaluate_665(ctx),
564            667 => self.evaluate_667(ctx),
565            668 => self.evaluate_668(ctx),
566            670 => self.evaluate_670(ctx),
567            671 => self.evaluate_671(ctx),
568            672 => self.evaluate_672(ctx),
569            673 => self.evaluate_673(ctx),
570            674 => self.evaluate_674(ctx),
571            675 => self.evaluate_675(ctx),
572            677 => self.evaluate_677(ctx),
573            678 => self.evaluate_678(ctx),
574            679 => self.evaluate_679(ctx),
575            680 => self.evaluate_680(ctx),
576            682 => self.evaluate_682(ctx),
577            683 => self.evaluate_683(ctx),
578            684 => self.evaluate_684(ctx),
579            685 => self.evaluate_685(ctx),
580            687 => self.evaluate_687(ctx),
581            688 => self.evaluate_688(ctx),
582            689 => self.evaluate_689(ctx),
583            690 => self.evaluate_690(ctx),
584            693 => self.evaluate_693(ctx),
585            694 => self.evaluate_694(ctx),
586            695 => self.evaluate_695(ctx),
587            696 => self.evaluate_696(ctx),
588            698 => self.evaluate_698(ctx),
589            699 => self.evaluate_699(ctx),
590            700 => self.evaluate_700(ctx),
591            704 => self.evaluate_704(ctx),
592            705 => self.evaluate_705(ctx),
593            706 => self.evaluate_706(ctx),
594            707 => self.evaluate_707(ctx),
595            708 => self.evaluate_708(ctx),
596            709 => self.evaluate_709(ctx),
597            710 => self.evaluate_710(ctx),
598            711 => self.evaluate_711(ctx),
599            712 => self.evaluate_712(ctx),
600            713 => self.evaluate_713(ctx),
601            714 => self.evaluate_714(ctx),
602            715 => self.evaluate_715(ctx),
603            716 => self.evaluate_716(ctx),
604            717 => self.evaluate_717(ctx),
605            718 => self.evaluate_718(ctx),
606            719 => self.evaluate_719(ctx),
607            902 => self.evaluate_902(ctx),
608            910 => self.evaluate_910(ctx),
609            914 => self.evaluate_914(ctx),
610            922 => self.evaluate_922(ctx),
611            926 => self.evaluate_926(ctx),
612            930 => self.evaluate_930(ctx),
613            931 => self.evaluate_931(ctx),
614            932 => self.evaluate_932(ctx),
615            933 => self.evaluate_933(ctx),
616            937 => self.evaluate_937(ctx),
617            938 => self.evaluate_938(ctx),
618            939 => self.evaluate_939(ctx),
619            940 => self.evaluate_940(ctx),
620            942 => self.evaluate_942(ctx),
621            943 => self.evaluate_943(ctx),
622            946 => self.evaluate_946(ctx),
623            948 => self.evaluate_948(ctx),
624            950 => self.evaluate_950(ctx),
625            951 => self.evaluate_951(ctx),
626            952 => self.evaluate_952(ctx),
627            955 => self.evaluate_955(ctx),
628            957 => self.evaluate_957(ctx),
629            960 => self.evaluate_960(ctx),
630            961 => self.evaluate_961(ctx),
631            967 => self.evaluate_967(ctx),
632            2001 => self.evaluate_2001(ctx),
633            2002 => self.evaluate_2002(ctx),
634            2003 => self.evaluate_2003(ctx),
635            2004 => self.evaluate_2004(ctx),
636            2005 => self.evaluate_2005(ctx),
637            2006 => self.evaluate_2006(ctx),
638            2007 => self.evaluate_2007(ctx),
639            2008 => self.evaluate_2008(ctx),
640            2009 => self.evaluate_2009(ctx),
641            2010 => self.evaluate_2010(ctx),
642            2011 => self.evaluate_2011(ctx),
643            2012 => self.evaluate_2012(ctx),
644            2013 => self.evaluate_2013(ctx),
645            2014 => self.evaluate_2014(ctx),
646            2015 => self.evaluate_2015(ctx),
647            2016 => self.evaluate_2016(ctx),
648            2017 => self.evaluate_2017(ctx),
649            2018 => self.evaluate_2018(ctx),
650            2060 => self.evaluate_2060(ctx),
651            2061 => self.evaluate_2061(ctx),
652            2071 => self.evaluate_2071(ctx),
653            2073 => self.evaluate_2073(ctx),
654            2075 => self.evaluate_2075(ctx),
655            2080 => self.evaluate_2080(ctx),
656            2095 => self.evaluate_2095(ctx),
657            2096 => self.evaluate_2096(ctx),
658            2119 => self.evaluate_2119(ctx),
659            2140 => self.evaluate_2140(ctx),
660            2182 => self.evaluate_2182(ctx),
661            2183 => self.evaluate_2183(ctx),
662            2207 => self.evaluate_2207(ctx),
663            2225 => self.evaluate_2225(ctx),
664            2236 => self.evaluate_2236(ctx),
665            2252 => self.evaluate_2252(ctx),
666            2261 => self.evaluate_2261(ctx),
667            2284 => self.evaluate_2284(ctx),
668            2286 => self.evaluate_2286(ctx),
669            2287 => self.evaluate_2287(ctx),
670            2288 => self.evaluate_2288(ctx),
671            2307 => self.evaluate_2307(ctx),
672            2308 => self.evaluate_2308(ctx),
673            2309 => self.evaluate_2309(ctx),
674            2310 => self.evaluate_2310(ctx),
675            2311 => self.evaluate_2311(ctx),
676            2312 => self.evaluate_2312(ctx),
677            2313 => self.evaluate_2313(ctx),
678            2317 => self.evaluate_2317(ctx),
679            2318 => self.evaluate_2318(ctx),
680            2344 => self.evaluate_2344(ctx),
681            2350 => self.evaluate_2350(ctx),
682            2351 => self.evaluate_2351(ctx),
683            2352 => self.evaluate_2352(ctx),
684            2356 => self.evaluate_2356(ctx),
685            2357 => self.evaluate_2357(ctx),
686            2358 => self.evaluate_2358(ctx),
687            2359 => self.evaluate_2359(ctx),
688            2360 => self.evaluate_2360(ctx),
689            2361 => self.evaluate_2361(ctx),
690            _ => ConditionResult::Unknown,
691        }
692    }
693
694    fn is_external(&self, condition: u32) -> bool {
695        self.external_conditions.contains(&condition)
696    }
697    fn is_known(&self, condition: u32) -> bool {
698        matches!(
699            condition,
700            1 | 4
701                | 5
702                | 6
703                | 7
704                | 8
705                | 9
706                | 10
707                | 11
708                | 12
709                | 13
710                | 14
711                | 15
712                | 16
713                | 17
714                | 18
715                | 19
716                | 20
717                | 21
718                | 22
719                | 23
720                | 24
721                | 25
722                | 27
723                | 30
724                | 31
725                | 32
726                | 33
727                | 34
728                | 35
729                | 36
730                | 37
731                | 38
732                | 39
733                | 40
734                | 41
735                | 42
736                | 43
737                | 44
738                | 45
739                | 46
740                | 47
741                | 48
742                | 49
743                | 50
744                | 51
745                | 52
746                | 53
747                | 54
748                | 55
749                | 56
750                | 57
751                | 58
752                | 60
753                | 61
754                | 62
755                | 63
756                | 64
757                | 65
758                | 66
759                | 67
760                | 68
761                | 69
762                | 70
763                | 71
764                | 72
765                | 73
766                | 74
767                | 75
768                | 76
769                | 77
770                | 78
771                | 83
772                | 84
773                | 85
774                | 86
775                | 87
776                | 88
777                | 89
778                | 90
779                | 91
780                | 92
781                | 93
782                | 94
783                | 95
784                | 96
785                | 97
786                | 98
787                | 99
788                | 100
789                | 101
790                | 102
791                | 103
792                | 104
793                | 105
794                | 106
795                | 107
796                | 108
797                | 110
798                | 111
799                | 112
800                | 113
801                | 114
802                | 115
803                | 116
804                | 117
805                | 118
806                | 119
807                | 120
808                | 121
809                | 122
810                | 123
811                | 124
812                | 125
813                | 126
814                | 127
815                | 128
816                | 129
817                | 130
818                | 131
819                | 132
820                | 133
821                | 134
822                | 135
823                | 136
824                | 137
825                | 138
826                | 139
827                | 140
828                | 141
829                | 142
830                | 143
831                | 144
832                | 145
833                | 146
834                | 147
835                | 148
836                | 149
837                | 150
838                | 151
839                | 152
840                | 153
841                | 156
842                | 161
843                | 162
844                | 163
845                | 164
846                | 165
847                | 166
848                | 167
849                | 170
850                | 172
851                | 173
852                | 174
853                | 175
854                | 176
855                | 177
856                | 178
857                | 179
858                | 180
859                | 184
860                | 190
861                | 191
862                | 192
863                | 193
864                | 194
865                | 195
866                | 196
867                | 197
868                | 198
869                | 199
870                | 201
871                | 202
872                | 203
873                | 204
874                | 205
875                | 206
876                | 209
877                | 210
878                | 212
879                | 213
880                | 215
881                | 216
882                | 217
883                | 219
884                | 220
885                | 221
886                | 223
887                | 224
888                | 227
889                | 229
890                | 232
891                | 233
892                | 234
893                | 237
894                | 238
895                | 239
896                | 240
897                | 241
898                | 243
899                | 244
900                | 248
901                | 249
902                | 251
903                | 252
904                | 254
905                | 255
906                | 256
907                | 257
908                | 259
909                | 261
910                | 262
911                | 265
912                | 266
913                | 267
914                | 268
915                | 269
916                | 270
917                | 273
918                | 279
919                | 280
920                | 282
921                | 284
922                | 285
923                | 286
924                | 287
925                | 288
926                | 291
927                | 292
928                | 293
929                | 294
930                | 295
931                | 296
932                | 297
933                | 298
934                | 299
935                | 300
936                | 301
937                | 302
938                | 303
939                | 304
940                | 305
941                | 306
942                | 307
943                | 309
944                | 314
945                | 315
946                | 316
947                | 317
948                | 318
949                | 321
950                | 322
951                | 323
952                | 327
953                | 329
954                | 331
955                | 332
956                | 333
957                | 334
958                | 335
959                | 336
960                | 337
961                | 338
962                | 339
963                | 340
964                | 341
965                | 342
966                | 344
967                | 345
968                | 346
969                | 347
970                | 348
971                | 349
972                | 350
973                | 351
974                | 352
975                | 355
976                | 356
977                | 357
978                | 358
979                | 359
980                | 360
981                | 363
982                | 365
983                | 366
984                | 367
985                | 368
986                | 370
987                | 371
988                | 372
989                | 373
990                | 375
991                | 376
992                | 377
993                | 378
994                | 379
995                | 380
996                | 384
997                | 386
998                | 387
999                | 388
1000                | 391
1001                | 392
1002                | 393
1003                | 394
1004                | 395
1005                | 396
1006                | 397
1007                | 398
1008                | 399
1009                | 401
1010                | 402
1011                | 403
1012                | 404
1013                | 405
1014                | 406
1015                | 407
1016                | 408
1017                | 409
1018                | 410
1019                | 411
1020                | 412
1021                | 413
1022                | 414
1023                | 415
1024                | 416
1025                | 417
1026                | 419
1027                | 420
1028                | 421
1029                | 425
1030                | 426
1031                | 427
1032                | 428
1033                | 429
1034                | 430
1035                | 431
1036                | 432
1037                | 433
1038                | 435
1039                | 436
1040                | 437
1041                | 438
1042                | 440
1043                | 441
1044                | 442
1045                | 444
1046                | 445
1047                | 446
1048                | 447
1049                | 448
1050                | 449
1051                | 450
1052                | 451
1053                | 452
1054                | 453
1055                | 454
1056                | 455
1057                | 456
1058                | 457
1059                | 458
1060                | 459
1061                | 460
1062                | 461
1063                | 462
1064                | 463
1065                | 465
1066                | 466
1067                | 467
1068                | 468
1069                | 469
1070                | 470
1071                | 471
1072                | 472
1073                | 473
1074                | 474
1075                | 475
1076                | 476
1077                | 477
1078                | 478
1079                | 479
1080                | 480
1081                | 481
1082                | 483
1083                | 484
1084                | 487
1085                | 489
1086                | 490
1087                | 491
1088                | 494
1089                | 500
1090                | 501
1091                | 502
1092                | 503
1093                | 504
1094                | 505
1095                | 506
1096                | 507
1097                | 508
1098                | 509
1099                | 510
1100                | 511
1101                | 512
1102                | 513
1103                | 514
1104                | 515
1105                | 516
1106                | 517
1107                | 518
1108                | 519
1109                | 520
1110                | 521
1111                | 522
1112                | 523
1113                | 524
1114                | 525
1115                | 526
1116                | 527
1117                | 528
1118                | 529
1119                | 530
1120                | 531
1121                | 532
1122                | 533
1123                | 555
1124                | 556
1125                | 558
1126                | 559
1127                | 563
1128                | 566
1129                | 567
1130                | 568
1131                | 569
1132                | 570
1133                | 572
1134                | 576
1135                | 577
1136                | 579
1137                | 580
1138                | 581
1139                | 586
1140                | 590
1141                | 594
1142                | 599
1143                | 601
1144                | 606
1145                | 609
1146                | 611
1147                | 614
1148                | 617
1149                | 618
1150                | 619
1151                | 621
1152                | 622
1153                | 623
1154                | 630
1155                | 631
1156                | 632
1157                | 637
1158                | 638
1159                | 639
1160                | 640
1161                | 641
1162                | 642
1163                | 643
1164                | 645
1165                | 646
1166                | 647
1167                | 648
1168                | 651
1169                | 653
1170                | 655
1171                | 659
1172                | 660
1173                | 662
1174                | 663
1175                | 664
1176                | 665
1177                | 667
1178                | 668
1179                | 670
1180                | 671
1181                | 672
1182                | 673
1183                | 674
1184                | 675
1185                | 677
1186                | 678
1187                | 679
1188                | 680
1189                | 682
1190                | 683
1191                | 684
1192                | 685
1193                | 687
1194                | 688
1195                | 689
1196                | 690
1197                | 693
1198                | 694
1199                | 695
1200                | 696
1201                | 698
1202                | 699
1203                | 700
1204                | 704
1205                | 705
1206                | 706
1207                | 707
1208                | 708
1209                | 709
1210                | 710
1211                | 711
1212                | 712
1213                | 713
1214                | 714
1215                | 715
1216                | 716
1217                | 717
1218                | 718
1219                | 719
1220                | 902
1221                | 910
1222                | 914
1223                | 922
1224                | 926
1225                | 930
1226                | 931
1227                | 932
1228                | 933
1229                | 937
1230                | 938
1231                | 939
1232                | 940
1233                | 942
1234                | 943
1235                | 946
1236                | 948
1237                | 950
1238                | 951
1239                | 952
1240                | 955
1241                | 957
1242                | 960
1243                | 961
1244                | 967
1245                | 2001
1246                | 2002
1247                | 2003
1248                | 2004
1249                | 2005
1250                | 2006
1251                | 2007
1252                | 2008
1253                | 2009
1254                | 2010
1255                | 2011
1256                | 2012
1257                | 2013
1258                | 2014
1259                | 2015
1260                | 2016
1261                | 2017
1262                | 2018
1263                | 2060
1264                | 2061
1265                | 2071
1266                | 2073
1267                | 2075
1268                | 2080
1269                | 2095
1270                | 2096
1271                | 2119
1272                | 2140
1273                | 2182
1274                | 2183
1275                | 2207
1276                | 2225
1277                | 2236
1278                | 2252
1279                | 2261
1280                | 2284
1281                | 2286
1282                | 2287
1283                | 2288
1284                | 2307
1285                | 2308
1286                | 2309
1287                | 2310
1288                | 2311
1289                | 2312
1290                | 2313
1291                | 2317
1292                | 2318
1293                | 2344
1294                | 2350
1295                | 2351
1296                | 2352
1297                | 2356
1298                | 2357
1299                | 2358
1300                | 2359
1301                | 2360
1302                | 2361
1303        )
1304    }
1305}
1306
1307impl UtilmdStromConditionEvaluatorFV2504 {
1308    /// [2006] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolge...
1309    // REVIEW: Counts SG8 instances with SEQ+Z01 where condition [199] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. Returns True if at least one qualifying parent exists (cardinality enforcement is partial — full 'exactly once' check requires knowing the current SG type). The recursive call self.evaluate(199, ctx) operates at message scope, which is the best available approximation. (medium confidence)
1310    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1311        let nav = match ctx.navigator() {
1312            Some(n) => n,
1313            None => return ConditionResult::Unknown,
1314        };
1315        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1316        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1317        for i in 0..sg8_count {
1318            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1319            let is_z01 = seq_segs.iter().any(|s| {
1320                s.elements
1321                    .first()
1322                    .and_then(|e| e.first())
1323                    .is_some_and(|v| v == "Z01")
1324            });
1325            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1326                for seq in &seq_segs {
1327                    if seq
1328                        .elements
1329                        .first()
1330                        .and_then(|e| e.first())
1331                        .is_some_and(|v| v == "Z01")
1332                    {
1333                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1334                            if !zid.is_empty() {
1335                                qualifying_zeitraum_ids.push(zid.clone());
1336                            }
1337                        }
1338                    }
1339                }
1340            }
1341        }
1342        if qualifying_zeitraum_ids.is_empty() {
1343            return ConditionResult::Unknown;
1344        }
1345        ConditionResult::True
1346    }
1347
1348    /// [2007] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
1349    // REVIEW: Semantically identical to [2006] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation with no functional difference. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [199] is fulfilled. Recursive call self.evaluate(199, ctx) operates at message scope. (medium confidence)
1350    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1351        let nav = match ctx.navigator() {
1352            Some(n) => n,
1353            None => return ConditionResult::Unknown,
1354        };
1355        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1356        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1357        for i in 0..sg8_count {
1358            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1359            let is_z01 = seq_segs.iter().any(|s| {
1360                s.elements
1361                    .first()
1362                    .and_then(|e| e.first())
1363                    .is_some_and(|v| v == "Z01")
1364            });
1365            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1366                for seq in &seq_segs {
1367                    if seq
1368                        .elements
1369                        .first()
1370                        .and_then(|e| e.first())
1371                        .is_some_and(|v| v == "Z01")
1372                    {
1373                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1374                            if !zid.is_empty() {
1375                                qualifying_zeitraum_ids.push(zid.clone());
1376                            }
1377                        }
1378                    }
1379                }
1380            }
1381        }
1382        if qualifying_zeitraum_ids.is_empty() {
1383            return ConditionResult::Unknown;
1384        }
1385        ConditionResult::True
1386    }
1387
1388    /// [2008] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolgen...
1389    // REVIEW: Same structure as [2006/2007] but references condition [89] instead of [199]. Counts qualifying SG8 SEQ+Z01 instances where condition [89] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. The recursive self.evaluate(89, ctx) call operates at message scope. (medium confidence)
1390    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1391        let nav = match ctx.navigator() {
1392            Some(n) => n,
1393            None => return ConditionResult::Unknown,
1394        };
1395        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1396        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1397        for i in 0..sg8_count {
1398            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1399            let is_z01 = seq_segs.iter().any(|s| {
1400                s.elements
1401                    .first()
1402                    .and_then(|e| e.first())
1403                    .is_some_and(|v| v == "Z01")
1404            });
1405            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1406                for seq in &seq_segs {
1407                    if seq
1408                        .elements
1409                        .first()
1410                        .and_then(|e| e.first())
1411                        .is_some_and(|v| v == "Z01")
1412                    {
1413                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1414                            if !zid.is_empty() {
1415                                qualifying_zeitraum_ids.push(zid.clone());
1416                            }
1417                        }
1418                    }
1419                }
1420            }
1421        }
1422        if qualifying_zeitraum_ids.is_empty() {
1423            return ConditionResult::Unknown;
1424        }
1425        ConditionResult::True
1426    }
1427
1428    /// [2009] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolg...
1429    // REVIEW: Semantically identical to [2008] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [89] is fulfilled. Recursive call self.evaluate(89, ctx) operates at message scope. (medium confidence)
1430    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1431        let nav = match ctx.navigator() {
1432            Some(n) => n,
1433            None => return ConditionResult::Unknown,
1434        };
1435        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1436        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1437        for i in 0..sg8_count {
1438            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1439            let is_z01 = seq_segs.iter().any(|s| {
1440                s.elements
1441                    .first()
1442                    .and_then(|e| e.first())
1443                    .is_some_and(|v| v == "Z01")
1444            });
1445            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1446                for seq in &seq_segs {
1447                    if seq
1448                        .elements
1449                        .first()
1450                        .and_then(|e| e.first())
1451                        .is_some_and(|v| v == "Z01")
1452                    {
1453                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1454                            if !zid.is_empty() {
1455                                qualifying_zeitraum_ids.push(zid.clone());
1456                            }
1457                        }
1458                    }
1459                }
1460            }
1461        }
1462        if qualifying_zeitraum_ids.is_empty() {
1463            return ConditionResult::Unknown;
1464        }
1465        ConditionResult::True
1466    }
1467
1468    /// [1] Wenn Aufteilung vorhanden
1469    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1470        ctx.external.evaluate("message_splitting")
1471    }
1472
1473    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1474    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1475        ctx.external.evaluate("recipient_is_lf")
1476    }
1477
1478    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1479    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1480        ctx.external.evaluate("sender_is_lf")
1481    }
1482
1483    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1484    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1485        ctx.external.evaluate("recipient_role_check")
1486    }
1487
1488    /// [7] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) vorhanden
1489    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1490        let sts_segs = ctx.find_segments("STS");
1491        let found = sts_segs.iter().any(|s| {
1492            s.elements
1493                .first()
1494                .and_then(|e| e.first())
1495                .is_some_and(|v| v == "7")
1496                && s.elements
1497                    .get(2)
1498                    .and_then(|e| e.first())
1499                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1500        });
1501        ConditionResult::from(found)
1502    }
1503
1504    /// [8] Wenn für Datenclearing erforderlich
1505    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1506        ctx.external.evaluate("data_clearing_required")
1507    }
1508
1509    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1510    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1511        ctx.external.evaluate("sender_role_check")
1512    }
1513
1514    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1515    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1516        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1517        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1518    }
1519
1520    /// [11] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) nicht vorhanden
1521    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1522        let sts_segs = ctx.find_segments("STS");
1523        let found = sts_segs.iter().any(|s| {
1524            s.elements
1525                .first()
1526                .and_then(|e| e.first())
1527                .is_some_and(|v| v == "7")
1528                && s.elements
1529                    .get(2)
1530                    .and_then(|e| e.first())
1531                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1532        });
1533        ConditionResult::from(!found)
1534    }
1535
1536    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1537    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1538        let dtm_segs = ctx.find_segments("DTM");
1539        let found = dtm_segs.iter().any(|s| {
1540            s.elements
1541                .first()
1542                .and_then(|e| e.first())
1543                .is_some_and(|v| v == "471")
1544        });
1545        ConditionResult::from(!found)
1546    }
1547
1548    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1549    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1550        let sts_segs = ctx.find_segments("STS");
1551        let found = sts_segs.iter().any(|s| {
1552            s.elements
1553                .first()
1554                .and_then(|e| e.first())
1555                .is_some_and(|v| v == "E01")
1556                && s.elements
1557                    .get(2)
1558                    .and_then(|e| e.first())
1559                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1560        });
1561        ConditionResult::from(!found)
1562    }
1563
1564    /// [14] Wenn Datum bekannt
1565    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1566        ctx.external.evaluate("date_known")
1567    }
1568
1569    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1570    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1571        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1572        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1573        let found = seq_segs.iter().any(|s| {
1574            s.elements
1575                .first()
1576                .and_then(|e| e.first())
1577                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1578        });
1579        ConditionResult::from(found)
1580    }
1581
1582    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1583    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1584        let sts_segs = ctx.find_segments("STS");
1585        let found = sts_segs.iter().any(|s| {
1586            s.elements
1587                .first()
1588                .and_then(|e| e.first())
1589                .is_some_and(|v| v == "E01")
1590                && s.elements
1591                    .get(2)
1592                    .and_then(|e| e.first())
1593                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1594        });
1595        ConditionResult::from(found)
1596    }
1597
1598    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1599    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1600        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1601        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1602        let found = seq_segs.iter().any(|s| {
1603            s.elements
1604                .first()
1605                .and_then(|e| e.first())
1606                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1607        });
1608        ConditionResult::from(found)
1609    }
1610
1611    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1612    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1613        let dtm_segs = ctx.find_segments("DTM");
1614        let found = dtm_segs.iter().any(|s| {
1615            s.elements
1616                .first()
1617                .and_then(|e| e.first())
1618                .is_some_and(|v| v == "93")
1619        });
1620        ConditionResult::from(!found)
1621    }
1622
1623    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1624    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1625        // HAND-EDITED (#336): the SEQ alone says nothing about a Prognose auf Basis von Werten; the CCI of one of its
1626        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1627        if ctx.navigator().is_none() {
1628            let seq = ctx
1629                .find_segments("SEQ")
1630                .iter()
1631                .any(|s| ["Z01", "Z98"].contains(&s.get_element(0)));
1632            return ConditionResult::from(
1633                seq && sg4_instances::has_cci(
1634                    &ctx.find_segments("CCI")
1635                        .into_iter()
1636                        .cloned()
1637                        .collect::<Vec<_>>(),
1638                    "",
1639                    "ZC0",
1640                ),
1641            );
1642        }
1643        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01", "Z98"], "", "ZC0"))
1644    }
1645
1646    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1647    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1648        // HAND-EDITED (#336): the SEQ alone is every MaLo, consumption or not; the CCI of one of its
1649        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1650        if ctx.navigator().is_none() {
1651            let seq = ctx
1652                .find_segments("SEQ")
1653                .iter()
1654                .any(|s| ["Z01"].contains(&s.get_element(0)));
1655            return ConditionResult::from(
1656                seq && sg4_instances::has_cci(
1657                    &ctx.find_segments("CCI")
1658                        .into_iter()
1659                        .cloned()
1660                        .collect::<Vec<_>>(),
1661                    "Z30",
1662                    "Z07",
1663                ),
1664            );
1665        }
1666        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z07"))
1667    }
1668
1669    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1670    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1671        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1672        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1673        let found = cci_segs.iter().any(|s| {
1674            s.elements
1675                .get(2)
1676                .and_then(|e| e.first())
1677                .is_some_and(|v| v == "ZA6")
1678        });
1679        ConditionResult::from(found)
1680    }
1681
1682    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1683    // HAND-EDITED: the value is one of the Zeitraum-IDs (DE1156) of the SG6
1684    // RFF+Z47/Z48/Z49 of its SG4. The generated code compared those IDs with
1685    // the SG8 SEQ DE1050 and never looked at the value.
1686    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1687        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
1688            return ConditionResult::Unknown;
1689        };
1690        let found = ["Z47", "Z48", "Z49"].iter().any(|q| {
1691            ctx.scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, q)
1692                .iter()
1693                .any(|s| s.get_component(0, 2) == value)
1694        });
1695        ConditionResult::from(found)
1696    }
1697
1698    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1699    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1700        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1701        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1702        let found = sts_segs.iter().any(|s| {
1703            s.elements
1704                .first()
1705                .and_then(|e| e.first())
1706                .is_some_and(|v| v == "E01")
1707                && s.elements
1708                    .get(2)
1709                    .and_then(|e| e.first())
1710                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1711        });
1712        ConditionResult::from(found)
1713    }
1714
1715    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1716    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1717        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1718        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1719        let found = sts_segs.iter().any(|s| {
1720            s.elements
1721                .first()
1722                .and_then(|e| e.first())
1723                .is_some_and(|v| v == "E01")
1724                && s.elements
1725                    .get(2)
1726                    .and_then(|e| e.first())
1727                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1728        });
1729        ConditionResult::from(found)
1730    }
1731
1732    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1733    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1734        // TODO: implement
1735        ConditionResult::Unknown
1736    }
1737
1738    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1739    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1740        let dtm_segs = ctx.find_segments("DTM");
1741        let found = dtm_segs.iter().any(|s| {
1742            s.elements
1743                .first()
1744                .and_then(|e| e.first())
1745                .is_some_and(|v| v == "Z01")
1746                && s.elements
1747                    .first()
1748                    .and_then(|e| e.get(1))
1749                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1750        });
1751        ConditionResult::from(found)
1752    }
1753
1754    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1755    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1756        // Antwort auf Aktivierung — depends on PID context
1757        ctx.external.evaluate("activation_response")
1758    }
1759
1760    /// [31] Wenn eine Korrektur erfolgt
1761    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1762        ctx.external.evaluate("correction_in_progress")
1763    }
1764
1765    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1766    // HAND-EDITED: counts the SG8 SEQ+Z78 (Referenz auf die
1767    // Lokationsbündelstruktur) of this SG4. The generated code counted every
1768    // SEQ of the message, so any 55043 held it (reported by mako-twin).
1769    // HAND-EDITED (#392): 55177/55180 give the reference as SEQ+ZC7
1770    // (erwartet), others as ZC8 (im System vorhanden) or ZD5 (informativ);
1771    // counting Z78 alone made [32] false there and refused the numbers of
1772    // several bundles. One bundle may stand once as erwartet and once as im
1773    // System, so the count is per variant.
1774    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1775        let several = ["Z78", "ZC7", "ZC8", "ZD5"].iter().any(|code| {
1776            ctx.scoped_find_segments_in_with_qualifier("SG4", "SEQ", 0, code)
1777                .len()
1778                > 1
1779        });
1780        ConditionResult::from(several)
1781    }
1782
1783    /// [33] Wenn in einer SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6 \"Produkte zur Bestellung / Änderung von Date...
1784    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1785        ctx.external.evaluate("code_list_membership_check")
1786    }
1787
1788    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1789    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1790        // Antwort auf Deaktivierung — depends on PID context
1791        ctx.external.evaluate("deactivation_response")
1792    }
1793
1794    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1795    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1796        let dtm_segs = ctx.find_segments("DTM");
1797        let found = dtm_segs.iter().any(|s| {
1798            s.elements
1799                .first()
1800                .and_then(|e| e.first())
1801                .is_some_and(|v| v == "Z01")
1802                && s.elements
1803                    .first()
1804                    .and_then(|e| e.get(1))
1805                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1806        });
1807        ConditionResult::from(found)
1808    }
1809
1810    /// [36] Wenn in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTI...
1811    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1812        ctx.external.evaluate("code_list_membership_check")
1813    }
1814
1815    /// [37] Wenn Anmeldung/ Änderung befristet
1816    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1817        // Same check as [10]: STS element[4] is befristete Anmeldung code
1818        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1819    }
1820
1821    /// [38] Es sind nur die Code der Produkteigenschaft zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel  6.1 \"Anmeldung ei...
1822    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1823        ctx.external.evaluate("code_list_membership_check")
1824    }
1825
1826    /// [39] Wenn in derselben SG8 SEQ+Z79 m PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTIL...
1827    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1828        ctx.external.evaluate("code_list_membership_check")
1829    }
1830
1831    /// [40] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeld...
1832    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1833        ctx.external.evaluate("code_list_membership_check")
1834    }
1835
1836    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1837    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1838        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1839        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1840        if seq_z79.is_empty() {
1841            return ConditionResult::Unknown;
1842        }
1843        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1844        let found = seq_z79.iter().any(|s| {
1845            s.elements
1846                .get(1)
1847                .and_then(|e| e.first())
1848                .is_some_and(|v| !v.is_empty())
1849        });
1850        ConditionResult::from(found)
1851    }
1852
1853    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1854    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1855        let seq_segs = ctx.find_segments("SEQ");
1856        let zh0_count = seq_segs
1857            .iter()
1858            .filter(|s| {
1859                s.elements
1860                    .first()
1861                    .and_then(|e| e.first())
1862                    .is_some_and(|v| v == "ZH0")
1863            })
1864            .count();
1865        ConditionResult::from(zh0_count > 1)
1866    }
1867
1868    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1869    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1870        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1871        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1872        let found = sts_segs.iter().any(|s| {
1873            s.elements
1874                .first()
1875                .and_then(|e| e.first())
1876                .is_some_and(|v| v == "E01")
1877                && s.elements
1878                    .get(2)
1879                    .and_then(|e| e.first())
1880                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1881        });
1882        ConditionResult::from(found)
1883    }
1884
1885    /// [44] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: "Gültige Daten", "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN DE1154 ((Referenz Vo...
1886    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1887        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1888        ConditionResult::True
1889    }
1890
1891    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1892    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1893        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1894        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1895        let found = cci_segs.iter().any(|s| {
1896            s.elements
1897                .first()
1898                .and_then(|e| e.first())
1899                .is_some_and(|v| v == "Z01")
1900                && s.elements
1901                    .get(2)
1902                    .and_then(|e| e.first())
1903                    .is_some_and(|v| v == "Z82")
1904        });
1905        ConditionResult::from(found)
1906    }
1907
1908    /// [46] Wenn ID für Objekt vergeben wurde
1909    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1910        // TODO: implement
1911        ConditionResult::Unknown
1912    }
1913
1914    /// [47] Wenn SG10 CCI+15++Z21 (Summenzeitreihentyp) CAV+DBA/ VZR/ EGS/ LGS/ SLS/ SES/ TLS/ TES/ BIL/ BIP/ BIT/ GAL/ GAP/ GAT/ GEL/ GEP/ GET/ SOL/ SOP/ SOT/ WFL/ WFP/ WFT/ WNL/ WNP/ WNT vorhanden
1915    // REVIEW: Iterates all SG8 instances and their SG10 children looking for CCI with elements[0][0]=="15" and elements[2][0]=="Z21" (Zeitreihentyp), then checks if the same SG10 has a CAV with elements[0][0] in the Summenzeitreihentyp code list. Uses navigator for parent-child group traversal. (medium confidence)
1916    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1917        const CODES: &[&str] = &[
1918            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1919            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1920            "WNP", "WNT",
1921        ];
1922        let nav = match ctx.navigator {
1923            Some(n) => n,
1924            None => return ConditionResult::Unknown,
1925        };
1926        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1927        for i in 0..sg8_count {
1928            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1929            for j in 0..sg10_count {
1930                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1931                let has_cci_15_z21 = ccis.iter().any(|s| {
1932                    s.elements
1933                        .first()
1934                        .and_then(|e: &Vec<String>| e.first())
1935                        .is_some_and(|v: &String| v == "15")
1936                        && s.elements
1937                            .get(2)
1938                            .and_then(|e: &Vec<String>| e.first())
1939                            .is_some_and(|v: &String| v == "Z21")
1940                });
1941                if has_cci_15_z21 {
1942                    let cavs =
1943                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1944                    if cavs.iter().any(|s| {
1945                        s.elements
1946                            .first()
1947                            .and_then(|e: &Vec<String>| e.first())
1948                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1949                    }) {
1950                        return ConditionResult::True;
1951                    }
1952                }
1953            }
1954        }
1955        ConditionResult::False
1956    }
1957
1958    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1959    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1960        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1961        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1962        let found = sts_segs.iter().any(|s| {
1963            s.elements
1964                .first()
1965                .and_then(|e| e.first())
1966                .is_some_and(|v| v == "E01")
1967                && s.elements
1968                    .get(2)
1969                    .and_then(|e| e.first())
1970                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1971        });
1972        ConditionResult::from(found)
1973    }
1974
1975    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1976    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1977        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1978        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1979        let found = sts_segs.iter().any(|s| {
1980            s.elements
1981                .first()
1982                .and_then(|e| e.first())
1983                .is_some_and(|v| v == "E01")
1984                && s.elements
1985                    .get(2)
1986                    .and_then(|e| e.first())
1987                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1988        });
1989        ConditionResult::from(found)
1990    }
1991
1992    /// [50] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) mit derselben Zeitraum-ID im DE1050 auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Infor...
1993    // REVIEW: Collects (Zeitraum-ID from SEQ.elements[1][0], Marktlok-ID from sibling RFF+Z18.elements[0][1]) for all SG8 instances with SEQ+Z98 and SEQ+ZE7. Returns True if any Z98 group has no ZE7 group matching on both values. Uses collect_group_values with instance-index correlation via HashMap, assuming each SG8 has at most one SEQ (guaranteed as entry segment) and the zip ordering is stable. (medium confidence)
1994    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1995        use std::collections::HashMap;
1996
1997        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1998        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1999        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
2000        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
2001
2002        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
2003        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
2004        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
2005            rff_by_instance
2006                .entry(*idx)
2007                .or_default()
2008                .push((q.clone(), id.clone()));
2009        }
2010
2011        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
2012        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
2013        for (idx, z) in &seq_zeitraum {
2014            seq_zeitraum_map.insert(*idx, z.clone());
2015        }
2016
2017        let z98_data: Vec<(String, String)> = seq_quals
2018            .iter()
2019            .filter(|(_, q)| q == "Z98")
2020            .map(|(idx, _)| {
2021                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2022                let marktlok = rff_by_instance
2023                    .get(idx)
2024                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2025                    .map(|(_, id)| id.clone())
2026                    .unwrap_or_default();
2027                (zeitraum, marktlok)
2028            })
2029            .collect();
2030
2031        if z98_data.is_empty() {
2032            return ConditionResult::Unknown;
2033        }
2034
2035        let ze7_data: Vec<(String, String)> = seq_quals
2036            .iter()
2037            .filter(|(_, q)| q == "ZE7")
2038            .map(|(idx, _)| {
2039                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2040                let marktlok = rff_by_instance
2041                    .get(idx)
2042                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2043                    .map(|(_, id)| id.clone())
2044                    .unwrap_or_default();
2045                (zeitraum, marktlok)
2046            })
2047            .collect();
2048
2049        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2050            !ze7_data
2051                .iter()
2052                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2053        }))
2054    }
2055
2056    /// [51] Wenn die Marktlokations-ID im SG8 RFF+Z18 derselben SG8 SEQ+Z98 (Informative Daten der Marktlokation) mit derselben Zeitraum-ID im DE1050 nicht auf ein SG8 SEQ+Z78 (Referenz auf die Lokationsbünde...
2057    // REVIEW: Condition is True when no Z98 SG8 (Informative Daten der Marktlokation) references — via matching Zeitraum-ID in SEQ.C286.DE1050 — a Z78 SG8 (Referenz auf Lokationsbündelstruktur) that carries the pauschal bundle code 9992000000018 in RFF+Z31.DE1154. Requires navigator for cross-SG8 Zeitraum-ID correlation. (medium confidence)
2058    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2059        // HAND-EDITED: "derselben SG8 SEQ+Z98 mit derselben Zeitraum-ID … nicht auf ein SG8 SEQ+Z78" — compared within the enclosing SG4 when the scope
2060        // reaches an SG8; the message as before otherwise.
2061        if let Some(scope) = ctx.enclosing_scope("SG8") {
2062            let Some((_, zeitraum)) =
2063                sg4_instances::seq_at(ctx, scope).filter(|(code, _)| code == "Z98")
2064            else {
2065                return ConditionResult::False;
2066            };
2067            let pauschal = sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
2068                sg4_instances::seq_at(ctx, p)
2069                    .is_some_and(|(c, z)| c == "Z78" && !z.is_empty() && z == zeitraum)
2070                    && sg4_instances::rff_values_at(ctx, p, "Z31")
2071                        .iter()
2072                        .any(|v| v.replace(' ', "") == "9992000000018")
2073            });
2074            return ConditionResult::from(!pauschal);
2075        }
2076        let nav = match ctx.navigator() {
2077            Some(n) => n,
2078            None => return ConditionResult::Unknown,
2079        };
2080        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2081        let pauschal_id = "9992000000018";
2082        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2083        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2084        for i in 0..sg8_count {
2085            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2086            let has_z78 = seqs.iter().any(|s| {
2087                s.elements
2088                    .first()
2089                    .and_then(|e| e.first())
2090                    .is_some_and(|v| v == "Z78")
2091            });
2092            if !has_z78 {
2093                continue;
2094            }
2095            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2096            let has_pauschal = rffs.iter().any(|s| {
2097                s.elements
2098                    .first()
2099                    .and_then(|e| e.first())
2100                    .is_some_and(|v| v == "Z31")
2101                    && s.elements
2102                        .first()
2103                        .and_then(|e| e.get(1))
2104                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2105            });
2106            if has_pauschal {
2107                for seq in &seqs {
2108                    if seq
2109                        .elements
2110                        .first()
2111                        .and_then(|e| e.first())
2112                        .is_some_and(|v| v == "Z78")
2113                    {
2114                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2115                            if !zid.is_empty() {
2116                                pauschal_zeitraum_ids.push(zid.clone());
2117                            }
2118                        }
2119                    }
2120                }
2121            }
2122        }
2123        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2124        for i in 0..sg8_count {
2125            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2126            for seq in &seqs {
2127                if seq
2128                    .elements
2129                    .first()
2130                    .and_then(|e| e.first())
2131                    .is_some_and(|v| v == "Z98")
2132                {
2133                    let zid = seq
2134                        .elements
2135                        .get(1)
2136                        .and_then(|e| e.first())
2137                        .map(|s| s.as_str())
2138                        .unwrap_or("");
2139                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2140                        // Z98 SG8 references a pauschal Z78 → condition is False
2141                        return ConditionResult::False;
2142                    }
2143                }
2144            }
2145        }
2146        // No Z98 SG8 references the pauschal bundle → condition is True
2147        ConditionResult::True
2148    }
2149
2150    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2151    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2152        let cci_segs = ctx.find_segments("CCI");
2153        let found = cci_segs.iter().any(|s| {
2154            s.elements
2155                .first()
2156                .and_then(|e| e.first())
2157                .is_some_and(|v| v == "Z07")
2158                && s.elements
2159                    .get(2)
2160                    .and_then(|e| e.first())
2161                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2162        });
2163        ConditionResult::from(found)
2164    }
2165
2166    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2167    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2168        // TODO: implement
2169        ConditionResult::Unknown
2170    }
2171
2172    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2173    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2174        // HAND-EDITED (#336): the SEQ alone is every MaLo, generation or not; the CCI of one of its
2175        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
2176        if ctx.navigator().is_none() {
2177            let seq = ctx
2178                .find_segments("SEQ")
2179                .iter()
2180                .any(|s| ["Z01"].contains(&s.get_element(0)));
2181            return ConditionResult::from(
2182                seq && sg4_instances::has_cci(
2183                    &ctx.find_segments("CCI")
2184                        .into_iter()
2185                        .cloned()
2186                        .collect::<Vec<_>>(),
2187                    "Z30",
2188                    "Z06",
2189                ),
2190            );
2191        }
2192        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z06"))
2193    }
2194
2195    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2196    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2197        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2198        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2199        let found = sts_segs.iter().any(|s| {
2200            s.elements
2201                .first()
2202                .and_then(|e| e.first())
2203                .is_some_and(|v| v == "E01")
2204                && s.elements
2205                    .get(2)
2206                    .and_then(|e| e.first())
2207                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2208        });
2209        ConditionResult::from(found)
2210    }
2211
2212    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2213    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2214        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2215        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2216        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2217        let found = sts_segs.iter().any(|s| {
2218            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2219                [
2220                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2221                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2222                ]
2223                .contains(&v.as_str())
2224            })
2225        });
2226        ConditionResult::from(found)
2227    }
2228
2229    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2230    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2231        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2232        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2233        let found = cci_segs.iter().any(|s| {
2234            s.elements
2235                .first()
2236                .and_then(|e| e.first())
2237                .is_some_and(|v| v == "Z45")
2238                && s.elements
2239                    .get(2)
2240                    .and_then(|e| e.first())
2241                    .is_some_and(|v| v == "ZD9")
2242        });
2243        ConditionResult::from(found)
2244    }
2245
2246    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2247    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2248        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2249        // in tree validation), else the enclosing SG10's CCI.
2250        let has_293 =
2251            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2252        if let Some(own) = ctx.resolved_segment.filter(|_| {
2253            ctx.scoped_find_segments_in("SG10", "CCI")
2254                .iter()
2255                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2256        }) {
2257            return ConditionResult::from(has_293(own));
2258        }
2259        ConditionResult::from(
2260            ctx.scoped_find_segments_in("SG10", "CCI")
2261                .iter()
2262                .any(|s| has_293(&s.elements)),
2263        )
2264    }
2265
2266    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2267    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2268        let bgm_segs = ctx.find_segments("BGM");
2269        let found = bgm_segs.iter().any(|s| {
2270            s.elements
2271                .first()
2272                .and_then(|e| e.first())
2273                .is_some_and(|v| v == "Z90")
2274        });
2275        ConditionResult::from(found)
2276    }
2277
2278    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2279    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2280        let bgm_segs = ctx.find_segments("BGM");
2281        let found = bgm_segs.iter().any(|s| {
2282            s.elements
2283                .first()
2284                .and_then(|e| e.first())
2285                .is_some_and(|v| v == "Z89")
2286        });
2287        ConditionResult::from(found)
2288    }
2289
2290    /// [62] Wenn mehr als eine SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit einer Gruppen-/Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), ...
2291    // REVIEW: Iterates SG8 instances within each SG4, finds those with SEQ+Z45 and a non-empty Zeitraum-ID in elements[1][0]. Checks if PIA+Z02 in the same SG8 has a product code (elements[1][0]) beginning with 1-08-1 through 1-08-5. Groups by Zeitraum-ID and checks if any ID appears more than once. Requires navigator for per-instance group segment access; returns Unknown without one. (medium confidence)
2292    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2293        let nav = match ctx.navigator {
2294            Some(n) => n,
2295            None => return ConditionResult::Unknown,
2296        };
2297        use std::collections::HashMap;
2298        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2299        let sg4_count = nav.group_instance_count(&["SG4"]);
2300        for sg4_idx in 0..sg4_count {
2301            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2302            for sg8_idx in 0..sg8_count {
2303                let seqs =
2304                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2305                let zeitraum_opt = seqs
2306                    .iter()
2307                    .filter(|s| {
2308                        s.elements
2309                            .first()
2310                            .and_then(|e: &Vec<String>| e.first())
2311                            .is_some_and(|v: &String| v == "Z45")
2312                    })
2313                    .filter_map(|s| {
2314                        s.elements
2315                            .get(1)
2316                            .and_then(|e: &Vec<String>| e.first())
2317                            .filter(|v| !v.is_empty())
2318                            .cloned()
2319                    })
2320                    .next();
2321                let Some(zid) = zeitraum_opt else { continue };
2322                let pias =
2323                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2324                let has_qualifying_pia = pias.iter().any(|s| {
2325                    s.elements
2326                        .first()
2327                        .and_then(|e: &Vec<String>| e.first())
2328                        .is_some_and(|v: &String| v == "Z02")
2329                        && s.elements
2330                            .get(1)
2331                            .and_then(|e: &Vec<String>| e.first())
2332                            .map(|code| {
2333                                code.starts_with("1-08-1")
2334                                    || code.starts_with("1-08-2")
2335                                    || code.starts_with("1-08-3")
2336                                    || code.starts_with("1-08-4")
2337                                    || code.starts_with("1-08-5")
2338                            })
2339                            .unwrap_or(false)
2340                });
2341                if has_qualifying_pia {
2342                    *count_by_zid.entry(zid).or_insert(0) += 1;
2343                }
2344            }
2345        }
2346        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2347    }
2348
2349    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2350    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2351        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2352        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2353        let found = sts_segs.iter().any(|s| {
2354            s.elements
2355                .first()
2356                .and_then(|e| e.first())
2357                .is_some_and(|v| v == "E01")
2358                && s.elements
2359                    .get(2)
2360                    .and_then(|e| e.first())
2361                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2362        });
2363        ConditionResult::from(found)
2364    }
2365
2366    /// [64] Wenn mehr als eine SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) mit einer Gruppen-/ Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5...
2367    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2368        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2369        let pia_segs = ctx.find_segments("PIA");
2370        let count = pia_segs
2371            .iter()
2372            .filter(|s| {
2373                s.elements
2374                    .first()
2375                    .and_then(|e| e.first())
2376                    .is_some_and(|v| v == "Z02")
2377                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2378                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2379                            .iter()
2380                            .any(|p| v.starts_with(p))
2381                    })
2382            })
2383            .count();
2384        ConditionResult::from(count > 1)
2385    }
2386
2387    /// [65] Wenn in derselben SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, v...
2388    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2389        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2390        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2391        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2392        let found = pia_segs.iter().any(|s| {
2393            s.elements
2394                .first()
2395                .and_then(|e| e.first())
2396                .is_some_and(|v| v == "Z02")
2397                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2398                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2399                        .iter()
2400                        .any(|p| v.starts_with(p))
2401                })
2402        });
2403        ConditionResult::from(found)
2404    }
2405
2406    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2407    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2408        let seq_segs = ctx.find_segments("SEQ");
2409        let count = seq_segs
2410            .iter()
2411            .filter(|s| {
2412                s.elements
2413                    .first()
2414                    .and_then(|e| e.first())
2415                    .is_some_and(|v| v == "ZH0")
2416            })
2417            .count();
2418        ConditionResult::from(count > 1)
2419    }
2420
2421    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpaketes CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
2422    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2423        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2424        let cav_segs = ctx.find_segments("CAV");
2425        let found = cav_segs.iter().any(|s| {
2426            s.elements
2427                .first()
2428                .and_then(|e| e.first())
2429                .is_some_and(|v| v == "ZH9")
2430                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2431                && s.elements
2432                    .first()
2433                    .and_then(|e| e.get(3))
2434                    .is_some_and(|v| v == "9991000002933")
2435        });
2436        ConditionResult::from(!found)
2437    }
2438
2439    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2440    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2441        let seq_segs = ctx.find_segments("SEQ");
2442        let count = seq_segs
2443            .iter()
2444            .filter(|s| {
2445                s.elements
2446                    .first()
2447                    .and_then(|e| e.first())
2448                    .is_some_and(|v| v == "ZH0")
2449            })
2450            .count();
2451        ConditionResult::from(count > 2)
2452    }
2453
2454    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2455    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2456        let seq_segs = ctx.find_segments("SEQ");
2457        let count = seq_segs
2458            .iter()
2459            .filter(|s| {
2460                s.elements
2461                    .first()
2462                    .and_then(|e| e.first())
2463                    .is_some_and(|v| v == "ZH0")
2464            })
2465            .count();
2466        ConditionResult::from(count > 3)
2467    }
2468
2469    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2470    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2471        let seq_segs = ctx.find_segments("SEQ");
2472        let count = seq_segs
2473            .iter()
2474            .filter(|s| {
2475                s.elements
2476                    .first()
2477                    .and_then(|e| e.first())
2478                    .is_some_and(|v| v == "ZH0")
2479            })
2480            .count();
2481        ConditionResult::from(count == 5)
2482    }
2483
2484    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2485    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2486        // Antwort auf Zuordnung — depends on PID context
2487        ctx.external.evaluate("assignment_response")
2488    }
2489
2490    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2491    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2492        // Antwort auf Beendigung der Zuordnung — depends on PID context
2493        ctx.external.evaluate("assignment_end_response")
2494    }
2495
2496    /// [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist, der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist
2497    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2498    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2499    // Regenerating this function should preserve the shape, but re-verify
2500    // against the committed body if generate-conditions is run.
2501    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2502        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2503        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2504        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2505        //
2506        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2507        // narrows to the enclosing SG8 instance; otherwise falls back to
2508        // message-wide find_segments. Matches the AHB wording
2509        // "in derselben SG8 im PIA DE7140 ein Code...".
2510        use crate::eval::CodelistColumn;
2511        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2512            let is_z02 = s
2513                .elements
2514                .first()
2515                .and_then(|e| e.first())
2516                .is_some_and(|v| v == "Z02");
2517            if !is_z02 {
2518                return false;
2519            }
2520            let code = match s.elements.get(1).and_then(|e| e.first()) {
2521                Some(c) if !c.is_empty() => c,
2522                _ => return false,
2523            };
2524            ctx.codelist
2525                .flag(code, CodelistColumn::UtilmdPreisangabe)
2526                .unwrap_or(false)
2527        });
2528        ConditionResult::from(any_match)
2529    }
2530
2531    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2532    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2533        let seq_segs = ctx.find_segments("SEQ");
2534        let has_seq = seq_segs.iter().any(|s| {
2535            s.elements
2536                .first()
2537                .and_then(|e| e.first())
2538                .is_some_and(|v| v == "Z59")
2539        });
2540        let pia_segs = ctx.find_segments("PIA");
2541        let has_pia = pia_segs.iter().any(|s| {
2542            s.elements
2543                .first()
2544                .and_then(|e| e.first())
2545                .is_some_and(|v| v == "5")
2546        });
2547        ConditionResult::from(has_seq && has_pia)
2548    }
2549
2550    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2551    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2552        ctx.external.evaluate("code_list_membership_check")
2553    }
2554
2555    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2556    // REVIEW: SEQ+Z49 is 'Abgerechnete Daten der Bilanzierungsgebietssummenzeitreihe' — the only SEQ code in the reference that explicitly names a Bilanzierungsgebiet-level Summenzeitreihe. Checking its presence is the most direct mapping of the condition text. A broader interpretation involving CAV codes (Bezeichnung der Summenzeitreihe) is possible but those codes are not fully enumerated in the provided reference. (medium confidence)
2557    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2558        ctx.has_qualifier("SEQ", 0, "Z49")
2559    }
2560
2561    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2562    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2563        let seq_segs = ctx.find_segments("SEQ");
2564        let found = seq_segs.iter().any(|s| {
2565            s.elements
2566                .first()
2567                .and_then(|e| e.first())
2568                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2569        });
2570        ConditionResult::from(!found)
2571    }
2572
2573    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2574    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2575        let sts_segs = ctx.find_segments("STS");
2576        let found = sts_segs.iter().any(|s| {
2577            s.elements
2578                .first()
2579                .and_then(|e| e.first())
2580                .is_some_and(|v| v == "7")
2581                && s.elements
2582                    .get(2)
2583                    .and_then(|e| e.first())
2584                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2585        });
2586        ConditionResult::from(!found)
2587    }
2588
2589    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2590    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2591        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2592        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2593        let found = sts_segs.iter().any(|s| {
2594            s.elements
2595                .first()
2596                .and_then(|e| e.first())
2597                .is_some_and(|v| v == "E01")
2598                && s.elements
2599                    .get(2)
2600                    .and_then(|e| e.first())
2601                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2602        });
2603        ConditionResult::from(found)
2604    }
2605
2606    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2607    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2608        let sts_segs = ctx.find_segments("STS");
2609        let found = sts_segs.iter().any(|s| {
2610            s.elements
2611                .first()
2612                .and_then(|e| e.first())
2613                .is_some_and(|v| v == "E01")
2614                && s.elements
2615                    .get(2)
2616                    .and_then(|e| e.first())
2617                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2618        });
2619        ConditionResult::from(found)
2620    }
2621
2622    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2623    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2624        let dtm_segs = ctx.find_segments("DTM");
2625        let found = dtm_segs.iter().any(|s| {
2626            s.elements
2627                .first()
2628                .and_then(|e| e.first())
2629                .is_some_and(|v| v == "Z01")
2630                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2631                    v.chars()
2632                        .nth(3)
2633                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2634                })
2635        });
2636        ConditionResult::from(found)
2637    }
2638
2639    /// [86] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der...
2640    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2641        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" for this SG6's RFF+Z50 — compared within the enclosing SG4 when the scope
2642        // reaches an SG6; the message as before otherwise.
2643        if let Some(scope) = ctx.enclosing_scope("SG6") {
2644            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
2645            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
2646                sg4_instances::has_cci(s, "", "ZA6")
2647            }));
2648        }
2649        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2650        ctx.groups_share_qualified_value(
2651            "RFF",
2652            0,
2653            "Z50",
2654            0,
2655            2,
2656            &["SG4", "SG6"],
2657            "RFF",
2658            0,
2659            2,
2660            &["SG4", "SG8"],
2661        )
2662    }
2663
2664    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2665    // HAND-EDITED: judges the DTM+157 value in German local time. The generated
2666    // code looked for raw `DD=01`/`HHMM=0000` in any DTM, which a correct UTC
2667    // value (`202610312300+00`) never has (mako-twin, 55018).
2668    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2669        use chrono::{Datelike, Timelike};
2670        let value = match ctx.resolved_value.filter(|v| !v.is_empty()) {
2671            Some(v) => v.to_string(),
2672            None => {
2673                let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2674                match dtm157.first() {
2675                    Some(dtm) => dtm.get_component(0, 1).to_string(),
2676                    None => return ConditionResult::Unknown,
2677                }
2678            }
2679        };
2680        match german_local_time(&value) {
2681            Some(t) => ConditionResult::from(t.day() == 1 && t.hour() == 0 && t.minute() == 0),
2682            None => ConditionResult::False,
2683        }
2684    }
2685
2686    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2687    // HAND-EDITED: compares the value; the generated code only checked that a
2688    // DTM+157 exists.
2689    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2690        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
2691            return ConditionResult::Unknown;
2692        };
2693        let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2694        match dtm157.first() {
2695            Some(dtm) => ConditionResult::from(dtm.get_component(0, 1) == value),
2696            None => ConditionResult::Unknown,
2697        }
2698    }
2699
2700    /// [89] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in diesem SG8, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+...
2701    // REVIEW: Checks whether any SG8 instance contains an SG10 child with CCI at elements[2][0]=ZA6 and CAV at elements[0][0]=E14. The condition text requires the Zeitraum-ID in DE1050 to match 'this SG8', but a per-group evaluation context is not available in the current API — the Zeitraum-ID cross-reference is therefore not enforced and is instead approximated as a message-wide check. The CCI qualifier position (elements[2][0]) is confirmed by the MIG reference for CCI segments in SG10. (medium confidence)
2702    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2703        let nav = match ctx.navigator {
2704            Some(n) => n,
2705            None => {
2706                return ctx.filtered_parent_child_has_qualifier(
2707                    &["SG4", "SG8"],
2708                    "SEQ",
2709                    0,
2710                    "Z01",
2711                    "SG10",
2712                    "CCI",
2713                    2,
2714                    "ZA6",
2715                )
2716            }
2717        };
2718        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2719        for i in 0..sg8_count {
2720            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2721            for j in 0..sg10_count {
2722                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2723                let has_cci_za6 = ccis.iter().any(|s| {
2724                    s.elements
2725                        .get(2)
2726                        .and_then(|e: &Vec<String>| e.first())
2727                        .is_some_and(|v: &String| v == "ZA6")
2728                });
2729                if !has_cci_za6 {
2730                    continue;
2731                }
2732                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2733                if cavs.iter().any(|s| {
2734                    s.elements
2735                        .first()
2736                        .and_then(|e: &Vec<String>| e.first())
2737                        .is_some_and(|v: &String| v == "E14")
2738                }) {
2739                    return ConditionResult::True;
2740                }
2741            }
2742        }
2743        ConditionResult::False
2744    }
2745
2746    /// [90] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2747    // REVIEW: Within each SG8, iterates all SG10 child instances. For each SG10 that has a CCI with elements[2][0]=ZA6, checks the CAV elements[0][0] value. Tracks whether E14 and E02 have both been seen within the same SG8. Returns True when both are found in a single SG8, satisfying the requirement that both Prognosegrundlage options appear 'in dieser SG8'. (medium confidence)
2748    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2749        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2750        // scope reaches one; the message as before otherwise.
2751        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2752            return ConditionResult::from(
2753                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2754                    .unwrap_or_default()
2755                    .iter()
2756                    .any(|segs| {
2757                        segs.iter()
2758                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2759                            && segs
2760                                .iter()
2761                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2762                    })
2763                    && ctx
2764                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2765                        .unwrap_or_default()
2766                        .iter()
2767                        .any(|segs| {
2768                            segs.iter()
2769                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2770                                && segs
2771                                    .iter()
2772                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2773                        }),
2774            );
2775        }
2776        let nav = match ctx.navigator {
2777            Some(n) => n,
2778            None => return ConditionResult::Unknown,
2779        };
2780        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2781        for i in 0..sg8_count {
2782            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2783            let mut has_za6_e14 = false;
2784            let mut has_za6_e02 = false;
2785            for j in 0..sg10_count {
2786                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2787                let has_cci_za6 = ccis.iter().any(|s| {
2788                    s.elements
2789                        .get(2)
2790                        .and_then(|e: &Vec<String>| e.first())
2791                        .is_some_and(|v: &String| v == "ZA6")
2792                });
2793                if !has_cci_za6 {
2794                    continue;
2795                }
2796                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2797                for cav in &cavs {
2798                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2799                        if code == "E14" {
2800                            has_za6_e14 = true;
2801                        }
2802                        if code == "E02" {
2803                            has_za6_e02 = true;
2804                        }
2805                    }
2806                }
2807            }
2808            if has_za6_e14 && has_za6_e02 {
2809                return ConditionResult::True;
2810            }
2811        }
2812        ConditionResult::False
2813    }
2814
2815    /// [91] Wenn nicht SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/ TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2816    // REVIEW: Logical negation of condition 90. Condition 91 is explicitly defined as 'Wenn NICHT (CCI+++ZA6 einmal mit CAV+E14 und einmal mit CAV+E02 in dieser SG8 vorhanden)', which is the exact complement of condition 90. Preserving Unknown propagation handles the no-navigator fallback case correctly. (medium confidence)
2817    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2818        match self.evaluate_90(ctx) {
2819            ConditionResult::True => ConditionResult::False,
2820            ConditionResult::False => ConditionResult::True,
2821            ConditionResult::Unknown => ConditionResult::Unknown,
2822        }
2823    }
2824
2825    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2826    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2827        // TODO: implement
2828        ConditionResult::Unknown
2829    }
2830
2831    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2832    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2833        // TODO: implement
2834        ConditionResult::Unknown
2835    }
2836
2837    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2838    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2839        // Structural: segment exists within SG (always true for valid messages)
2840        ConditionResult::True
2841    }
2842
2843    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2844    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2845        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2846        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2847        let has_cci = cci_segs.iter().any(|s| {
2848            s.elements
2849                .first()
2850                .and_then(|e| e.first())
2851                .is_some_and(|v| v == "Z17")
2852        });
2853        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2854        let has_cav = cav_segs.iter().any(|s| {
2855            s.elements
2856                .first()
2857                .and_then(|e| e.first())
2858                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2859        });
2860        ConditionResult::from(has_cci && has_cav)
2861    }
2862
2863    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2864    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2865        let sts_segs = ctx.find_segments("STS");
2866        let found = sts_segs.iter().any(|s| {
2867            s.elements
2868                .first()
2869                .and_then(|e| e.first())
2870                .is_some_and(|v| v == "7")
2871                && s.elements
2872                    .get(3)
2873                    .and_then(|e| e.first())
2874                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2875        });
2876        ConditionResult::from(found)
2877    }
2878
2879    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2880    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2881        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2882        let cav_segs = ctx.find_segments("CAV");
2883        let found = cav_segs.iter().any(|s| {
2884            s.elements
2885                .first()
2886                .and_then(|e| e.first())
2887                .is_some_and(|v| v == "ZH9")
2888                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2889                && s.elements
2890                    .first()
2891                    .and_then(|e| e.get(3))
2892                    .is_some_and(|v| v == "9991000002420")
2893        });
2894        ConditionResult::from(found)
2895    }
2896
2897    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2898    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2899        ctx.external.evaluate("sender_role_check")
2900    }
2901
2902    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2903    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2904        ctx.external.evaluate("sender_role_check")
2905    }
2906
2907    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2908    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2909        let cav_segs = ctx.find_segments("CAV");
2910        let found = cav_segs.iter().any(|s| {
2911            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2912                [
2913                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2914                ]
2915                .contains(&v.as_str())
2916            })
2917        });
2918        ConditionResult::from(found)
2919    }
2920
2921    /// [101] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14/ Z36 (TLP/ TEP/ TEP mit Ref.messung) in dieser SG8 vorhanden
2922    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2923        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2924        sg10_has(ctx, true, |segs| {
2925            sg4_instances::has_cci(segs, "", "ZA6")
2926                && (sg4_instances::has_cav(segs, "E14") || sg4_instances::has_cav(segs, "Z36"))
2927        })
2928    }
2929
2930    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2931    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2932        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2933        sg10_has(ctx, true, |segs| {
2934            sg4_instances::has_cci(segs, "", "ZA6") && sg4_instances::has_cav(segs, "Z36")
2935        })
2936    }
2937
2938    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2939    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2940        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2941        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2942        let found = qty_segs.iter().any(|s| {
2943            s.elements
2944                .first()
2945                .and_then(|e| e.first())
2946                .is_some_and(|v| v == "265")
2947        });
2948        ConditionResult::from(found)
2949    }
2950
2951    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2952    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2953        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2954        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2955        let found = qty_segs.iter().any(|s| {
2956            s.elements
2957                .first()
2958                .and_then(|e| e.first())
2959                .is_some_and(|v| v == "Z08")
2960        });
2961        ConditionResult::from(found)
2962    }
2963
2964    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2965    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2966        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2967        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2968        let found = qty_segs.iter().any(|s| {
2969            s.elements
2970                .first()
2971                .and_then(|e| e.first())
2972                .is_some_and(|v| v == "Z10")
2973        });
2974        ConditionResult::from(found)
2975    }
2976
2977    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2978    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2979        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2980        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2981        ctx.this_group_parent_child_has_qualifier(
2982            &["SG4", "SG8"],
2983            "SEQ",
2984            0,
2985            "Z01",
2986            "SG10",
2987            "CAV",
2988            0,
2989            "E02",
2990        )
2991    }
2992
2993    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2994    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2995        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2996        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2997        let found = cav_segs.iter().any(|s| {
2998            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2999                [
3000                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
3001                ]
3002                .contains(&v.as_str())
3003            })
3004        });
3005        ConditionResult::from(found)
3006    }
3007
3008    /// [108] Wenn in derselben SG8 (Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, vorhanden
3009    // HAND-EDITED — do not let generate-conditions regenerate this function
3010    // without first confirming prompt.rs B6 SCOPE RULES section is in the
3011    // system prompt. Regeneration without that guidance would emit
3012    // ctx.has_qualifier (message-wide) and silently reintroduce the
3013    // per-instance bug this function fixes.
3014    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
3015    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
3016        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
3017        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
3018        // SG8 instance when GroupScope is set; falls back to message-wide.
3019        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
3020        let found = ctx
3021            .scoped_find_segments_in("SG8", "PIA")
3022            .iter()
3023            .filter(|s| {
3024                s.elements
3025                    .first()
3026                    .and_then(|e| e.first())
3027                    .is_some_and(|q| q == "Z02")
3028            })
3029            .any(|s| {
3030                s.elements
3031                    .get(1)
3032                    .and_then(|e| e.first())
3033                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
3034            });
3035        ConditionResult::from(found)
3036    }
3037
3038    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
3039    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
3040        let cav_segs = ctx.find_segments("CAV");
3041        let found = cav_segs.iter().any(|s| {
3042            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
3043                [
3044                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
3045                ]
3046                .contains(&v.as_str())
3047            })
3048        });
3049        ConditionResult::from(found)
3050    }
3051
3052    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3053    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3054        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3055        // scope reaches one; the message as before otherwise.
3056        if let Some(scope) = ctx.enclosing_scope("SG8") {
3057            return ConditionResult::from(
3058                ctx.find_segments_at(scope, "SEQ")
3059                    .iter()
3060                    .any(|s| matches!(s.get_element(0), "Z59"))
3061                    && !ctx
3062                        .find_segments_at(scope, "PIA")
3063                        .iter()
3064                        .any(|s| s.get_element(0) == "5"),
3065            );
3066        }
3067        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3068    }
3069
3070    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3071    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3072        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3073        // scope reaches one; the message as before otherwise.
3074        if let Some(scope) = ctx.enclosing_scope("SG8") {
3075            return ConditionResult::from(
3076                ctx.find_segments_at(scope, "SEQ")
3077                    .iter()
3078                    .any(|s| matches!(s.get_element(0), "Z59"))
3079                    && !ctx
3080                        .find_segments_at(scope, "CCI")
3081                        .iter()
3082                        .any(|s| s.get_element(0) == "11"),
3083            );
3084        }
3085        let result = ctx.filtered_parent_child_has_qualifier(
3086            &["SG4", "SG8"],
3087            "SEQ",
3088            0,
3089            "Z59",
3090            "SG10",
3091            "CCI",
3092            0,
3093            "11",
3094        );
3095        match result {
3096            ConditionResult::True => ConditionResult::False,
3097            ConditionResult::False => ConditionResult::True,
3098            ConditionResult::Unknown => ConditionResult::Unknown,
3099        }
3100    }
3101
3102    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3103    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3104        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3105        ctx.external.evaluate("counting_time_order_response")
3106    }
3107
3108    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3109    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3110        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3111        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3112        spannungsebene_in_this_sg10(ctx, "E06")
3113    }
3114
3115    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3116    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3117        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3118        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3119        spannungsebene_in_this_sg10(ctx, "E05")
3120    }
3121
3122    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3123    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3124        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3125        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3126        spannungsebene_in_this_sg10(ctx, "E04")
3127    }
3128
3129    /// [117] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
3130    // REVIEW: Full condition requires: (1) current SG8 has RFF+Z18=X, (2) another SG8 with SEQ+Z01 also has RFF+Z18=X, (3) that SG8 has SG10 with CCI elements[2][0]='ZA6' AND CAV elements[0][0]='E14'. Implementation simplifies to checking whether any SG8 has SG10 with both CCI+++ZA6 and CAV+E14 co-occurring in the same SG10 instance. The cross-SG RFF+Z18 value correlation and SEQ+Z01 parent check are omitted because the navigator API only exposes child-group segments (SG10 within SG8), not SG8's own entry segments. Medium confidence due to this structural limitation. (medium confidence)
3131    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3132        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (TLP/TEP) — compared within the enclosing SG4 when the scope
3133        // reaches an SG8; the message as before otherwise.
3134        if let Some(scope) = ctx.enclosing_scope("SG8") {
3135            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
3136            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
3137                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E14")
3138            }));
3139        }
3140        let nav = match ctx.navigator {
3141            Some(n) => n,
3142            None => return ConditionResult::Unknown,
3143        };
3144        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3145        for i in 0..sg8_count {
3146            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3147            for j in 0..sg10_count {
3148                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3149                let has_cci_za6 = ccis.iter().any(|s| {
3150                    s.elements
3151                        .get(2)
3152                        .and_then(|e: &Vec<String>| e.first())
3153                        .is_some_and(|v: &String| v == "ZA6")
3154                });
3155                if has_cci_za6 {
3156                    let cavs =
3157                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3158                    if cavs.iter().any(|s| {
3159                        s.elements
3160                            .first()
3161                            .and_then(|e: &Vec<String>| e.first())
3162                            .is_some_and(|v: &String| v == "E14")
3163                    }) {
3164                        return ConditionResult::True;
3165                    }
3166                }
3167            }
3168        }
3169        ConditionResult::False
3170    }
3171
3172    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3173    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3174        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3175        // scope reaches one; the message as before otherwise.
3176        if let Some(scope) = ctx.enclosing_scope("SG8") {
3177            return ConditionResult::from(
3178                ctx.find_segments_at(scope, "SEQ")
3179                    .iter()
3180                    .any(|s| matches!(s.get_element(0), "Z98"))
3181                    && ctx
3182                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3183                        .unwrap_or_default()
3184                        .iter()
3185                        .any(|segs| {
3186                            segs.iter()
3187                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3188                                && segs
3189                                    .iter()
3190                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3191                        }),
3192            );
3193        }
3194        let has_cci = ctx.filtered_parent_child_has_qualifier(
3195            &["SG4", "SG8"],
3196            "SEQ",
3197            0,
3198            "Z98",
3199            "SG10",
3200            "CCI",
3201            2,
3202            "ZA6",
3203        );
3204        if has_cci != ConditionResult::True {
3205            return has_cci;
3206        }
3207        ctx.filtered_parent_child_has_qualifier(
3208            &["SG4", "SG8"],
3209            "SEQ",
3210            0,
3211            "Z98",
3212            "SG10",
3213            "CAV",
3214            0,
3215            "E02",
3216        )
3217    }
3218
3219    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3220    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3221        // HAND-EDITED (#337): the CAV+E02 of the SG10 CCI+++ZA6, as [265] asks
3222        // for its CAV+Z36 — not any CAV+E02 (CCI+++ZC0 CAV+E02 is no profile).
3223        let Some(blocks) = sg8_blocks(ctx) else {
3224            return ConditionResult::Unknown;
3225        };
3226        let found = blocks
3227            .iter()
3228            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
3229            .any(|b| {
3230                b.sg10.iter().any(|(ccis, cavs)| {
3231                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
3232                        && cavs.iter().any(|c| component(c, 0, 0) == Some("E02"))
3233                })
3234            });
3235        ConditionResult::from(found)
3236    }
3237
3238    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3239    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3240        // TODO: implement
3241        ConditionResult::Unknown
3242    }
3243
3244    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3245    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3246        let z03 =
3247            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3248        if z03 == ConditionResult::True {
3249            return ConditionResult::True;
3250        }
3251        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3252    }
3253
3254    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3255    // REVIEW: Uses filtered_parent_child_has_qualifier to find SG8 instances where SEQ+Z98 has an SG10 child with CCI at elements[2]='E03' (Spannungsebene der Marktlokation). Then confirms CAV+E06 (Niederspannung) exists in an SG8 group. The CAV check is group-scoped rather than strictly same-SG10-instance because the navigator API does not directly expose parent-group (SG8) segment access alongside child-group (SG10) checks in one call. (medium confidence)
3256    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3257        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3258        // scope reaches one; the message as before otherwise.
3259        if let Some(scope) = ctx.enclosing_scope("SG8") {
3260            return ConditionResult::from(
3261                ctx.find_segments_at(scope, "SEQ")
3262                    .iter()
3263                    .any(|s| matches!(s.get_element(0), "Z98"))
3264                    && ctx
3265                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3266                        .unwrap_or_default()
3267                        .iter()
3268                        .any(|segs| {
3269                            segs.iter()
3270                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3271                                && segs
3272                                    .iter()
3273                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3274                        }),
3275            );
3276        }
3277        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3278        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3279            &["SG4", "SG8"],
3280            "SEQ",
3281            0,
3282            "Z98",
3283            "SG10",
3284            "CCI",
3285            2,
3286            "E03",
3287        );
3288        if has_cci_e03 != ConditionResult::True {
3289            return has_cci_e03;
3290        }
3291        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3292        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3293    }
3294
3295    /// [123] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit dem SG8 RFF+MG (Gerätenummer des Zählers) auf die gleiche Identifikation/Nummer des Gerätes...
3296    // REVIEW: Scans segments linearly to collect RFF+MG device IDs from each SG8 group with SEQ+Z20 (OBIS-Daten der Zähleinrichtung/Smartmeter-Gateway). A new SEQ starts a new SG8 context; RFF+MG within that context records the device number. Returns True if any device ID appears in 2+ different SG8 groups (indicating multiple OBIS datasets reference the same device). Linear scan is used because the navigator API does not expose a direct method to retrieve segments within a parent SG8 instance by absolute index. (medium confidence)
3297    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3298        struct Sg8Z20Entry {
3299            mg_id: Option<String>,
3300        }
3301        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3302        let mut current: Option<Sg8Z20Entry> = None;
3303        for seg in ctx.segments {
3304            match seg.id.as_str() {
3305                "SEQ" => {
3306                    if let Some(entry) = current.take() {
3307                        entries.push(entry);
3308                    }
3309                    if seg
3310                        .elements
3311                        .first()
3312                        .and_then(|e: &Vec<String>| e.first())
3313                        .is_some_and(|v: &String| v == "Z20")
3314                    {
3315                        current = Some(Sg8Z20Entry { mg_id: None });
3316                    }
3317                }
3318                "RFF" if current.is_some() => {
3319                    if seg
3320                        .elements
3321                        .first()
3322                        .and_then(|e: &Vec<String>| e.first())
3323                        .is_some_and(|v: &String| v == "MG")
3324                    {
3325                        if let Some(ref mut entry) = current {
3326                            if entry.mg_id.is_none() {
3327                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3328                            }
3329                        }
3330                    }
3331                }
3332                _ => {}
3333            }
3334        }
3335        if let Some(entry) = current {
3336            entries.push(entry);
3337        }
3338        let ids: Vec<&str> = entries
3339            .iter()
3340            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3341            .collect();
3342        for i in 0..ids.len() {
3343            for j in (i + 1)..ids.len() {
3344                if ids[i] == ids[j] {
3345                    return ConditionResult::True;
3346                }
3347            }
3348        }
3349        ConditionResult::False
3350    }
3351
3352    /// [124] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit derselben Zeitraum-ID aus dem DE1050 dieser SG8 mit dem SG8 RFF+MG (Gerätenummer des Zählers...
3353    // REVIEW: Extends condition 123 by also requiring that the matching SG8 groups share the same Zeitraum-ID (from SEQ.elements[1][0] = DE1050 / C286.d1050). Both the Zeitraum-ID and the RFF+MG device number must be identical across at least two different SG8 instances with SEQ+Z20 for the condition to be True. (medium confidence)
3354    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3355        // HAND-EDITED: "noch mindestens eine weitere SG8 SEQ+Z20 mit derselben Zeitraum-ID … RFF+MG" — compared within the enclosing SG4 when the scope
3356        // reaches an SG8; the message as before otherwise.
3357        if let Some(scope) = ctx.enclosing_scope("SG8") {
3358            let Some((_, zeitraum)) = sg4_instances::seq_at(ctx, scope)
3359                .filter(|(code, zeitraum)| code == "Z20" && !zeitraum.is_empty())
3360            else {
3361                return ConditionResult::False;
3362            };
3363            let meters = sg4_instances::rff_values_at(ctx, scope, "MG");
3364            return ConditionResult::from(sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
3365                sg4_instances::seq_at(ctx, p).is_some_and(|(c, z)| c == "Z20" && z == zeitraum)
3366                    && sg4_instances::rff_values_at(ctx, p, "MG")
3367                        .iter()
3368                        .any(|m| meters.contains(m))
3369            }));
3370        }
3371        struct Sg8Z20Entry {
3372            zeitraum_id: String,
3373            mg_id: Option<String>,
3374        }
3375        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3376        let mut current: Option<Sg8Z20Entry> = None;
3377        for seg in ctx.segments {
3378            match seg.id.as_str() {
3379                "SEQ" => {
3380                    if let Some(entry) = current.take() {
3381                        entries.push(entry);
3382                    }
3383                    if seg
3384                        .elements
3385                        .first()
3386                        .and_then(|e: &Vec<String>| e.first())
3387                        .is_some_and(|v: &String| v == "Z20")
3388                    {
3389                        let zeitraum_id = seg
3390                            .elements
3391                            .get(1)
3392                            .and_then(|e: &Vec<String>| e.first())
3393                            .cloned()
3394                            .unwrap_or_default();
3395                        current = Some(Sg8Z20Entry {
3396                            zeitraum_id,
3397                            mg_id: None,
3398                        });
3399                    }
3400                }
3401                "RFF" if current.is_some() => {
3402                    if seg
3403                        .elements
3404                        .first()
3405                        .and_then(|e: &Vec<String>| e.first())
3406                        .is_some_and(|v: &String| v == "MG")
3407                    {
3408                        if let Some(ref mut entry) = current {
3409                            if entry.mg_id.is_none() {
3410                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3411                            }
3412                        }
3413                    }
3414                }
3415                _ => {}
3416            }
3417        }
3418        if let Some(entry) = current {
3419            entries.push(entry);
3420        }
3421        for i in 0..entries.len() {
3422            for j in (i + 1)..entries.len() {
3423                let ei = &entries[i];
3424                let ej = &entries[j];
3425                if !ei.zeitraum_id.is_empty()
3426                    && ei.zeitraum_id == ej.zeitraum_id
3427                    && ei.mg_id.is_some()
3428                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3429                    && ei.mg_id == ej.mg_id
3430                {
3431                    return ConditionResult::True;
3432                }
3433            }
3434        }
3435        ConditionResult::False
3436    }
3437
3438    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3439    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3440        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3441        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3442        let found = seq_segs.iter().any(|s| {
3443            s.elements
3444                .first()
3445                .and_then(|e| e.first())
3446                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3447        });
3448        ConditionResult::from(found)
3449    }
3450
3451    /// [126] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) ergibt. Bedeutet: Das erste SG6 RFF+Z49/ Z53...
3452    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z49/Z53
3453    // of its SG4, counting from 1. The generated code only checked that one
3454    // existed, so any value passed.
3455    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3456        repetition_ordinal(ctx, &["Z49", "Z53"])
3457    }
3458
3459    /// [127] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) ergibt. Bedeutet: Das erste SG6  R...
3460    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z48/Z55
3461    // of its SG4, counting from 1. The generated code only checked that one
3462    // existed, so any value passed.
3463    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3464        repetition_ordinal(ctx, &["Z48", "Z55"])
3465    }
3466
3467    /// [128] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) ergibt. Bede...
3468    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z47/Z54
3469    // of its SG4, counting from 1. The generated code only checked that one
3470    // existed, so any value passed.
3471    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3472        repetition_ordinal(ctx, &["Z47", "Z54"])
3473    }
3474
3475    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3476    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3477        // Check: all LOC+Z16 DE3225 values are identical
3478        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3479        if loc_segs.len() <= 1 {
3480            return ConditionResult::True;
3481        }
3482        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3483        let all_same = loc_segs
3484            .iter()
3485            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3486        ConditionResult::from(all_same)
3487    }
3488
3489    /// [130] Wenn an Messlokation vorhanden
3490    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3491        // TODO: implement
3492        ConditionResult::Unknown
3493    }
3494
3495    /// [131] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID \"1\" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+1...
3496    // HAND-EDITED: compares the dates. The generated code only checked that
3497    // some RFF carries Zeitraum-ID 1 (mako-twin, 55643).
3498    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3499        first_period_start_vs_next_day(ctx, |start, next_day| start <= next_day)
3500    }
3501
3502    /// [132] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID "1" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+137...
3503    // HAND-EDITED: judges the dates in German local time, in this SG6. The
3504    // generated code compared raw digits with `…0000`, which a correct UTC
3505    // value (`202610222200+00`) never has.
3506    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3507        first_period_start_vs_next_day(ctx, |start, next_day| start == next_day)
3508    }
3509
3510    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3511    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3512        // TODO: implement
3513        ConditionResult::Unknown
3514    }
3515
3516    /// [134] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) mit der Zeitraum ID "1" im DE1156 ist, muss das Datum ...
3517    // HAND-EDITED: judges this SG6 against the Z48/Z55 SG6 with the next lower
3518    // Zeitraum-ID. The generated code checked every slice message-wide and was
3519    // true on a Z48 SG6 with Zeitraum-ID 1 (mako-twin, 55658).
3520    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3521        continues_previous_period(ctx, &["Z48", "Z55"])
3522    }
3523
3524    /// [135] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) mit der Zeitraum ID "1" im DE115...
3525    // HAND-EDITED: as [134], for Z47/Z54.
3526    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3527        continues_previous_period(ctx, &["Z47", "Z54"])
3528    }
3529
3530    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3531    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3532        // HAND-EDITED: the CCI and the CAV of one SG10.
3533        sg10_has(ctx, false, |segs| {
3534            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "EHZ")
3535        })
3536    }
3537
3538    /// [137] Nicht bei Neuanlage
3539    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3540        // Check: not a Neuanlage (new registration) process
3541        // STS+7 with transaktionsgrund containing Neuanlage indicators
3542        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3543        let is_neuanlage = sts_segs.iter().any(|s| {
3544            s.elements
3545                .get(2)
3546                .and_then(|e| e.first())
3547                .is_some_and(|v| v == "E01" || v == "E03")
3548        });
3549        ConditionResult::from(!is_neuanlage)
3550    }
3551
3552    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3553    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3554        let qty_segs = ctx.find_segments("QTY");
3555        let found = qty_segs.iter().any(|s| {
3556            s.elements
3557                .first()
3558                .and_then(|e| e.first())
3559                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3560        });
3561        ConditionResult::from(found)
3562    }
3563
3564    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3565    // REVIEW: Condition is True when SG10 CAV+IVA (Individuelle Abstimmung) is NOT present anywhere. Uses parent-child navigation to iterate all SG8 instances and their SG10 child groups, checking CAV elements[0][0] for 'IVA'. Falls back to message-wide lacks_qualifier when no navigator is available. Returns False as soon as any CAV+IVA is found, True if none found. (medium confidence)
3566    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3567        let nav = match ctx.navigator {
3568            Some(n) => n,
3569            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3570        };
3571        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3572        for i in 0..sg8_count {
3573            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3574            for j in 0..sg10_count {
3575                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3576                if cavs.iter().any(|s| {
3577                    s.elements
3578                        .first()
3579                        .and_then(|e: &Vec<String>| e.first())
3580                        .is_some_and(|v: &String| v == "IVA")
3581                }) {
3582                    return ConditionResult::False;
3583                }
3584            }
3585        }
3586        ConditionResult::True
3587    }
3588
3589    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3590    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3591        let qty_segs = ctx.find_segments("QTY");
3592        let found = qty_segs.iter().any(|s| {
3593            s.elements
3594                .first()
3595                .and_then(|e| e.first())
3596                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3597        });
3598        ConditionResult::from(found)
3599    }
3600
3601    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3602    // HAND-EDITED (#392, reported by mako-twin): the CAVs of this SG8 (the
3603    // Wandler whose Faktor is asked for), not of the message: an MUW beside
3604    // an MIW in another SG8 was asked for a Faktor.
3605    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3606        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
3607        let found = cav_segs.iter().any(|s| {
3608            s.elements
3609                .first()
3610                .and_then(|e| e.first())
3611                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3612        });
3613        ConditionResult::from(found)
3614    }
3615
3616    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3617    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3618        let pia_segs = ctx.find_segments("PIA");
3619        let found = pia_segs.iter().any(|s| {
3620            s.elements
3621                .first()
3622                .and_then(|e| e.first())
3623                .is_some_and(|v| v == "Z01")
3624                && s.elements
3625                    .get(1)
3626                    .and_then(|e| e.get(1))
3627                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3628        });
3629        ConditionResult::from(found)
3630    }
3631
3632    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3633    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3634        let sts_segs = ctx.find_segments("STS");
3635        let found = sts_segs.iter().any(|s| {
3636            s.elements
3637                .first()
3638                .and_then(|e| e.first())
3639                .is_some_and(|v| v == "7")
3640                && s.elements
3641                    .get(2)
3642                    .and_then(|e| e.first())
3643                    .is_some_and(|v| v == "ZX3")
3644        });
3645        ConditionResult::from(found)
3646    }
3647
3648    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3649    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3650        let sts_segs = ctx.find_segments("STS");
3651        let found = sts_segs.iter().any(|s| {
3652            s.elements
3653                .first()
3654                .and_then(|e| e.first())
3655                .is_some_and(|v| v == "7")
3656                && s.elements
3657                    .get(2)
3658                    .and_then(|e| e.first())
3659                    .is_some_and(|v| v == "ZAN")
3660        });
3661        ConditionResult::from(found)
3662    }
3663
3664    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3665    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3666        let sts_segs = ctx.find_segments("STS");
3667        let found = sts_segs.iter().any(|s| {
3668            s.elements
3669                .first()
3670                .and_then(|e| e.first())
3671                .is_some_and(|v| v == "7")
3672                && s.elements
3673                    .get(2)
3674                    .and_then(|e| e.first())
3675                    .is_some_and(|v| v == "ZX2")
3676        });
3677        ConditionResult::from(found)
3678    }
3679
3680    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3681    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3682        let sts_segs = ctx.find_segments("STS");
3683        let found = sts_segs.iter().any(|s| {
3684            s.elements
3685                .first()
3686                .and_then(|e| e.first())
3687                .is_some_and(|v| v == "7")
3688                && s.elements
3689                    .get(2)
3690                    .and_then(|e| e.first())
3691                    .is_some_and(|v| v == "ZA0")
3692        });
3693        ConditionResult::from(found)
3694    }
3695
3696    /// [147] Wenn in Anfrage vorhanden
3697    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3698        // TODO: implement
3699        ConditionResult::Unknown
3700    }
3701
3702    /// [148] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-017/ 1-02-0-018 vorhanden
3703    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3704        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3705        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3706        let found = pia_segs.iter().any(|s| {
3707            s.elements
3708                .first()
3709                .and_then(|e| e.first())
3710                .is_some_and(|v| v == "Z02")
3711                && s.elements
3712                    .get(1)
3713                    .and_then(|e| e.first())
3714                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3715        });
3716        ConditionResult::from(found)
3717    }
3718
3719    /// [149] Wenn zwei SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit  derselben Zeitraum-ID im DE1050, mit Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), eine SG8 mit 1-02-0-017...
3720    // REVIEW: Condition checks whether two SG8 SEQ+Z45 groups with the same Zeitraum-ID (elements[1][0]) each contain PIA+Z02 (DE4347=Z02) with product codes '1-02-0-017' and '1-02-0-018'. Uses collect_group_values to gather SEQ qualifiers and Zeitraum-IDs per group instance, then PIA qualifiers and product codes (elements[1][0]=DE7140) per group instance. Positional alignment of pia_quals and pia_prods (both from same PIA segments in same order) enables reliable qualifier-to-product matching. Groups product codes by Zeitraum-ID to check co-occurrence. (medium confidence)
3721    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3722        use std::collections::{HashMap, HashSet};
3723        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3724        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3725        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3726        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3727        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3728        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3729        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3730            if q == "Z45" && !zid.is_empty() {
3731                idx_to_zeitraum.insert(*gi, zid.clone());
3732            }
3733        }
3734        if idx_to_zeitraum.is_empty() {
3735            return ConditionResult::False;
3736        }
3737        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3738        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3739        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3740            if qual == "Z02" {
3741                if let Some(zid) = idx_to_zeitraum.get(gi) {
3742                    if let Some((_, prod)) = pia_prods.get(i) {
3743                        if !prod.is_empty() {
3744                            zeitraum_products
3745                                .entry(zid.clone())
3746                                .or_default()
3747                                .insert(prod.clone());
3748                        }
3749                    }
3750                }
3751            }
3752        }
3753        ConditionResult::from(
3754            zeitraum_products
3755                .values()
3756                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
3757        )
3758    }
3759
3760    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
3761    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
3762        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3763        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3764        let found = pia_segs.iter().any(|s| {
3765            s.elements
3766                .first()
3767                .and_then(|e| e.first())
3768                .is_some_and(|v| v == "Z02")
3769                && s.elements
3770                    .get(1)
3771                    .and_then(|e| e.first())
3772                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
3773        });
3774        ConditionResult::from(found)
3775    }
3776
3777    /// [151] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Ü̈bertragungsweg \"aus dem SMGW\" bestellt wurde. Der Empfänger der Werte kann dadurch seine...
3778    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
3779        ctx.external.evaluate("code_list_membership_check")
3780    }
3781
3782    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
3783    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
3784        let ftx_segs = ctx.find_segments("FTX");
3785        let found = ftx_segs.iter().any(|s| {
3786            s.elements
3787                .first()
3788                .and_then(|e| e.first())
3789                .is_some_and(|v| v == "Z28")
3790        });
3791        ConditionResult::from(!found)
3792    }
3793
3794    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
3795    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
3796        let ftx_segs = ctx.find_segments("FTX");
3797        let found = ftx_segs.iter().any(|s| {
3798            s.elements
3799                .first()
3800                .and_then(|e| e.first())
3801                .is_some_and(|v| v == "Z27")
3802        });
3803        ConditionResult::from(!found)
3804    }
3805
3806    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
3807    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
3808        let ftx_segs = ctx.find_segments("FTX");
3809        let found = ftx_segs.iter().any(|s| {
3810            s.elements
3811                .first()
3812                .and_then(|e| e.first())
3813                .is_some_and(|v| v == "Z17")
3814        });
3815        ConditionResult::from(found)
3816    }
3817
3818    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
3819    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
3820        let sts_segs = ctx.find_segments("STS");
3821        let found = sts_segs.iter().any(|s| {
3822            s.elements
3823                .first()
3824                .and_then(|e| e.first())
3825                .is_some_and(|v| v == "7")
3826                && s.elements
3827                    .get(2)
3828                    .and_then(|e| e.first())
3829                    .is_some_and(|v| v == "E03")
3830                && s.elements
3831                    .get(3)
3832                    .and_then(|e| e.first())
3833                    .is_some_and(|v| v == "ZW8")
3834        });
3835        ConditionResult::from(found)
3836    }
3837
3838    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
3839    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
3840        let sts_segs = ctx.find_segments("STS");
3841        let found = sts_segs.iter().any(|s| {
3842            s.elements
3843                .first()
3844                .and_then(|e| e.first())
3845                .is_some_and(|v| v == "7")
3846                && s.elements
3847                    .get(2)
3848                    .and_then(|e| e.first())
3849                    .is_some_and(|v| v == "E03")
3850                && s.elements
3851                    .get(3)
3852                    .and_then(|e| e.first())
3853                    .is_some_and(|v| v == "ZW9")
3854        });
3855        ConditionResult::from(found)
3856    }
3857
3858    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
3859    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
3860        let sts_segs = ctx.find_segments("STS");
3861        let found = sts_segs.iter().any(|s| {
3862            s.elements
3863                .first()
3864                .and_then(|e| e.first())
3865                .is_some_and(|v| v == "7")
3866                && s.elements
3867                    .get(2)
3868                    .and_then(|e| e.first())
3869                    .is_some_and(|v| v == "E03")
3870                && s.elements
3871                    .get(3)
3872                    .and_then(|e| e.first())
3873                    .is_some_and(|v| v == "ZX0")
3874        });
3875        ConditionResult::from(found)
3876    }
3877
3878    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
3879    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
3880        let sts_segs = ctx.find_segments("STS");
3881        let found = sts_segs.iter().any(|s| {
3882            s.elements
3883                .first()
3884                .and_then(|e| e.first())
3885                .is_some_and(|v| v == "7")
3886                && s.elements
3887                    .get(2)
3888                    .and_then(|e| e.first())
3889                    .is_some_and(|v| v == "E03")
3890                && s.elements
3891                    .get(3)
3892                    .and_then(|e| e.first())
3893                    .is_some_and(|v| v == "ZX1")
3894        });
3895        ConditionResult::from(found)
3896    }
3897
3898    /// [165] Wenn bekannt
3899    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
3900        // TODO: implement
3901        ConditionResult::Unknown
3902    }
3903
3904    /// [166] Wenn vorhanden
3905    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
3906        // "If present" — always true when the field is being validated
3907        ConditionResult::True
3908    }
3909
3910    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
3911    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
3912        let loc_segs = ctx.find_segments("LOC");
3913        let found = loc_segs.iter().any(|s| {
3914            s.elements
3915                .first()
3916                .and_then(|e| e.first())
3917                .is_some_and(|v| v == "Z21")
3918        });
3919        ConditionResult::from(found)
3920    }
3921
3922    /// [170] Wenn Anschrift der Marktlokation vorhanden
3923    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
3924        // TODO: implement
3925        ConditionResult::Unknown
3926    }
3927
3928    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
3929    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
3930        let qty_segs = ctx.find_segments("QTY");
3931        let found = qty_segs.iter().any(|s| {
3932            s.elements
3933                .first()
3934                .and_then(|e| e.first())
3935                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
3936        });
3937        ConditionResult::from(found)
3938    }
3939
3940    /// [173] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 (Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (...
3941    // REVIEW: Condition is True when, in an SG8 with SEQ+Z58/ZC9/ZD0/ZD6 (Zuordnung Lokation zum Objektcode), RFF+Z33 exists but its DE1156 (elements[0][2] = Fortlaufende Nummer) is absent or empty. Uses collect_group_values to find target SG8 group indices by SEQ qualifier, then positionally aligns rff_quals[i] with rff_de1156[i] (same RFF segment) to check DE1156 emptiness scoped to those groups. Returns Unknown if no RFF+Z33 found in target groups (condition not applicable), False if all RFF+Z33 have DE1156 present. (medium confidence)
3942    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
3943        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
3944        // RFF+Z33 without DE1156.
3945        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
3946        if !kinds
3947            .iter()
3948            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
3949        {
3950            return ConditionResult::Unknown;
3951        }
3952        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
3953        if z33.is_empty() {
3954            return ConditionResult::Unknown;
3955        }
3956        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
3957    }
3958
3959    /// [174] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Z...
3960    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
3961        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3962        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
3963        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
3964        let found = rff_z33.iter().any(|s| {
3965            s.elements
3966                .first()
3967                .and_then(|e| e.get(2))
3968                .is_some_and(|v| !v.is_empty())
3969        });
3970        ConditionResult::from(found)
3971    }
3972
3973    /// [175] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in einem weite...
3974    // REVIEW: Cross-SG8 check: find all SG8 instances with SEQ+Z58 that also have RFF+Z33. Check whether any Objektcode (DE1154) with the same fortlaufende Nummer (DE1156) appears in two distinct SG8 instances. Uses nav.find_segments_in_group to access SG8-level segments directly, following the existing navigator pattern. (medium confidence)
3975    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
3976        // HAND-EDITED: "in einem weiteren SG8 SEQ+Z58 mit derselben fortlaufenden Nummer" — compared within the enclosing SG4 when the scope
3977        // reaches an SG8; the message as before otherwise.
3978        if let Some(scope) = ctx.enclosing_scope("SG8") {
3979            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
3980                ctx,
3981                scope,
3982                &["Z58"],
3983                true,
3984                false,
3985            ));
3986        }
3987        let nav = match ctx.navigator {
3988            Some(n) => n,
3989            None => return ConditionResult::Unknown,
3990        };
3991        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3992        let mut entries: Vec<(usize, String, String)> = Vec::new();
3993        for i in 0..sg8_count {
3994            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
3995            if !seqs.iter().any(|s| {
3996                s.elements
3997                    .first()
3998                    .and_then(|e: &Vec<String>| e.first())
3999                    .is_some_and(|v: &String| v == "Z58")
4000            }) {
4001                continue;
4002            }
4003            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4004            for rff in &rffs {
4005                let elems = match rff.elements.first() {
4006                    Some(e) => e,
4007                    None => continue,
4008                };
4009                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4010                    continue;
4011                }
4012                let objektcode = elems.get(1).cloned().unwrap_or_default();
4013                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4014                if !objektcode.is_empty() {
4015                    entries.push((i, objektcode, fort_nr));
4016                }
4017            }
4018        }
4019        for a in 0..entries.len() {
4020            for b in (a + 1)..entries.len() {
4021                if entries[a].0 != entries[b].0
4022                    && entries[a].1 == entries[b].1
4023                    && entries[a].2 == entries[b].2
4024                {
4025                    return ConditionResult::True;
4026                }
4027            }
4028        }
4029        ConditionResult::False
4030    }
4031
4032    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4033    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4034        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4035    }
4036
4037    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4038    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4039        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4040    }
4041
4042    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4043    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4044        let loc_segs = ctx.find_segments("LOC");
4045        let found = loc_segs.iter().any(|s| {
4046            s.elements
4047                .first()
4048                .and_then(|e| e.first())
4049                .is_some_and(|v| v == "Z18")
4050        });
4051        ConditionResult::from(found)
4052    }
4053
4054    /// [179] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9 / ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels...
4055    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4056        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4057        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4058        // a group scope: "derselben SG8" cannot be told apart then.
4059        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4060        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4061            return ConditionResult::Unknown;
4062        };
4063        let seq_of = |segs: &[OwnedSegment]| {
4064            segs.iter()
4065                .find(|s| s.id == "SEQ")
4066                .map(|s| s.get_element(0).to_string())
4067        };
4068        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4069            segs.iter()
4070                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4071                .map(|s| s.get_component(0, 1).to_string())
4072                .filter(|v| !v.is_empty())
4073                .collect()
4074        };
4075        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4076        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4077            return ConditionResult::False;
4078        };
4079        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4080        if own.is_empty() {
4081            return ConditionResult::False;
4082        }
4083        let found = siblings.iter().any(|path| {
4084            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4085                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4086                    .iter()
4087                    .any(|code| own.contains(code))
4088        });
4089        ConditionResult::from(found)
4090    }
4091
4092    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4093    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4094        let qty_segs = ctx.find_segments("QTY");
4095        let found = qty_segs.iter().any(|s| {
4096            s.elements
4097                .first()
4098                .and_then(|e| e.first())
4099                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4100        });
4101        ConditionResult::from(found)
4102    }
4103
4104    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4105    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4106        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
4107        // and the CCI+Z01++Z82 the condition names is part of it: a second
4108        // Messlokation (CCI+Z01++Z81) needs no Messstellenbetreiber Alt.
4109        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4110        let seq_z18 = seq_segs.iter().any(|s| {
4111            s.elements
4112                .first()
4113                .and_then(|e| e.first())
4114                .is_some_and(|v| v == "Z18")
4115        });
4116        let cci_z82 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4117            s.elements
4118                .first()
4119                .and_then(|e| e.first())
4120                .is_some_and(|v| v == "Z01")
4121                && s.elements
4122                    .get(2)
4123                    .and_then(|e| e.first())
4124                    .is_some_and(|v| v == "Z82")
4125        });
4126        ConditionResult::from(seq_z18 && cci_z82)
4127    }
4128
4129    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4130    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4131        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4132        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4133        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4134        let found = pia_segs.iter().any(|s| {
4135            s.elements
4136                .first()
4137                .and_then(|e| e.first())
4138                .is_some_and(|v| v == "5")
4139                && s.elements
4140                    .get(1)
4141                    .and_then(|e| e.first())
4142                    .is_some_and(|v| v == "9991000000721")
4143        });
4144        ConditionResult::from(found)
4145    }
4146
4147    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4148    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4149        // HAND-EDITED (#343): the list's own STS+E01, in the SG4 opened by IDE+Z01; a
4150        // Vorgang's STS+E01 (IDE+24) answers the Vorgang, not the list.
4151        let mut in_list = false;
4152        let mut found = false;
4153        for s in ctx.segments.iter() {
4154            match s.id.as_str() {
4155                "IDE" => in_list = s.get_element(0) == "Z01",
4156                "STS" if in_list && s.get_element(0) == "E01" => found = true,
4157                _ => {}
4158            }
4159        }
4160        ConditionResult::from(!found)
4161    }
4162
4163    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4164    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4165        let sts_segs = ctx.find_segments("STS");
4166        let found = sts_segs.iter().any(|s| {
4167            s.elements
4168                .first()
4169                .and_then(|e| e.first())
4170                .is_some_and(|v| v == "7")
4171                && s.elements
4172                    .get(3)
4173                    .and_then(|e| e.first())
4174                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4175        });
4176        ConditionResult::from(found)
4177    }
4178
4179    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4180    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4181        let nav = match ctx.navigator {
4182            Some(n) => n,
4183            None => {
4184                let cavs = ctx.find_segments("CAV");
4185                return ConditionResult::from(cavs.iter().any(|s| {
4186                    s.elements
4187                        .first()
4188                        .and_then(|e: &Vec<String>| e.first())
4189                        .is_some_and(|v: &String| v == "E06")
4190                }));
4191            }
4192        };
4193        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4194        for i in 0..sg8_count {
4195            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4196            for j in 0..sg10_count {
4197                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4198                let has_cci_zb5 = ccis.iter().any(|s| {
4199                    s.elements
4200                        .get(2)
4201                        .and_then(|e: &Vec<String>| e.first())
4202                        .is_some_and(|v: &String| v == "ZB5")
4203                });
4204                if !has_cci_zb5 {
4205                    continue;
4206                }
4207                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4208                if cavs.iter().any(|s| {
4209                    s.elements
4210                        .first()
4211                        .and_then(|e: &Vec<String>| e.first())
4212                        .is_some_and(|v: &String| v == "E06")
4213                }) {
4214                    return ConditionResult::True;
4215                }
4216            }
4217        }
4218        ConditionResult::False
4219    }
4220
4221    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4222    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4223        let cci_segs = ctx.find_segments("CCI");
4224        let has_cci = cci_segs.iter().any(|s| {
4225            s.elements
4226                .iter()
4227                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4228        });
4229        let cav_segs = ctx.find_segments("CAV");
4230        let has_cav = cav_segs.iter().any(|s| {
4231            s.elements
4232                .first()
4233                .and_then(|e| e.first())
4234                .is_some_and(|v| v == "E05")
4235        });
4236        ConditionResult::from(has_cci && has_cav)
4237    }
4238
4239    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4240    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4241        let cci_segs = ctx.find_segments("CCI");
4242        let has_cci = cci_segs.iter().any(|s| {
4243            s.elements
4244                .iter()
4245                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4246        });
4247        let cav_segs = ctx.find_segments("CAV");
4248        let has_cav = cav_segs.iter().any(|s| {
4249            s.elements
4250                .first()
4251                .and_then(|e| e.first())
4252                .is_some_and(|v| v == "E04")
4253        });
4254        ConditionResult::from(has_cci && has_cav)
4255    }
4256
4257    /// [196] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ZC9/ZD0 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in ein...
4258    // REVIEW: Extended cross-SG8 check vs 175: now requires matching SEQ qualifier code (DE1229), Zeitraum-ID from SEQ DE1050 (elements[1][0]), Objektcode (RFF+Z33 DE1154), AND fortlaufende Nummer (RFF+Z33 DE1156) to all be identical between two distinct SG8 instances. SEQ qualifiers are Z58/ZC9/ZD0 per schema. (medium confidence)
4259    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4260        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4261        // reaches an SG8; the message as before otherwise.
4262        if let Some(scope) = ctx.enclosing_scope("SG8") {
4263            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4264                ctx,
4265                scope,
4266                &["Z58", "ZC9", "ZD0"],
4267                true,
4268                true,
4269            ));
4270        }
4271        let nav = match ctx.navigator {
4272            Some(n) => n,
4273            None => return ConditionResult::Unknown,
4274        };
4275        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4276        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4277        for i in 0..sg8_count {
4278            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4279            let seq_opt = seqs.iter().find(|s| {
4280                s.elements
4281                    .first()
4282                    .and_then(|e: &Vec<String>| e.first())
4283                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4284            });
4285            let seq = match seq_opt {
4286                Some(s) => s,
4287                None => continue,
4288            };
4289            let seq_code = seq
4290                .elements
4291                .first()
4292                .and_then(|e: &Vec<String>| e.first())
4293                .cloned()
4294                .unwrap_or_default();
4295            let zeitraum_id = seq
4296                .elements
4297                .get(1)
4298                .and_then(|e: &Vec<String>| e.first())
4299                .cloned()
4300                .unwrap_or_default();
4301            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4302            for rff in &rffs {
4303                let elems = match rff.elements.first() {
4304                    Some(e) => e,
4305                    None => continue,
4306                };
4307                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4308                    continue;
4309                }
4310                let objektcode = elems.get(1).cloned().unwrap_or_default();
4311                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4312                if !objektcode.is_empty() {
4313                    entries.push((
4314                        i,
4315                        seq_code.clone(),
4316                        zeitraum_id.clone(),
4317                        objektcode,
4318                        fort_nr,
4319                    ));
4320                }
4321            }
4322        }
4323        for a in 0..entries.len() {
4324            for b in (a + 1)..entries.len() {
4325                if entries[a].0 != entries[b].0
4326                    && entries[a].1 == entries[b].1
4327                    && entries[a].2 == entries[b].2
4328                    && entries[a].3 == entries[b].3
4329                    && entries[a].4 == entries[b].4
4330                {
4331                    return ConditionResult::True;
4332                }
4333            }
4334        }
4335        ConditionResult::False
4336    }
4337
4338    /// [197] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
4339    // REVIEW: Two-phase cross-SG8 check. Phase 1: collect Marktlokation IDs from SG8 instances that (a) have SEQ+Z01 and (b) have an SG10 child with CCI elements[2][0]=ZA6 and CAV elements[0][0]=E02. Phase 2: check whether any SG8's RFF+Z18 references one of those qualifying Marktlokation IDs, establishing the cross-reference described in the condition. (medium confidence)
4340    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4341        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4342        // reaches an SG8; the message as before otherwise.
4343        if let Some(scope) = ctx.enclosing_scope("SG8") {
4344            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4345            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4346                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4347            }));
4348        }
4349        let nav = match ctx.navigator {
4350            Some(n) => n,
4351            None => return ConditionResult::Unknown,
4352        };
4353        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4354        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4355        for i in 0..sg8_count {
4356            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4357            if !seqs.iter().any(|s| {
4358                s.elements
4359                    .first()
4360                    .and_then(|e: &Vec<String>| e.first())
4361                    .is_some_and(|v: &String| v == "Z01")
4362            }) {
4363                continue;
4364            }
4365            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4366            let has_prog = (0..sg10_count).any(|j| {
4367                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4368                let has_za6 = ccis.iter().any(|s| {
4369                    s.elements
4370                        .get(2)
4371                        .and_then(|e: &Vec<String>| e.first())
4372                        .is_some_and(|v: &String| v == "ZA6")
4373                });
4374                if !has_za6 {
4375                    return false;
4376                }
4377                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4378                cavs.iter().any(|s| {
4379                    s.elements
4380                        .first()
4381                        .and_then(|e: &Vec<String>| e.first())
4382                        .is_some_and(|v: &String| v == "E02")
4383                })
4384            });
4385            if !has_prog {
4386                continue;
4387            }
4388            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4389            for rff in &rffs {
4390                let elems = match rff.elements.first() {
4391                    Some(e) => e,
4392                    None => continue,
4393                };
4394                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4395                    continue;
4396                }
4397                if let Some(malo_id) = elems.get(1) {
4398                    if !malo_id.is_empty() {
4399                        qualifying_malo_ids.push(malo_id.clone());
4400                    }
4401                }
4402            }
4403        }
4404        if qualifying_malo_ids.is_empty() {
4405            return ConditionResult::False;
4406        }
4407        for i in 0..sg8_count {
4408            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4409            for rff in &rffs {
4410                let elems = match rff.elements.first() {
4411                    Some(e) => e,
4412                    None => continue,
4413                };
4414                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4415                    continue;
4416                }
4417                if let Some(malo_id) = elems.get(1) {
4418                    if qualifying_malo_ids.contains(malo_id) {
4419                        return ConditionResult::True;
4420                    }
4421                }
4422            }
4423        }
4424        ConditionResult::False
4425    }
4426
4427    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4428    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4429        let sts_segs = ctx.find_segments("STS");
4430        let found = sts_segs.iter().any(|s| {
4431            s.elements
4432                .first()
4433                .and_then(|e| e.first())
4434                .is_some_and(|v| v == "7")
4435                && s.elements
4436                    .get(2)
4437                    .and_then(|e| e.first())
4438                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4439        });
4440        ConditionResult::from(found)
4441    }
4442
4443    /// [199] Wenn in der SG8 SEQ+Z01/ SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) mit identischer Zeitraum-ID im DE1050 CAV+E02 (SLP/SEP) vorhanden
4444    // REVIEW: Checks whether any SG8 with SEQ+Z01 (Daten der Marktlokation) contains an SG10 child group where CCI elements[2][0]=ZA6 (Prognosegrundlage: Prognose auf Basis von Profilen) and CAV elements[0][0]=E02 (SLP/SEP) appear in the same SG10 instance. The 'identischer Zeitraum-ID' constraint from SEQ DE1050 is structurally satisfied by operating within the same SG8 instance; deeper Zeitraum-ID cross-referencing with other groups is omitted as it would require knowing the external referencing context. (medium confidence)
4445    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4446        let nav = match ctx.navigator {
4447            Some(n) => n,
4448            None => {
4449                return ctx.filtered_parent_child_has_qualifier(
4450                    &["SG4", "SG8"],
4451                    "SEQ",
4452                    0,
4453                    "Z01",
4454                    "SG10",
4455                    "CCI",
4456                    2,
4457                    "ZA6",
4458                )
4459            }
4460        };
4461        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4462        for i in 0..sg8_count {
4463            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4464            if !seqs.iter().any(|s| {
4465                s.elements
4466                    .first()
4467                    .and_then(|e: &Vec<String>| e.first())
4468                    .is_some_and(|v: &String| v == "Z01")
4469            }) {
4470                continue;
4471            }
4472            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4473            for j in 0..sg10_count {
4474                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4475                if !ccis.iter().any(|s| {
4476                    s.elements
4477                        .get(2)
4478                        .and_then(|e: &Vec<String>| e.first())
4479                        .is_some_and(|v: &String| v == "ZA6")
4480                }) {
4481                    continue;
4482                }
4483                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4484                if cavs.iter().any(|s| {
4485                    s.elements
4486                        .first()
4487                        .and_then(|e: &Vec<String>| e.first())
4488                        .is_some_and(|v: &String| v == "E02")
4489                }) {
4490                    return ConditionResult::True;
4491                }
4492            }
4493        }
4494        ConditionResult::False
4495    }
4496
4497    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4498    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4499        // TODO: implement
4500        ConditionResult::Unknown
4501    }
4502
4503    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4504    // REVIEW: Collects Zeitraum-IDs from SG5 LOC.DE3224 (elements[1][3]), checks SEQ+Z78 DE1050 (elements[1][0]) for a match, then verifies RFF+Z39 exists. Co-occurrence of SEQ+Z78 and RFF+Z39 in the same SG8 instance is not verified without navigator group-instance access. (medium confidence)
4505    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4506        use std::collections::HashSet;
4507        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4508        let loc_zeitraum_ids: HashSet<String> = ctx
4509            .find_segments("LOC")
4510            .into_iter()
4511            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4512            .filter(|v| !v.is_empty())
4513            .collect();
4514        if loc_zeitraum_ids.is_empty() {
4515            return ConditionResult::False;
4516        }
4517        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4518        let matching_seq = ctx
4519            .find_segments_with_qualifier("SEQ", 0, "Z78")
4520            .into_iter()
4521            .any(|s| {
4522                s.elements
4523                    .get(1)
4524                    .and_then(|e: &Vec<String>| e.first())
4525                    .is_some_and(|v: &String| {
4526                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4527                    })
4528            });
4529        if !matching_seq {
4530            return ConditionResult::False;
4531        }
4532        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4533        ctx.has_qualifier("RFF", 0, "Z39")
4534    }
4535
4536    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4537    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4538        let sts_segs = ctx.find_segments("STS");
4539        let found = sts_segs.iter().any(|s| {
4540            s.elements
4541                .first()
4542                .and_then(|e| e.first())
4543                .is_some_and(|v| v == "7")
4544                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4545                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4546                })
4547        });
4548        ConditionResult::from(found)
4549    }
4550
4551    /// [204] Wenn im SG8+SEQ+Z03/ ZA3/ ZA4 (Zähleinrichtungsdaten) mit der selben Zeitraum-ID im SG8 SEQ DE1050, sowie identischer Qualität im DE1229 der SG8 SEQ für die in diesem RFF DE1154 genannte Gerätenummer eines Zählers das RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden ist
4552    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4553        // HAND-EDITED (#392): the Zähleinrichtungsdaten of the meter this RFF
4554        // names (its number in the SG10 CAV+Z30 DE7110), with this SG8's
4555        // Zeitraum-ID and quality — erwartet (ZA3) beside an erwartete SG8
4556        // (ZA5, ZB7, …), im System vorhanden (ZA4) beside a vorhandene one —
4557        // not any Zähleinrichtungsdaten of the message. No such SG8: no
4558        // RFF+Z14 for that meter either.
4559        let Some(number) = rff_reference(ctx) else {
4560            return ConditionResult::Unknown;
4561        };
4562        let Some(blocks) = sg8_blocks(ctx) else {
4563            return ConditionResult::Unknown;
4564        };
4565        let own = ctx
4566            .enclosing_scope("SG8")
4567            .and_then(|scope| ctx.find_segments_at(scope, "SEQ").into_iter().next());
4568        let own_seq = own.as_ref().map(|s| s.get_element(0).to_string());
4569        let own_zeitraum = own
4570            .as_ref()
4571            .and_then(|s| component(s, 1, 0).map(str::to_string));
4572        let quality = |seq: &str| match seq {
4573            "ZA3" | "ZA5" | "ZB7" | "ZB9" | "ZC1" | "ZC3" | "ZG6" | "ZH3" => Some(true),
4574            "ZA4" | "ZA6" | "ZB8" | "ZC0" | "ZC2" | "ZC4" | "ZG7" | "ZH4" => Some(false),
4575            _ => None,
4576        };
4577        let meter = blocks
4578            .iter()
4579            .filter(|b| matches!(b.seq.as_str(), "Z03" | "ZA3" | "ZA4"))
4580            .filter(
4581                |b| match (quality(&b.seq), own_seq.as_deref().and_then(quality)) {
4582                    (Some(theirs), Some(ours)) => theirs == ours,
4583                    _ => true,
4584                },
4585            )
4586            .filter(|b| match own_zeitraum.as_deref() {
4587                Some(id) if !id.is_empty() && !b.zeitraum.is_empty() => b.zeitraum == id,
4588                _ => true,
4589            })
4590            .find(|b| {
4591                b.sg10.iter().any(|(_, cavs)| {
4592                    cavs.iter().any(|c| {
4593                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
4594                    })
4595                })
4596            });
4597        ConditionResult::from(meter.map_or(true, |b| {
4598            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
4599        }))
4600    }
4601
4602    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4603    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4604        let loc_segs = ctx.find_segments("LOC");
4605        let found = loc_segs.iter().any(|s| {
4606            s.elements
4607                .first()
4608                .and_then(|e| e.first())
4609                .is_some_and(|v| v == "Z19")
4610        });
4611        ConditionResult::from(found)
4612    }
4613
4614    /// [206] Wenn SG4 STS+7++ZG5 (Transaktionsgrund: Aufhebung der zukünftigen Zuordnung aufgrund §38 EEG 2014 bzw. §21b Abs. 1 Nr. 2 EEG 2017) nicht vorhanden
4615    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4616        let sts_segs = ctx.find_segments("STS");
4617        let found = sts_segs.iter().any(|s| {
4618            s.elements
4619                .first()
4620                .and_then(|e| e.first())
4621                .is_some_and(|v| v == "7")
4622                && s.elements
4623                    .get(2)
4624                    .and_then(|e| e.first())
4625                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4626        });
4627        ConditionResult::from(!found)
4628    }
4629
4630    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4631    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4632        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4633    }
4634
4635    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4636    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4637        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4638        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4639        let found = cci_segs.iter().any(|s| {
4640            s.elements
4641                .get(2)
4642                .and_then(|e| e.first())
4643                .is_some_and(|v| v == "ZA6")
4644        });
4645        ConditionResult::from(found)
4646    }
4647
4648    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4649    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4650        let nad_segs = ctx.find_segments("NAD");
4651        let found = nad_segs.iter().any(|s| {
4652            s.elements
4653                .get(3)
4654                .and_then(|e| e.first())
4655                .is_some_and(|v| !v.is_empty())
4656        });
4657        ConditionResult::from(!found)
4658    }
4659
4660    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4661    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4662        let nad_segs = ctx.find_segments("NAD");
4663        let found = nad_segs.iter().any(|s| {
4664            s.elements
4665                .first()
4666                .and_then(|e| e.first())
4667                .is_some_and(|v| v == "Z09")
4668        });
4669        ConditionResult::from(found)
4670    }
4671
4672    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4673    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4674        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4675        // SG10s included). Both segments are looked for anywhere in that SG8,
4676        // not within one SG10.
4677        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4678            s.elements
4679                .get(2)
4680                .and_then(|e| e.first())
4681                .is_some_and(|v| v == "E13")
4682        });
4683        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4684            s.elements
4685                .first()
4686                .and_then(|e| e.first())
4687                .is_some_and(|v| v == "MME")
4688        });
4689        ConditionResult::from(has_cci_e13 && has_cav_mme)
4690    }
4691
4692    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4693    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4694        // HAND-EDITED: the CCI and the CAV of one SG10; Z08 is DE7110 of the CAV.
4695        sg10_has(ctx, false, |segs| {
4696            sg4_instances::has_cci(segs, "", "Z88")
4697                && segs.iter().any(|s| {
4698                    s.id == "CAV" && s.get_element(0) == "Z74" && s.get_component(0, 3) == "Z08"
4699                })
4700        })
4701    }
4702
4703    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4704    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4705        let rff_segs = ctx.find_segments("RFF");
4706        let found = rff_segs.iter().any(|s| {
4707            s.elements
4708                .first()
4709                .and_then(|e| e.first())
4710                .is_some_and(|v| v == "Z49")
4711        });
4712        ConditionResult::from(found)
4713    }
4714
4715    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4716    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4717        let nad_segs = ctx.find_segments("NAD");
4718        let found = nad_segs.iter().any(|s| {
4719            s.elements
4720                .first()
4721                .and_then(|e| e.first())
4722                .is_some_and(|v| v == "Z65")
4723        });
4724        ConditionResult::from(found)
4725    }
4726
4727    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4728    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4729        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4730        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4731        let has_seq = seq_segs.iter().any(|s| {
4732            s.elements
4733                .first()
4734                .and_then(|e| e.first())
4735                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4736        });
4737        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4738        let has_cci = cci_segs.iter().any(|s| {
4739            s.elements
4740                .first()
4741                .and_then(|e| e.first())
4742                .is_some_and(|v| v == "Z30")
4743                && s.elements
4744                    .get(2)
4745                    .and_then(|e| e.first())
4746                    .is_some_and(|v| v == "Z06")
4747        });
4748        ConditionResult::from(has_seq && has_cci)
4749    }
4750
4751    /// [221] Wenn kein SG8 SEQ+Z15 (Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z01 (Daten der Marktlokation)
4752    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4753        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4754        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4755        if !has_seq_z15 {
4756            return ConditionResult::True;
4757        }
4758        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4759        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4760    }
4761
4762    /// [223] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation) verweist, und dort das S...
4763    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4764    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4765    // held for every message with a Marktlokation. The scope never narrows to
4766    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4767    // other than SEQ+Z01, as in the neighbouring conditions.
4768    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4769        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4770        // reaches an SG8; the message as before otherwise.
4771        if let Some(scope) = ctx.enclosing_scope("SG8") {
4772            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4773            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4774                sg4_instances::has_cci(s, "Z30", "Z06")
4775            }));
4776        }
4777        let Some(blocks) = sg8_blocks(ctx) else {
4778            return ConditionResult::Unknown;
4779        };
4780        let loc_ids: Vec<String> = ctx
4781            .find_segments_with_qualifier("LOC", 0, "Z16")
4782            .iter()
4783            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4784            .collect();
4785        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4786            b.malo_refs
4787                .iter()
4788                .filter(|id| loc_ids.contains(id))
4789                .cloned()
4790                .collect()
4791        };
4792        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
4793        let erzeugung: Vec<String> = blocks
4794            .iter()
4795            .filter(|b| b.seq == "Z01")
4796            .filter(|b| {
4797                b.sg10.iter().any(|(ccis, _)| {
4798                    ccis.iter().any(|c| {
4799                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
4800                    })
4801                })
4802            })
4803            .flat_map(|b| malo_refs(b))
4804            .collect();
4805        let referenced = blocks
4806            .iter()
4807            .filter(|b| b.seq != "Z01")
4808            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
4809        ConditionResult::from(referenced)
4810    }
4811
4812    /// [224] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Informative Daten der Marktlokation)
4813    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
4814        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
4815        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
4816        if !has_seq_ze7 {
4817            return ConditionResult::True;
4818        }
4819        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
4820    }
4821
4822    /// [227] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14 (TLP/TEP) vorhanden
4823    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
4824        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
4825        let r1 = ctx.filtered_parent_child_has_qualifier(
4826            &["SG4", "SG8"],
4827            "SEQ",
4828            0,
4829            "Z98",
4830            "SG10",
4831            "CAV",
4832            0,
4833            "E14",
4834        );
4835        if r1 == ConditionResult::True {
4836            return ConditionResult::True;
4837        }
4838        ctx.filtered_parent_child_has_qualifier(
4839            &["SG4", "SG8"],
4840            "SEQ",
4841            0,
4842            "Z98",
4843            "SG10",
4844            "CAV",
4845            0,
4846            "ETK",
4847        )
4848    }
4849
4850    /// [229] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels)...
4851    // REVIEW: Detects if the same (SEQ-qualifier, Zeitraum-ID, RFF+Z33-Objektcode) triple appears in two different SG8 instances — which is what the condition guards against. Uses collect_group_values to correlate SEQ and RFF data by SG8 instance index. Limitation: collect_group_values may only return the first RFF per SG8 instance, so Z33 may be missed if it is not the first RFF in the group. (medium confidence)
4852    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
4853        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4854        // reaches an SG8; the message as before otherwise.
4855        if let Some(scope) = ctx.enclosing_scope("SG8") {
4856            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4857                ctx,
4858                scope,
4859                &["Z58", "ZC9", "ZD0", "ZD6"],
4860                false,
4861                true,
4862            ));
4863        }
4864        use std::collections::HashSet;
4865        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
4866        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
4867        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4868        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
4869        // Collect per-SG8-instance: RFF qualifier and DE1154 value
4870        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
4871        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
4872        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
4873        let mut seen: HashSet<(String, String, String)> = HashSet::new();
4874        for (idx, seq_qual) in &seq_quals {
4875            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
4876                continue;
4877            }
4878            let zeitraum_id = seq_zeitraums
4879                .iter()
4880                .find(|(i, _)| i == idx)
4881                .map(|(_, v)| v.as_str())
4882                .unwrap_or("");
4883            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
4884            for (ridx, rff_qual) in rff_quals.iter() {
4885                if ridx != idx || rff_qual != "Z33" {
4886                    continue;
4887                }
4888                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
4889                    if obj_code.is_empty() {
4890                        continue;
4891                    }
4892                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
4893                    if seen.contains(&key) {
4894                        return ConditionResult::True;
4895                    }
4896                    seen.insert(key);
4897                }
4898            }
4899        }
4900        ConditionResult::False
4901    }
4902
4903    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
4904    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
4905        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
4906        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
4907        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
4908        sg10_has(ctx, true, |segs| {
4909            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z52")
4910        })
4911    }
4912
4913    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
4914    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
4915        ctx.external.evaluate("recipient_role_check")
4916    }
4917
4918    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
4919    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
4920        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
4921        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
4922        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
4923        sg10_has(ctx, true, |segs| {
4924            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z53")
4925        })
4926    }
4927
4928    /// [237] Wenn in derselben SG8 im DE7140 des PIA+Z02 (Gruppenartikel-ID / Artikel-ID) eine Gruppenartikel-ID / Artikel-ID mit 1-06 (entspricht Entgelte des Messstellenbetriebs bei kME) beginnt
4929    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
4930        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4931        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
4932        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4933        let found = pia_segs.iter().any(|s| {
4934            s.elements
4935                .first()
4936                .and_then(|e| e.first())
4937                .is_some_and(|v| v == "Z02")
4938                && s.elements
4939                    .get(1)
4940                    .and_then(|e| e.first())
4941                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
4942        });
4943        ConditionResult::from(found)
4944    }
4945
4946    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
4947    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
4948        let ide_segs = ctx.find_segments("IDE");
4949        let found = ide_segs.iter().any(|s| {
4950            s.elements
4951                .first()
4952                .and_then(|e| e.first())
4953                .is_some_and(|v| v == "24")
4954        });
4955        ConditionResult::from(!found)
4956    }
4957
4958    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
4959    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
4960        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
4961        // SG10, when the scope reaches one; of any one SG10 otherwise.
4962        this_sg10_has(ctx, |segs| {
4963            segs.iter()
4964                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
4965                && sg4_instances::has_cav(segs, "Z65")
4966        })
4967    }
4968
4969    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
4970    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
4971        let seq_segs = ctx.find_segments("SEQ");
4972        let found = seq_segs.iter().any(|s| {
4973            s.elements
4974                .first()
4975                .and_then(|e| e.first())
4976                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
4977        });
4978        ConditionResult::from(found)
4979    }
4980
4981    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
4982    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
4983        ctx.external.evaluate("recipient_role_check")
4984    }
4985
4986    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
4987    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
4988        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4989        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4990        let found = cci_segs.iter().any(|s| {
4991            s.elements
4992                .first()
4993                .and_then(|e| e.first())
4994                .is_some_and(|v| v == "6")
4995                && s.elements
4996                    .get(2)
4997                    .and_then(|e| e.first())
4998                    .is_some_and(|v| v == "ZA8")
4999        });
5000        ConditionResult::from(found)
5001    }
5002
5003    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5004    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5005        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5006        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5007        let found = cci_segs.iter().any(|s| {
5008            s.elements
5009                .first()
5010                .and_then(|e| e.first())
5011                .is_some_and(|v| v == "6")
5012                && s.elements
5013                    .get(2)
5014                    .and_then(|e| e.first())
5015                    .is_some_and(|v| v == "ZA9")
5016        });
5017        ConditionResult::from(found)
5018    }
5019
5020    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5021    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5022        // TODO: implement
5023        ConditionResult::Unknown
5024    }
5025
5026    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5027    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5028        // Check: all STS+E01 DE1131 values are identical
5029        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5030        if sts_segs.len() <= 1 {
5031            return ConditionResult::True;
5032        }
5033        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5034        let all_same = sts_segs
5035            .iter()
5036            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5037        ConditionResult::from(all_same)
5038    }
5039
5040    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5041    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5042        ctx.external.evaluate("sender_role_check")
5043    }
5044
5045    /// [252] Wenn DE0068 vorhanden
5046    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5047        let unb_segs = ctx.find_segments("UNB");
5048        let found = unb_segs.iter().any(|s| {
5049            s.elements
5050                .get(4)
5051                .and_then(|e| e.first())
5052                .is_some_and(|v| !v.is_empty())
5053        });
5054        ConditionResult::from(found)
5055    }
5056
5057    /// [254] Es ist der Code \"AUA\" einzutragen
5058    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5059        // Check: the code "AUA" is present
5060        ConditionResult::True // AUA is always the required code when this condition applies
5061    }
5062
5063    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5064    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5065        // TODO: implement
5066        ConditionResult::Unknown
5067    }
5068
5069    /// [256] Wenn in derselben SG8 SEQ (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e vorhanden
5070    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5071        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5072        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5073        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5074        let found = pia_segs.iter().any(|s| {
5075            s.elements
5076                .first()
5077                .and_then(|e| e.first())
5078                .is_some_and(|v| v == "5")
5079                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5080                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5081                })
5082        });
5083        ConditionResult::from(found)
5084    }
5085
5086    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5087    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5088        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5089        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5090        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5091        let found = pia_segs.iter().any(|s| {
5092            s.elements
5093                .first()
5094                .and_then(|e| e.first())
5095                .is_some_and(|v| v == "5")
5096                && s.elements
5097                    .get(1)
5098                    .and_then(|e| e.first())
5099                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5100        });
5101        ConditionResult::from(found)
5102    }
5103
5104    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5105    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5106        let pia_segs = ctx.find_segments("PIA");
5107        let found = pia_segs.iter().any(|s| {
5108            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5109                let prefixes = ["1-01", "1-03", "1-05"];
5110                prefixes.iter().any(|p| v.starts_with(p))
5111            })
5112        });
5113        ConditionResult::from(found)
5114    }
5115
5116    /// [261] Wenn in diesem PIA der Code im DE7140 mit 1-07-1/2, 1-10-1/2/4/5/6/7/8/9, 1-11-1 beginnt
5117    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5118        let pia_segs = ctx.find_segments("PIA");
5119        let found = pia_segs.iter().any(|s| {
5120            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5121                let prefixes = [
5122                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5123                ];
5124                prefixes.iter().any(|p| v.starts_with(p))
5125            })
5126        });
5127        ConditionResult::from(found)
5128    }
5129
5130    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5131    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5132        // TODO: implement
5133        ConditionResult::Unknown
5134    }
5135
5136    /// [265] Wenn im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP m. Referenzmessung) vorhanden
5137    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5138    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5139    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5140    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5141        let Some(blocks) = sg8_blocks(ctx) else {
5142            return ConditionResult::Unknown;
5143        };
5144        let found = blocks
5145            .iter()
5146            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5147            .any(|b| {
5148                b.sg10.iter().any(|(ccis, cavs)| {
5149                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5150                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5151                })
5152            });
5153        ConditionResult::from(found)
5154    }
5155
5156    /// [266] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im SG8 SEQ+Z01 DE1050 wie in dieser SG8 SEQ, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis vo...
5157    // REVIEW: Navigates all SG8 instances and their SG10 children, checking for CCI with elements[2][0]=ZA6 (Prognosegrundlage) co-occurring with CAV with elements[0][0]=Z36 (TEP m. Referenzmessung) in the same SG10 instance. The parent SG8 SEQ+Z01 filter and Zeitraum-ID cross-matching are not verified due to lack of direct SG8-level segment access; relies on CCI+ZA6/CAV+Z36 being specific enough to Marktlokation groups. (medium confidence)
5158    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5159        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5160        // reaches an SG8; the message as before otherwise.
5161        if let Some(scope) = ctx.enclosing_scope("SG8") {
5162            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5163            return ConditionResult::from(z01s.iter().any(|p| {
5164                sg4_instances::sg10_at(ctx, p, |s| {
5165                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5166                })
5167            }));
5168        }
5169        let nav = match ctx.navigator {
5170            Some(n) => n,
5171            None => {
5172                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5173                return ctx.filtered_parent_child_has_qualifier(
5174                    &["SG4", "SG8"],
5175                    "SEQ",
5176                    0,
5177                    "Z01",
5178                    "SG10",
5179                    "CAV",
5180                    0,
5181                    "Z36",
5182                );
5183            }
5184        };
5185        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5186        for i in 0..sg8_count {
5187            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5188            for j in 0..sg10_count {
5189                // CCI+++ZA6 means elements[2][0] == "ZA6"
5190                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5191                let has_cci_za6 = ccis.iter().any(|s| {
5192                    s.elements
5193                        .get(2)
5194                        .and_then(|e: &Vec<String>| e.first())
5195                        .is_some_and(|v: &String| v == "ZA6")
5196                });
5197                if has_cci_za6 {
5198                    let cavs =
5199                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5200                    if cavs.iter().any(|s| {
5201                        s.elements
5202                            .first()
5203                            .and_then(|e: &Vec<String>| e.first())
5204                            .is_some_and(|v: &String| v == "Z36")
5205                    }) {
5206                        return ConditionResult::True;
5207                    }
5208                }
5209            }
5210        }
5211        ConditionResult::False
5212    }
5213
5214    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5215    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5216        let seq_segs = ctx.find_segments("SEQ");
5217        let found = seq_segs.iter().any(|s| {
5218            s.elements
5219                .first()
5220                .and_then(|e| e.first())
5221                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5222        });
5223        ConditionResult::from(!found)
5224    }
5225
5226    /// [268] Wenn der Code im DE3207 in der \"EDI@Energy Codeliste der europäischen Ländercodes\" in der Spalte \"PLZ vorhanden\" ein \"X\" aufgeführt ist
5227    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5228        // Most European countries have postal codes — default to true
5229        ConditionResult::True
5230    }
5231
5232    /// [269] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap.3 anzugeben welche an der Tranche erforderlich sind
5233    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5234        // TODO: implement
5235        ConditionResult::Unknown
5236    }
5237
5238    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5239    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5240        let pia_segs = ctx.find_segments("PIA");
5241        let found = pia_segs.iter().any(|s| {
5242            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5243                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5244                prefixes.iter().any(|p| v.starts_with(p))
5245            })
5246        });
5247        ConditionResult::from(found)
5248    }
5249
5250    /// [273] Wenn in derselben SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e / 1-b?:3.8.e / 1-b?:4.8.e / 1-b?:5.8.e / 1-b?:6.8.e / 1-b?:7.8.e / 1-b?:8.8.e ...
5251    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5252        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5253        // The PIA+5 OBIS must be an energy register `1-b:c.8.e` (c = 1..8;
5254        // 1.8.63 is one of them). A load profile such as 1-1:1.29.0 is not
5255        // (mako-twin, 55168: it demanded `produkt` and Z63 of a 1.29.0 item).
5256        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5257        let found = pia_segs.iter().any(|s| {
5258            s.elements
5259                .first()
5260                .and_then(|e| e.first())
5261                .is_some_and(|v| v == "5")
5262                && s.elements
5263                    .get(1)
5264                    .and_then(|e| e.first())
5265                    .is_some_and(|v| is_energy_register_obis(v))
5266        });
5267        ConditionResult::from(found)
5268    }
5269
5270    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5271    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5272        let sts_segs = ctx.find_segments("STS");
5273        let found = sts_segs.iter().any(|s| {
5274            s.elements
5275                .first()
5276                .and_then(|e| e.first())
5277                .is_some_and(|v| v == "7")
5278                && s.elements
5279                    .get(3)
5280                    .and_then(|e| e.first())
5281                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5282        });
5283        ConditionResult::from(found)
5284    }
5285
5286    /// [280] Nur MP-ID aus Sparte Strom
5287    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5288        // Check: Marktpartner-ID is from Sparte Strom
5289        // Approximate: check NAD+MS C082 for Strom MP-ID format
5290        ctx.external.evaluate("sector_check")
5291    }
5292
5293    /// [282] Wenn in diesem PIA der Code im DE7140 1-01-6-005, 1-01-9-001/002, 1-07-3-001, 1- 08-1-001, 1-08-3-001, 1-08-4-001/002/003/004, 1-10-3-001 enthalten ist
5294    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5295        // Check: PIA DE7140 has one of the specific codes
5296        let pia_segs = ctx.find_segments("PIA");
5297        let found = pia_segs.iter().any(|s| {
5298            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5299                v.starts_with("1-01-6-005")
5300                    || v.starts_with("1-01-9-001")
5301                    || v.starts_with("1-01-9-002")
5302                    || v.starts_with("1-07-3-001")
5303                    || v.starts_with("1-08-1-001")
5304                    || v.starts_with("1-08-3-001")
5305                    || v.starts_with("1-08-4-001")
5306                    || v.starts_with("1-08-4-002")
5307                    || v.starts_with("1-08-4-003")
5308                    || v.starts_with("1-08-5")
5309            })
5310        });
5311        ConditionResult::from(found)
5312    }
5313
5314    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5315    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5316    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5317    // Regenerating this function should preserve the shape, but re-verify
5318    // against the committed body if generate-conditions is run.
5319    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5320        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5321        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5322        //
5323        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5324        // narrows to the enclosing SG8 instance; otherwise falls back to
5325        // message-wide find_segments. Matches the AHB wording
5326        // "in derselben SG8 im PIA DE7140 ein Code...".
5327        use crate::eval::CodelistColumn;
5328        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5329            let is_z02 = s
5330                .elements
5331                .first()
5332                .and_then(|e| e.first())
5333                .is_some_and(|v| v == "Z02");
5334            if !is_z02 {
5335                return false;
5336            }
5337            let code = match s.elements.get(1).and_then(|e| e.first()) {
5338                Some(c) if !c.is_empty() => c,
5339                _ => return false,
5340            };
5341            ctx.codelist
5342                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5343                .unwrap_or(false)
5344        });
5345        ConditionResult::from(any_match)
5346    }
5347
5348    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5349    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5350        // Check: PIA DE7140 starts with 1-08-3-AGS
5351        let pia_segs = ctx.find_segments("PIA");
5352        let found = pia_segs.iter().any(|s| {
5353            s.elements
5354                .get(1)
5355                .and_then(|e| e.first())
5356                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5357        });
5358        ConditionResult::from(found)
5359    }
5360
5361    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5362    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5363        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5364        let pia_segs = ctx.find_segments("PIA");
5365        let found = pia_segs.iter().any(|s| {
5366            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5367                (v.starts_with("1-08-1")
5368                    || v.starts_with("1-08-2")
5369                    || v.starts_with("1-08-4")
5370                    || v.starts_with("1-08-5"))
5371                    && v.contains("AGS")
5372                    && v.contains("KG")
5373            })
5374        });
5375        ConditionResult::from(found)
5376    }
5377
5378    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5379    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5380        // TODO: implement
5381        ConditionResult::Unknown
5382    }
5383
5384    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5385    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5386        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5387        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5388        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5389        let found = pia_segs.iter().any(|s| {
5390            s.elements
5391                .first()
5392                .and_then(|e| e.first())
5393                .is_some_and(|v| v == "5")
5394                && s.elements
5395                    .get(1)
5396                    .and_then(|e| e.first())
5397                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5398        });
5399        ConditionResult::from(found)
5400    }
5401
5402    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5403    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5404        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5405        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5406        let found = rff_segs.iter().any(|s| {
5407            s.elements
5408                .first()
5409                .and_then(|e| e.first())
5410                .is_some_and(|v| v == "Z37")
5411        });
5412        ConditionResult::from(found)
5413    }
5414
5415    /// [292] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTILMD)\" in der Spalte \"Anmeldung einer Zuordnung des LFN (UTIL...
5416    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5417        ctx.external.evaluate("code_list_membership_check")
5418    }
5419
5420    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5421    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5422        let loc_segs = ctx.find_segments("LOC");
5423        let found = loc_segs.iter().any(|s| {
5424            s.elements
5425                .first()
5426                .and_then(|e| e.first())
5427                .is_some_and(|v| v == "Z22")
5428        });
5429        ConditionResult::from(found)
5430    }
5431
5432    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5433    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5434        let loc_segs = ctx.find_segments("LOC");
5435        let found = loc_segs.iter().any(|s| {
5436            s.elements
5437                .first()
5438                .and_then(|e| e.first())
5439                .is_some_and(|v| v == "Z18")
5440        });
5441        ConditionResult::from(found)
5442    }
5443
5444    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5445    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5446        let loc_segs = ctx.find_segments("LOC");
5447        let found = loc_segs.iter().any(|s| {
5448            s.elements
5449                .first()
5450                .and_then(|e| e.first())
5451                .is_some_and(|v| v == "Z15")
5452        });
5453        ConditionResult::from(!found)
5454    }
5455
5456    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5457    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5458        let loc_segs = ctx.find_segments("LOC");
5459        let found = loc_segs.iter().any(|s| {
5460            s.elements
5461                .first()
5462                .and_then(|e| e.first())
5463                .is_some_and(|v| v == "Z20")
5464        });
5465        ConditionResult::from(found)
5466    }
5467
5468    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5469    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5470        let loc_segs = ctx.find_segments("LOC");
5471        let found = loc_segs.iter().any(|s| {
5472            s.elements
5473                .first()
5474                .and_then(|e| e.first())
5475                .is_some_and(|v| v == "Z18")
5476        });
5477        ConditionResult::from(!found)
5478    }
5479
5480    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5481    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5482        let loc_segs = ctx.find_segments("LOC");
5483        let found = loc_segs.iter().any(|s| {
5484            s.elements
5485                .first()
5486                .and_then(|e| e.first())
5487                .is_some_and(|v| v == "Z20")
5488        });
5489        ConditionResult::from(!found)
5490    }
5491
5492    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5493    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5494        let loc_segs = ctx.find_segments("LOC");
5495        let found = loc_segs.iter().any(|s| {
5496            s.elements
5497                .first()
5498                .and_then(|e| e.first())
5499                .is_some_and(|v| v == "Z19")
5500        });
5501        ConditionResult::from(!found)
5502    }
5503
5504    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5505    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5506        let seq_segs = ctx.find_segments("SEQ");
5507        let found = seq_segs.iter().any(|s| {
5508            s.elements
5509                .first()
5510                .and_then(|e| e.first())
5511                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5512        });
5513        ConditionResult::from(!found)
5514    }
5515
5516    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5517    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5518        let bgm_segs = ctx.find_segments("BGM");
5519        let found = bgm_segs.iter().any(|s| {
5520            s.elements
5521                .first()
5522                .and_then(|e| e.first())
5523                .is_some_and(|v| v == "E03")
5524        });
5525        ConditionResult::from(found)
5526    }
5527
5528    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5529    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5530        let seq_segs = ctx.find_segments("SEQ");
5531        let found = seq_segs.iter().any(|s| {
5532            s.elements
5533                .first()
5534                .and_then(|e| e.first())
5535                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5536        });
5537        ConditionResult::from(!found)
5538    }
5539
5540    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5541    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5542        let seq_segs = ctx.find_segments("SEQ");
5543        let found = seq_segs.iter().any(|s| {
5544            s.elements
5545                .first()
5546                .and_then(|e| e.first())
5547                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5548        });
5549        ConditionResult::from(!found)
5550    }
5551
5552    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5553    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5554        let seq_segs = ctx.find_segments("SEQ");
5555        let found = seq_segs.iter().any(|s| {
5556            s.elements
5557                .first()
5558                .and_then(|e| e.first())
5559                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5560        });
5561        ConditionResult::from(found)
5562    }
5563
5564    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5565    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5566        let seq_segs = ctx.find_segments("SEQ");
5567        let found = seq_segs.iter().any(|s| {
5568            s.elements
5569                .first()
5570                .and_then(|e| e.first())
5571                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5572        });
5573        ConditionResult::from(found)
5574    }
5575
5576    /// [306] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5577    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5578        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5579        // reaches an SG8; the message as before otherwise.
5580        if let Some(scope) = ctx.enclosing_scope("SG8") {
5581            return ConditionResult::from(
5582                sg4_instances::seq_at(ctx, scope)
5583                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5584                    .is_some_and(|(_, zeitraum)| {
5585                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5586                    }),
5587            );
5588        }
5589        use std::collections::HashSet;
5590        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5591        let loc_z22_ids: HashSet<String> = ctx
5592            .find_segments_with_qualifier("LOC", 0, "Z22")
5593            .into_iter()
5594            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5595            .filter(|v| !v.is_empty())
5596            .collect();
5597        if loc_z22_ids.is_empty() {
5598            return ConditionResult::False;
5599        }
5600        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5601        let has_match = ctx
5602            .find_segments_with_qualifier("SEQ", 0, "Z58")
5603            .iter()
5604            .any(|s| {
5605                s.elements
5606                    .get(1)
5607                    .and_then(|e: &Vec<String>| e.first())
5608                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5609            });
5610        ConditionResult::from(has_match)
5611    }
5612
5613    /// [307] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 nicht vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5614    // REVIEW: Collects Zeitraum-IDs from SG5 LOC+Z22 segments (DE3224 = elements[1][3]) and SEQ+Z58 segments (DE1050 = elements[1][0]). Returns True when any SEQ+Z58 has a Zeitraum-ID with no matching LOC+Z22 entry. Message-wide fallback since group-level Zeitraum-ID isolation is not possible without navigator. (medium confidence)
5615    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5616        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID … nicht vorhanden wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5617        // reaches an SG8; the message as before otherwise.
5618        if let Some(scope) = ctx.enclosing_scope("SG8") {
5619            return ConditionResult::from(
5620                sg4_instances::seq_at(ctx, scope)
5621                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5622                    .is_some_and(|(_, zeitraum)| {
5623                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5624                    }),
5625            );
5626        }
5627        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5628        if seq_segs.is_empty() {
5629            return ConditionResult::Unknown;
5630        }
5631        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5632        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5633            .iter()
5634            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5635            .filter(|v| !v.is_empty())
5636            .collect();
5637        ConditionResult::from(seq_segs.iter().any(|seq| {
5638            let zid = seq
5639                .elements
5640                .get(1)
5641                .and_then(|e: &Vec<String>| e.first())
5642                .map(|v| v.as_str())
5643                .unwrap_or("");
5644            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5645        }))
5646    }
5647
5648    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5649    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5650        let loc_segs = ctx.find_segments("LOC");
5651        let found = loc_segs.iter().any(|s| {
5652            s.elements
5653                .first()
5654                .and_then(|e| e.first())
5655                .is_some_and(|v| v == "Z22")
5656        });
5657        ConditionResult::from(!found)
5658    }
5659
5660    /// [314] Es sind alle OBIS-Kennzahlen gem. Codeliste der OBIS-Kennzahlen und Medien Kapitel 3 anzugeben welche an der Marktlokation erforderlich sind. Der Mindestumfang der OBIS-Kennzahlen ergibt sich aus d...
5661    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5662        ctx.external.evaluate("code_list_membership_check")
5663    }
5664
5665    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5666    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5667        ctx.external.evaluate("data_clearing_required")
5668    }
5669
5670    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5671    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5672        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
5673        // whatever its SEQ (Z03, ZA3/ZA4, ZF5 all carry [316]; #305).
5674        ctx.this_group_lacks_qualifier("RFF", 0, "Z14", &["SG4", "SG8"])
5675    }
5676
5677    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5678    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5679        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5680        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5681        if found {
5682            ConditionResult::True
5683        } else {
5684            ConditionResult::Unknown
5685        }
5686    }
5687
5688    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5689    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5690        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5691        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5692        if found {
5693            ConditionResult::True
5694        } else {
5695            ConditionResult::Unknown
5696        }
5697    }
5698
5699    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5700    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5701        let com_segs = ctx.find_segments("COM");
5702        let has_em = com_segs.iter().any(|s| {
5703            s.elements
5704                .first()
5705                .and_then(|e| e.get(1))
5706                .is_some_and(|v| v == "EM")
5707        });
5708        ConditionResult::from(has_em)
5709    }
5710
5711    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5712    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5713        let com_segs = ctx.find_segments("COM");
5714        let has_phone = com_segs.iter().any(|s| {
5715            s.elements
5716                .first()
5717                .and_then(|e| e.get(1))
5718                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5719        });
5720        ConditionResult::from(has_phone)
5721    }
5722
5723    /// [323] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap. 3. anzugeben, welche an der Zähleinrichtung genutzt werden. Der Mindestum...
5724    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5725        ctx.external.evaluate("code_list_membership_check")
5726    }
5727
5728    /// [327] Wenn die Zeitraum-ID im DE1050 dieser SG8 SEQ, welche mit der Zeitraum-ID einer SG6 RFF+Z47/Z48/Z49 (Verwendungszeitraum der Daten) identisch ist und in deren SG das SG6 DTM+Z25 (Verwendung der Dat...
5729    // REVIEW: Step 1: Collect all SEQ DE1050 Zeitraum-IDs. Step 2: Find RFF+Z47/Z48/Z49 (Verwendungszeitraum) whose DE1156 (elements[0][2]) matches. Step 3: Check co-located DTM+Z25 DE2380 (elements[0][1]) >= threshold after normalising EDIFACT escape '?+' to '+'. Falls back to message-wide DTM search which may give false positives if multiple SG6 groups are present, but is sufficient for typical single-transaction messages. (medium confidence)
5730    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5731        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5732        // reaches an SG8; the message as before otherwise.
5733        if let Some(scope) = ctx.enclosing_scope("SG8") {
5734            let Some((_, zeitraum)) =
5735                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5736            else {
5737                return ConditionResult::False;
5738            };
5739            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5740            return ConditionResult::from(sg6s.iter().any(|p| {
5741                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5742                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5743                        && s.get_component(0, 2) == zeitraum
5744                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5745                    s.get_element(0) == "Z25"
5746                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5747                })
5748            }));
5749        }
5750        const THRESHOLD: &str = "202212312300+00";
5751        let seq_zeitraum_ids: Vec<String> = ctx
5752            .find_segments("SEQ")
5753            .iter()
5754            .filter_map(|s| {
5755                s.elements
5756                    .get(1)
5757                    .and_then(|e: &Vec<String>| e.first())
5758                    .cloned()
5759            })
5760            .filter(|v| !v.is_empty())
5761            .collect();
5762        if seq_zeitraum_ids.is_empty() {
5763            return ConditionResult::Unknown;
5764        }
5765        let rff_segs = ctx.find_segments("RFF");
5766        let matching_rff = rff_segs.iter().any(|rff| {
5767            let qual = rff
5768                .elements
5769                .first()
5770                .and_then(|e: &Vec<String>| e.first())
5771                .map(|v| v.as_str())
5772                .unwrap_or("");
5773            if !["Z47", "Z48", "Z49"].contains(&qual) {
5774                return false;
5775            }
5776            let zid = rff
5777                .elements
5778                .first()
5779                .and_then(|e| e.get(2))
5780                .map(|v| v.as_str())
5781                .unwrap_or("");
5782            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5783        });
5784        if !matching_rff {
5785            return ConditionResult::False;
5786        }
5787        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5788            let val = dtm
5789                .elements
5790                .first()
5791                .and_then(|e| e.get(1))
5792                .map(|v| v.replace("?+", "+"))
5793                .unwrap_or_default();
5794            if !val.is_empty() && val.as_str() >= THRESHOLD {
5795                return ConditionResult::True;
5796            }
5797        }
5798        ConditionResult::False
5799    }
5800
5801    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5802    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5803        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
5804        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
5805        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
5806        let has_a03_e0096 = sts_segs.iter().any(|s| {
5807            s.elements
5808                .get(2)
5809                .and_then(|e| e.first())
5810                .is_some_and(|v| v == "A03")
5811                && s.elements
5812                    .get(2)
5813                    .and_then(|e| e.get(1))
5814                    .is_some_and(|v| v == "E_0096")
5815        });
5816        let has_a04_e0097 = sts_segs.iter().any(|s| {
5817            s.elements
5818                .get(2)
5819                .and_then(|e| e.first())
5820                .is_some_and(|v| v == "A04")
5821                && s.elements
5822                    .get(2)
5823                    .and_then(|e| e.get(1))
5824                    .is_some_and(|v| v == "E_0097")
5825        });
5826        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
5827    }
5828
5829    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5830    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
5831        let seq_segs = ctx.find_segments("SEQ");
5832        let has_seq = seq_segs.iter().any(|s| {
5833            s.elements
5834                .first()
5835                .and_then(|e| e.first())
5836                .is_some_and(|v| v == "Z98")
5837        });
5838        let cci_segs = ctx.find_segments("CCI");
5839        let has_cci = cci_segs.iter().any(|s| {
5840            s.elements
5841                .first()
5842                .and_then(|e| e.first())
5843                .is_some_and(|v| v == "6")
5844                && s.elements
5845                    .get(2)
5846                    .and_then(|e| e.first())
5847                    .is_some_and(|v| v == "ZA9")
5848        });
5849        ConditionResult::from(!(has_seq && has_cci))
5850    }
5851
5852    /// [332] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in dieser SG8, das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5853    // REVIEW: Checks whether any SG8 instance lacks CCI+6++ZA9 (Aggregationsverantwortung ÜNB, elements[0]='6' and elements[2][0]='ZA9') in its SG10 children. With navigator: iterates SG8 instances and their SG10 children. Without navigator: falls back to message-wide CCI+6 with ZA9 check. The Zeitraum-ID cross-reference between this SG8 and the Z01 SG8 cannot be fully verified without direct SG8-level segment access via navigator, so this is an approximation that may produce false positives in multi-SG4-group messages. (medium confidence)
5854    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
5855        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
5856        // reaches an SG8; the message as before otherwise.
5857        if let Some(scope) = ctx.enclosing_scope("SG8") {
5858            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5859            return ConditionResult::from(z01s.iter().any(|p| {
5860                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
5861            }));
5862        }
5863        let nav = match ctx.navigator {
5864            Some(n) => n,
5865            None => {
5866                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
5867                    return ConditionResult::Unknown;
5868                }
5869                let has_cci_za9 = ctx
5870                    .find_segments_with_qualifier("CCI", 0, "6")
5871                    .iter()
5872                    .any(|s| {
5873                        s.elements
5874                            .get(2)
5875                            .and_then(|e: &Vec<String>| e.first())
5876                            .map(|v| v.as_str())
5877                            == Some("ZA9")
5878                    });
5879                return ConditionResult::from(!has_cci_za9);
5880            }
5881        };
5882        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5883        if sg8_count == 0 {
5884            return ConditionResult::Unknown;
5885        }
5886        for sg8_idx in 0..sg8_count {
5887            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
5888            let mut has_cci_za9 = false;
5889            for sg10_idx in 0..sg10_count {
5890                let ccis = nav.find_segments_in_child_group(
5891                    "CCI",
5892                    &["SG4", "SG8"],
5893                    sg8_idx,
5894                    "SG10",
5895                    sg10_idx,
5896                );
5897                if ccis.iter().any(|s| {
5898                    s.elements
5899                        .first()
5900                        .and_then(|e: &Vec<String>| e.first())
5901                        .map(|v| v.as_str())
5902                        == Some("6")
5903                        && s.elements
5904                            .get(2)
5905                            .and_then(|e: &Vec<String>| e.first())
5906                            .map(|v| v.as_str())
5907                            == Some("ZA9")
5908                }) {
5909                    has_cci_za9 = true;
5910                    break;
5911                }
5912            }
5913            if !has_cci_za9 {
5914                return ConditionResult::True;
5915            }
5916        }
5917        ConditionResult::False
5918    }
5919
5920    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
5921    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
5922        let rff_segs = ctx.find_segments("RFF");
5923        let found = rff_segs.iter().any(|s| {
5924            s.elements
5925                .first()
5926                .and_then(|e| e.first())
5927                .is_some_and(|v| v == "Z31")
5928        });
5929        ConditionResult::from(found)
5930    }
5931
5932    /// [334] Nur MP-ID aus Sparte Gas
5933    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
5934        // Check: Marktpartner-ID is from Sparte Gas
5935        ctx.external.evaluate("sector_check")
5936    }
5937
5938    /// [335] Wenn in dieser SG8 im SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) im DE1050 eine Zeitraum-ID angegeben wird, wie in einer SG8 SEQ+Z78 RFF+Z31 (Referenz auf die Lokationsbünde...
5939    // REVIEW: Collects Zeitraum-IDs from SEQ+Z58 DE1050 (elements[1][0]). Verifies an SG8 with SEQ+Z78 exists (entry for Lokationsbündelstruktur). Collects IDs from RFF+Z31 DE1154 (elements[0][1]) and checks if any SEQ+Z58 Zeitraum-ID matches. Co-location of RFF+Z31 with SEQ+Z78 in the same SG8 instance is approximated message-wide; exact co-location verification would require navigator SG8-level segment access. (medium confidence)
5940    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
5941        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
5942        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
5943        let z58_ids: Vec<String> = ctx
5944            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
5945            .iter()
5946            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
5947            .filter(|v| !v.is_empty())
5948            .collect();
5949        if z58_ids.is_empty() {
5950            return ConditionResult::Unknown;
5951        }
5952        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
5953            return ConditionResult::False;
5954        }
5955        let rff_z31_ids: Vec<String> = ctx
5956            .find_segments_with_qualifier("RFF", 0, "Z31")
5957            .iter()
5958            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
5959            .filter(|v| !v.is_empty())
5960            .collect();
5961        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
5962    }
5963
5964    /// [336] Wenn in Änderungsmeldung gefüllt
5965    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
5966        // TODO: implement
5967        ConditionResult::Unknown
5968    }
5969
5970    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
5971    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
5972        let cci_segs = ctx.find_segments("CCI");
5973        let has_cci = cci_segs.iter().any(|s| {
5974            s.elements
5975                .get(2)
5976                .and_then(|e| e.first())
5977                .is_some_and(|v| v == "ZB4")
5978        });
5979        let cav_segs = ctx.find_segments("CAV");
5980        let has_cav = cav_segs.iter().any(|s| {
5981            s.elements
5982                .first()
5983                .and_then(|e| e.first())
5984                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
5985        });
5986        ConditionResult::from(has_cci && has_cav)
5987    }
5988
5989    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
5990    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
5991        let cci_segs = ctx.find_segments("CCI");
5992        let has_cci = cci_segs.iter().any(|s| {
5993            s.elements
5994                .get(2)
5995                .and_then(|e| e.first())
5996                .is_some_and(|v| v == "ZB4")
5997        });
5998        let cav_segs = ctx.find_segments("CAV");
5999        let has_cav = cav_segs.iter().any(|s| {
6000            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6001                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6002            })
6003        });
6004        ConditionResult::from(has_cci && has_cav)
6005    }
6006
6007    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6008    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6009        let cci_segs = ctx.find_segments("CCI");
6010        let has_cci = cci_segs.iter().any(|s| {
6011            s.elements
6012                .get(2)
6013                .and_then(|e| e.first())
6014                .is_some_and(|v| v == "ZB4")
6015        });
6016        let cav_segs = ctx.find_segments("CAV");
6017        let has_cav = cav_segs.iter().any(|s| {
6018            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6019                [
6020                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6021                ]
6022                .contains(&v.as_str())
6023            })
6024        });
6025        ConditionResult::from(has_cci && has_cav)
6026    }
6027
6028    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6029    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6030        let cci_segs = ctx.find_segments("CCI");
6031        let has_cci = cci_segs.iter().any(|s| {
6032            s.elements
6033                .get(2)
6034                .and_then(|e| e.first())
6035                .is_some_and(|v| v == "ZB4")
6036        });
6037        let cav_segs = ctx.find_segments("CAV");
6038        let has_cav = cav_segs.iter().any(|s| {
6039            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6040                [
6041                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6042                ]
6043                .contains(&v.as_str())
6044            })
6045        });
6046        ConditionResult::from(has_cci && has_cav)
6047    }
6048
6049    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6050    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6051        let cci_segs = ctx.find_segments("CCI");
6052        let has_cci = cci_segs.iter().any(|s| {
6053            s.elements
6054                .get(2)
6055                .and_then(|e| e.first())
6056                .is_some_and(|v| v == "ZB4")
6057        });
6058        let cav_segs = ctx.find_segments("CAV");
6059        let has_cav = cav_segs.iter().any(|s| {
6060            s.elements
6061                .first()
6062                .and_then(|e| e.first())
6063                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6064        });
6065        ConditionResult::from(has_cci && has_cav)
6066    }
6067
6068    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6069    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6070        // TODO: implement
6071        ConditionResult::Unknown
6072    }
6073
6074    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6075    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6076        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6077        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6078        let has_cci = cci_segs.iter().any(|s| {
6079            s.elements
6080                .get(2)
6081                .and_then(|e| e.first())
6082                .is_some_and(|v| v == "ZB4")
6083        });
6084        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6085        let has_cav = cav_segs.iter().any(|s| {
6086            s.elements
6087                .first()
6088                .and_then(|e| e.first())
6089                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6090        });
6091        ConditionResult::from(has_cci && has_cav)
6092    }
6093
6094    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6095    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6096        let loc_segs = ctx.find_segments("LOC");
6097        let found = loc_segs.iter().any(|s| {
6098            s.elements
6099                .first()
6100                .and_then(|e| e.first())
6101                .is_some_and(|v| v == "Z17")
6102        });
6103        ConditionResult::from(found)
6104    }
6105
6106    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6107    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6108        // HAND-EDITED: the CCI and the CAV of one SG10.
6109        sg10_has(ctx, false, |segs| {
6110            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6111        })
6112    }
6113
6114    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6115    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6116        let loc_segs = ctx.find_segments("LOC");
6117        let found = loc_segs.iter().any(|s| {
6118            s.elements
6119                .first()
6120                .and_then(|e| e.first())
6121                .is_some_and(|v| v == "Z16")
6122        });
6123        ConditionResult::from(!found)
6124    }
6125
6126    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6127    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6128        let loc_segs = ctx.find_segments("LOC");
6129        let found = loc_segs.iter().any(|s| {
6130            s.elements
6131                .first()
6132                .and_then(|e| e.first())
6133                .is_some_and(|v| v == "Z21")
6134        });
6135        ConditionResult::from(!found)
6136    }
6137
6138    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6139    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6140        let loc_segs = ctx.find_segments("LOC");
6141        let found = loc_segs.iter().any(|s| {
6142            s.elements
6143                .first()
6144                .and_then(|e| e.first())
6145                .is_some_and(|v| v == "Z17")
6146        });
6147        ConditionResult::from(!found)
6148    }
6149
6150    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6151    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6152        let seq_segs = ctx.find_segments("SEQ");
6153        let has_seq = seq_segs.iter().any(|s| {
6154            s.elements
6155                .first()
6156                .and_then(|e| e.first())
6157                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6158        });
6159        let rff_segs = ctx.find_segments("RFF");
6160        let has_rff = rff_segs.iter().any(|s| {
6161            s.elements
6162                .first()
6163                .and_then(|e| e.first())
6164                .is_some_and(|v| v == "Z31")
6165        });
6166        ConditionResult::from(has_seq && has_rff)
6167    }
6168
6169    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6170    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6171        let sts_segs = ctx.find_segments("STS");
6172        let found = sts_segs.iter().any(|s| {
6173            s.elements
6174                .first()
6175                .and_then(|e| e.first())
6176                .is_some_and(|v| v == "E01")
6177                && s.elements
6178                    .get(2)
6179                    .and_then(|e| e.first())
6180                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6181        });
6182        ConditionResult::from(found)
6183    }
6184
6185    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6186    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6187        // HAND-EDITED: the answer stands in C556 DE9013 (element 2), as in [351].
6188        let sts_segs = ctx.find_segments("STS");
6189        let found = sts_segs.iter().any(|s| {
6190            s.elements
6191                .get(2)
6192                .and_then(|e| e.first())
6193                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6194        });
6195        ConditionResult::from(found)
6196    }
6197
6198    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6199    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6200        let sts_segs = ctx.find_segments("STS");
6201        let found = sts_segs.iter().any(|s| {
6202            s.elements
6203                .first()
6204                .and_then(|e| e.first())
6205                .is_some_and(|v| v == "E01")
6206                && s.elements
6207                    .get(2)
6208                    .and_then(|e| e.first())
6209                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6210        });
6211        ConditionResult::from(found)
6212    }
6213
6214    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6215    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6216        let sts_segs = ctx.find_segments("STS");
6217        let found = sts_segs.iter().any(|s| {
6218            s.elements
6219                .first()
6220                .and_then(|e| e.first())
6221                .is_some_and(|v| v == "E01")
6222                && s.elements
6223                    .get(2)
6224                    .and_then(|e| e.first())
6225                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6226        });
6227        ConditionResult::from(found)
6228    }
6229
6230    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6231    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6232        let sts_segs = ctx.find_segments("STS");
6233        let found = sts_segs.iter().any(|s| {
6234            s.elements
6235                .first()
6236                .and_then(|e| e.first())
6237                .is_some_and(|v| v == "E01")
6238                && s.elements
6239                    .get(2)
6240                    .and_then(|e| e.first())
6241                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6242        });
6243        ConditionResult::from(found)
6244    }
6245
6246    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6247    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6248        let sts_segs = ctx.find_segments("STS");
6249        let found = sts_segs.iter().any(|s| {
6250            s.elements
6251                .first()
6252                .and_then(|e| e.first())
6253                .is_some_and(|v| v == "E01")
6254                && s.elements
6255                    .get(2)
6256                    .and_then(|e| e.first())
6257                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6258        });
6259        ConditionResult::from(found)
6260    }
6261
6262    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6263    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6264    // Preserve across regeneration until the codegen prompt is updated.
6265    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6266        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6267    }
6268
6269    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6270    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6271    // Preserve across regeneration until the codegen prompt is updated.
6272    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6273        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6274    }
6275
6276    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6277    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6278        let sts_segs = ctx.find_segments("STS");
6279        let found = sts_segs.iter().any(|s| {
6280            s.elements
6281                .first()
6282                .and_then(|e| e.first())
6283                .is_some_and(|v| v == "E01")
6284                && s.elements
6285                    .get(2)
6286                    .and_then(|e| e.first())
6287                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6288        });
6289        ConditionResult::from(found)
6290    }
6291
6292    /// [365] Wenn in dieser SG4 IDE+24 das STS+E01++A04:E_0047/E_0014 bzw. A03:E_0049/E_0004 (Status der Antwort) nicht vorhanden
6293    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6294        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6295        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6296        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6297        let has_excluded = sts_segs.iter().any(|s| {
6298            let code = s
6299                .elements
6300                .get(2)
6301                .and_then(|e| e.first())
6302                .map(|v| v.as_str());
6303            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6304            matches!(
6305                (code, ebd),
6306                (Some("A04"), Some("E_0047"))
6307                    | (Some("A04"), Some("E_0014"))
6308                    | (Some("A03"), Some("E_0049"))
6309                    | (Some("A03"), Some("E_0004"))
6310            )
6311        });
6312        ConditionResult::from(!has_excluded)
6313    }
6314
6315    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6316    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6317    // Preserve across regeneration until the codegen prompt is updated.
6318    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6319        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A30"])
6320    }
6321
6322    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6323    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6324        let sts_segs = ctx.find_segments("STS");
6325        let found = sts_segs.iter().any(|s| {
6326            s.elements
6327                .first()
6328                .and_then(|e| e.first())
6329                .is_some_and(|v| v == "E01")
6330                && s.elements
6331                    .get(2)
6332                    .and_then(|e| e.first())
6333                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6334        });
6335        ConditionResult::from(found)
6336    }
6337
6338    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6339    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6340    // Preserve across regeneration until the codegen prompt is updated.
6341    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6342        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A41"])
6343    }
6344
6345    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6346    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6347        let cav_segs = ctx.find_segments("CAV");
6348        let found = cav_segs.iter().any(|s| {
6349            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6350                [
6351                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6352                ]
6353                .contains(&v.as_str())
6354            })
6355        });
6356        ConditionResult::from(found)
6357    }
6358
6359    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6360    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6361        let seq_segs = ctx.find_segments("SEQ");
6362        let found = seq_segs.iter().any(|s| {
6363            s.elements
6364                .first()
6365                .and_then(|e| e.first())
6366                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6367        });
6368        ConditionResult::from(!found)
6369    }
6370
6371    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6372    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6373        let cav_segs = ctx.find_segments("CAV");
6374        let found = cav_segs.iter().any(|s| {
6375            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6376                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6377            })
6378        });
6379        ConditionResult::from(found)
6380    }
6381
6382    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6383    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6384        // TODO: implement
6385        ConditionResult::Unknown
6386    }
6387
6388    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6389    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6390        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6391        ctx.lacks_qualifier("SEQ", 0, "Z24")
6392    }
6393
6394    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6395    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6396        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6397        ctx.lacks_qualifier("SEQ", 0, "Z22")
6398    }
6399
6400    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6401    // REVIEW: Checks if any SG8 has a child SG10 with CCI[0]='15' and CCI[2]='Z21' (Überführungszeitreihentyp) followed by CAV with value 'AUS'. Follows the navigator pattern from example 10 exactly. (medium confidence)
6402    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6403        let nav = match ctx.navigator {
6404            Some(n) => n,
6405            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6406        };
6407        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6408        for i in 0..sg8_count {
6409            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6410            for j in 0..sg10_count {
6411                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6412                let has_cci = ccis.iter().any(|s| {
6413                    s.elements
6414                        .first()
6415                        .and_then(|e: &Vec<String>| e.first())
6416                        .is_some_and(|v: &String| v == "15")
6417                        && s.elements
6418                            .get(2)
6419                            .and_then(|e: &Vec<String>| e.first())
6420                            .is_some_and(|v: &String| v == "Z21")
6421                });
6422                if has_cci {
6423                    let cavs =
6424                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6425                    if cavs.iter().any(|s| {
6426                        s.elements
6427                            .first()
6428                            .and_then(|e: &Vec<String>| e.first())
6429                            .is_some_and(|v: &String| v == "AUS")
6430                    }) {
6431                        return ConditionResult::True;
6432                    }
6433                }
6434            }
6435        }
6436        ConditionResult::False
6437    }
6438
6439    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6440    // REVIEW: Logical negation of condition 377: True when CCI+15++Z21 CAV+AUS is NOT present in any SG8's SG10 child. Iterates all instances and returns True only if the combination is never found. (medium confidence)
6441    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6442        let nav = match ctx.navigator {
6443            Some(n) => n,
6444            None => {
6445                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6446                return match r {
6447                    ConditionResult::True => ConditionResult::False,
6448                    ConditionResult::False => ConditionResult::True,
6449                    ConditionResult::Unknown => ConditionResult::Unknown,
6450                };
6451            }
6452        };
6453        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6454        for i in 0..sg8_count {
6455            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6456            for j in 0..sg10_count {
6457                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6458                let has_cci = ccis.iter().any(|s| {
6459                    s.elements
6460                        .first()
6461                        .and_then(|e: &Vec<String>| e.first())
6462                        .is_some_and(|v: &String| v == "15")
6463                        && s.elements
6464                            .get(2)
6465                            .and_then(|e: &Vec<String>| e.first())
6466                            .is_some_and(|v: &String| v == "Z21")
6467                });
6468                if has_cci {
6469                    let cavs =
6470                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6471                    if cavs.iter().any(|s| {
6472                        s.elements
6473                            .first()
6474                            .and_then(|e: &Vec<String>| e.first())
6475                            .is_some_and(|v: &String| v == "AUS")
6476                    }) {
6477                        return ConditionResult::False;
6478                    }
6479                }
6480            }
6481        }
6482        ConditionResult::True
6483    }
6484
6485    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6486    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6487        let cci_segs = ctx.find_segments("CCI");
6488        let found = cci_segs.iter().any(|s| {
6489            s.elements
6490                .first()
6491                .and_then(|e| e.first())
6492                .is_some_and(|v| v == "15")
6493                && s.elements
6494                    .get(2)
6495                    .and_then(|e| e.first())
6496                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6497        });
6498        ConditionResult::from(found)
6499    }
6500
6501    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6502    // REVIEW: Variant of condition 377 that accepts either 'AUS' or 'AU1' as the CAV value for the Überführungszeitreihentyp. Same SG8→SG10 parent-child navigation, broadened CAV value check. (medium confidence)
6503    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6504        let nav = match ctx.navigator {
6505            Some(n) => n,
6506            None => {
6507                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6508            }
6509        };
6510        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6511        for i in 0..sg8_count {
6512            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6513            for j in 0..sg10_count {
6514                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6515                let has_cci = ccis.iter().any(|s| {
6516                    s.elements
6517                        .first()
6518                        .and_then(|e: &Vec<String>| e.first())
6519                        .is_some_and(|v: &String| v == "15")
6520                        && s.elements
6521                            .get(2)
6522                            .and_then(|e: &Vec<String>| e.first())
6523                            .is_some_and(|v: &String| v == "Z21")
6524                });
6525                if has_cci {
6526                    let cavs =
6527                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6528                    if cavs.iter().any(|s| {
6529                        s.elements
6530                            .first()
6531                            .and_then(|e: &Vec<String>| e.first())
6532                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6533                    }) {
6534                        return ConditionResult::True;
6535                    }
6536                }
6537            }
6538        }
6539        ConditionResult::False
6540    }
6541
6542    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6543    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6544        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6545        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6546        let found = cci_segs.iter().any(|s| {
6547            s.elements
6548                .first()
6549                .and_then(|e| e.first())
6550                .is_some_and(|v| v == "Z37")
6551                && s.elements
6552                    .get(2)
6553                    .and_then(|e| e.first())
6554                    .is_some_and(|v| v == "ZD1")
6555        });
6556        ConditionResult::from(found)
6557    }
6558
6559    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6560    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6561        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6562        let seq_count = ctx
6563            .find_segments("SEQ")
6564            .iter()
6565            .filter(|s| {
6566                s.elements
6567                    .first()
6568                    .and_then(|e| e.first())
6569                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6570            })
6571            .count();
6572        ConditionResult::from(seq_count > 1)
6573    }
6574
6575    /// [387] Wenn in derselben SG8 (OBIS Daten der Marktlokation) eine OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordneter Zählzeit handelt (Tarif: e...
6576    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6577        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6578        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6579        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6580        let found = pia_segs.iter().any(|s| {
6581            s.elements
6582                .first()
6583                .and_then(|e| e.first())
6584                .is_some_and(|v| v == "5")
6585                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6586                    v.starts_with("1-")
6587                        && (v.contains(":1.8.")
6588                            || v.contains(":2.8.")
6589                            || v.contains(":1.9.")
6590                            || v.contains(":2.9."))
6591                })
6592        });
6593        ConditionResult::from(found)
6594    }
6595
6596    /// [388] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") anzugeben, wenn in diesem Zeitraum Blindarbeit bzw. -leis...
6597    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6598        // Check: at least one RFF+Z49 (Zeitraum) exists
6599        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6600        ConditionResult::from(found)
6601    }
6602
6603    /// [391] Wenn in derselben SG8 SEQ+Z20 (OBIS Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl für Zählerstand im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordn...
6604    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6605        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6606        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20,
6607        // with a tariff e = 1..62 (#351: 1-0:1.8.0 has no Zählzeit).
6608        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6609        let found = pia_segs.iter().any(|s| {
6610            s.elements
6611                .first()
6612                .and_then(|e| e.first())
6613                .is_some_and(|v| v == "5")
6614                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6615                    v.starts_with("1-")
6616                        && (v.contains(":1.8.") || v.contains(":2.8."))
6617                        && obis_tariff(v).is_some_and(|e| (1..=62).contains(&e))
6618                })
6619        });
6620        ConditionResult::from(found)
6621    }
6622
6623    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6624    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6625        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6626        let pia_segs = ctx.find_segments("PIA");
6627        let found = pia_segs.iter().any(|s| {
6628            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6629                let parts: Vec<&str> = v.split('-').collect();
6630                parts.len() == 3
6631                    && parts[0].len() == 1
6632                    && parts[0].chars().all(|c| c.is_ascii_digit())
6633                    && parts[1].len() == 2
6634                    && parts[1].chars().all(|c| c.is_ascii_digit())
6635                    && parts[2].len() == 1
6636                    && parts[2].chars().all(|c| c.is_ascii_digit())
6637            })
6638        });
6639        ConditionResult::from(found)
6640    }
6641
6642    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6643    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6644        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6645        let pia_segs = ctx.find_segments("PIA");
6646        let found = pia_segs.iter().any(|s| {
6647            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6648                let parts: Vec<&str> = v.split('-').collect();
6649                parts.len() == 4
6650                    && parts[0].len() == 1
6651                    && parts[0].chars().all(|c| c.is_ascii_digit())
6652                    && parts[1].len() == 2
6653                    && parts[1].chars().all(|c| c.is_ascii_digit())
6654                    && parts[2].len() == 1
6655                    && parts[2].chars().all(|c| c.is_ascii_digit())
6656                    && parts[3].len() == 3
6657                    && parts[3].chars().all(|c| c.is_ascii_digit())
6658            })
6659        });
6660        ConditionResult::from(found)
6661    }
6662
6663    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6664    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6665        // Check: PIA code in format n1-n2-n1-n8-n2
6666        let pia_segs = ctx.find_segments("PIA");
6667        let found = pia_segs.iter().any(|s| {
6668            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6669                let parts: Vec<&str> = v.split('-').collect();
6670                parts.len() == 5
6671                    && parts[0].len() == 1
6672                    && parts[0].chars().all(|c| c.is_ascii_digit())
6673                    && parts[1].len() == 2
6674                    && parts[1].chars().all(|c| c.is_ascii_digit())
6675                    && parts[2].len() == 1
6676                    && parts[2].chars().all(|c| c.is_ascii_digit())
6677                    && parts[3].len() == 8
6678                    && parts[3].chars().all(|c| c.is_ascii_digit())
6679                    && parts[4].len() == 2
6680                    && parts[4].chars().all(|c| c.is_ascii_digit())
6681            })
6682        });
6683        ConditionResult::from(found)
6684    }
6685
6686    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6687    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6688        // Check: PIA code in format n1-n2-n1-n8
6689        let pia_segs = ctx.find_segments("PIA");
6690        let found = pia_segs.iter().any(|s| {
6691            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6692                let parts: Vec<&str> = v.split('-').collect();
6693                parts.len() == 4
6694                    && parts[0].len() == 1
6695                    && parts[0].chars().all(|c| c.is_ascii_digit())
6696                    && parts[1].len() == 2
6697                    && parts[1].chars().all(|c| c.is_ascii_digit())
6698                    && parts[2].len() == 1
6699                    && parts[2].chars().all(|c| c.is_ascii_digit())
6700                    && parts[3].len() == 8
6701                    && parts[3].chars().all(|c| c.is_ascii_digit())
6702            })
6703        });
6704        ConditionResult::from(found)
6705    }
6706
6707    /// [396] Wenn in derselben SG8 SEQ+Z02/ ZA1/ ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
6708    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6709        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6710        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6711        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6712        let found = pia_segs.iter().any(|s| {
6713            s.elements
6714                .first()
6715                .and_then(|e| e.first())
6716                .is_some_and(|v| v == "5")
6717                && s.elements
6718                    .get(1)
6719                    .and_then(|e| e.first())
6720                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6721        });
6722        ConditionResult::from(found)
6723    }
6724
6725    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6726    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6727        let pia_segs = ctx.find_segments("PIA");
6728        let found = pia_segs.iter().any(|s| {
6729            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6730                let prefixes = ["1-08-1", "1-08-4"];
6731                prefixes.iter().any(|p| v.starts_with(p))
6732            })
6733        });
6734        ConditionResult::from(found)
6735    }
6736
6737    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6738    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6739        let pia_segs = ctx.find_segments("PIA");
6740        let found = pia_segs.iter().any(|s| {
6741            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6742                let prefixes = ["1-08-2", "1-08-5"];
6743                prefixes.iter().any(|p| v.starts_with(p))
6744            })
6745        });
6746        ConditionResult::from(found)
6747    }
6748
6749    /// [399] Wenn im SG8 SEQ+Z78 mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments, das RFF+Z31 (Lokationsbündelstruktur) im DE1153 der Code Z31 (Lokationsbündelstruktur) vorhanden, und im D...
6750    // REVIEW: Condition is True when an SG8 SEQ+Z78 exists whose Zeitraum-ID (DE1050) matches DE3224 of the current LOC segment (accessed via SG5 group navigator as LOC.C517.DE3224 = elements[1][3]), AND that Z78 SG8 has RFF+Z31 with a DE1154 value that is NOT the pauschal code 9992000000018. This identifies bundle structures that reference a non-pauschal Lokationsbündelstruktur. (medium confidence)
6751    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6752        let nav = match ctx.navigator() {
6753            Some(n) => n,
6754            None => return ConditionResult::Unknown,
6755        };
6756        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6757        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6758        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6759        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6760        for i in 0..sg5_count {
6761            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6762            for loc in &locs {
6763                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6764                    if !zid.is_empty() {
6765                        loc_zeitraum_ids.push(zid.clone());
6766                    }
6767                }
6768            }
6769        }
6770        if loc_zeitraum_ids.is_empty() {
6771            return ConditionResult::Unknown;
6772        }
6773        let pauschal_id = "9992000000018";
6774        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6775        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6776        // and that has RFF+Z31 with DE1154 != pauschal_id
6777        for i in 0..sg8_count {
6778            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6779            for seq in &seqs {
6780                if !seq
6781                    .elements
6782                    .first()
6783                    .and_then(|e| e.first())
6784                    .is_some_and(|v| v == "Z78")
6785                {
6786                    continue;
6787                }
6788                let zid = seq
6789                    .elements
6790                    .get(1)
6791                    .and_then(|e| e.first())
6792                    .map(|s| s.as_str())
6793                    .unwrap_or("");
6794                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6795                    continue;
6796                }
6797                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6798                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6799                for rff in &rffs {
6800                    let qual = rff
6801                        .elements
6802                        .first()
6803                        .and_then(|e| e.first())
6804                        .map(|v| v.as_str())
6805                        .unwrap_or("");
6806                    if qual == "Z31" {
6807                        let id = rff
6808                            .elements
6809                            .first()
6810                            .and_then(|e| e.get(1))
6811                            .map(|v| v.as_str())
6812                            .unwrap_or("");
6813                        let normalized = id.replace(' ', "");
6814                        if !normalized.is_empty() && normalized != pauschal_id {
6815                            return ConditionResult::True;
6816                        }
6817                    }
6818                }
6819            }
6820        }
6821        ConditionResult::False
6822    }
6823
6824    /// [401] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) mit der Zeitraum ID \"1\" im DE1156 ist, muss das Datum dem DTM+...
6825    // HAND-EDITED: as [134], for Z49/Z53. The generated code was true whenever
6826    // the message had no RFF+Z49/Z53 at all, false on every Z49 SG6.
6827    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6828        continues_previous_period(ctx, &["Z49", "Z53"])
6829    }
6830
6831    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6832    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6833        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6834        // Check: OBIS Zählerstand code in PIA+5
6835        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6836        let found = pia_segs.iter().any(|s| {
6837            s.elements
6838                .first()
6839                .and_then(|e| e.first())
6840                .is_some_and(|v| v == "5")
6841                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6842                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6843                })
6844        });
6845        ConditionResult::from(found)
6846    }
6847
6848    /// [403] Wenn nicht dieselbe MP-ID in SG2 NAD+MS (Nachrichtensender) und im SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6849    // REVIEW: Gets NAD+MS MP-ID from SG2 (elements[1][0]), then navigates all SG8→SG10 instances looking for CCI with elements[2][0]='ZB3' (Zugeordnete Marktpartner) and CAV with elements[0][0]='Z91' (Messstellenbetreiber). Compares the CAV+Z91 MP-ID (elements[0][1]) with the sender MP-ID. Returns True when they differ (not the same). Note: SEQ+Z01 filter on the parent SG8 is omitted because `find_segments_in_child_group` only accesses child groups, not the parent SG8 segments; the Z91 MSB identity is message-consistent regardless. (medium confidence)
6850    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
6851        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
6852            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
6853                Some(id) if !id.is_empty() => id.clone(),
6854                _ => return ConditionResult::Unknown,
6855            },
6856            None => return ConditionResult::Unknown,
6857        };
6858        let nav = match ctx.navigator {
6859            Some(n) => n,
6860            None => return ConditionResult::Unknown,
6861        };
6862        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6863        for i in 0..sg8_count {
6864            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6865            for j in 0..sg10_count {
6866                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6867                let has_cci_zb3 = ccis.iter().any(|s| {
6868                    s.elements
6869                        .get(2)
6870                        .and_then(|e: &Vec<String>| e.first())
6871                        .is_some_and(|v: &String| v == "ZB3")
6872                });
6873                if !has_cci_zb3 {
6874                    continue;
6875                }
6876                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6877                for cav in &cavs {
6878                    if cav
6879                        .elements
6880                        .first()
6881                        .and_then(|e: &Vec<String>| e.first())
6882                        .is_some_and(|v: &String| v == "Z91")
6883                    {
6884                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
6885                            if !msb_id.is_empty() {
6886                                return ConditionResult::from(
6887                                    sender_mp_id.as_str() != msb_id.as_str(),
6888                                );
6889                            }
6890                        }
6891                    }
6892                }
6893            }
6894        }
6895        ConditionResult::Unknown
6896    }
6897
6898    /// [404] Wenn nicht dieselbe MP-ID in SG2 NAD+MR (Nachrichtenempfänger) und im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6899    // REVIEW: Reads NAD+MR MP-ID from elements[1][0], then iterates all SG8 SG10 children looking for CCI with elements[2][0]==ZB3 and CAV with elements[0][0]==Z91 where the MP-ID (elements[0][1]) matches. Returns False if same MP-ID found (condition 'not same' fails), True otherwise. Approximation: cannot filter SG8 instances by SEQ+Z01/Z98 without find_segments_in_group; checks all SG8 SG10 children. This is acceptable since CCI+ZB3 CAV+Z91 patterns in other SG8 types would still represent valid MSB assignments to compare against. (medium confidence)
6900    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
6901        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
6902        let mr_mp_id = match mr_nads.first() {
6903            Some(nad) => nad
6904                .elements
6905                .get(1)
6906                .and_then(|e: &Vec<String>| e.first())
6907                .cloned()
6908                .unwrap_or_default(),
6909            None => return ConditionResult::Unknown,
6910        };
6911        if mr_mp_id.is_empty() {
6912            return ConditionResult::Unknown;
6913        }
6914        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
6915        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
6916        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
6917        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
6918        // otherwise, as before.
6919        if let Some(scope) = ctx.enclosing_scope("SG8") {
6920            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
6921            let names_recipient = |s: &[OwnedSegment]| {
6922                sg4_instances::has_cci(s, "", "ZB3")
6923                    && s.iter().any(|c| {
6924                        c.id == "CAV"
6925                            && c.get_element(0) == "Z91"
6926                            && c.get_component(0, 1) == mr_mp_id
6927                    })
6928            };
6929            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
6930                sg4_instances::seq_at(ctx, p)
6931                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
6932                    && (refs.is_empty()
6933                        || sg4_instances::rff_values_at(ctx, p, "Z18")
6934                            .iter()
6935                            .any(|v| refs.contains(v)))
6936                    && sg4_instances::sg10_at(ctx, p, names_recipient)
6937            });
6938            return ConditionResult::from(!same);
6939        }
6940
6941        let nav = match ctx.navigator {
6942            Some(n) => n,
6943            None => {
6944                let cavs = ctx.find_segments("CAV");
6945                let same_id_exists = cavs.iter().any(|s| {
6946                    s.elements
6947                        .first()
6948                        .and_then(|e: &Vec<String>| e.first())
6949                        .is_some_and(|v: &String| v == "Z91")
6950                        && s.elements
6951                            .first()
6952                            .and_then(|e| e.get(1))
6953                            .is_some_and(|v: &String| v == &mr_mp_id)
6954                });
6955                return ConditionResult::from(!same_id_exists);
6956            }
6957        };
6958
6959        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6960        for i in 0..sg8_count {
6961            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6962            for j in 0..sg10_count {
6963                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6964                let has_cci_zb3 = ccis.iter().any(|s| {
6965                    s.elements
6966                        .get(2)
6967                        .and_then(|e: &Vec<String>| e.first())
6968                        .is_some_and(|v: &String| v == "ZB3")
6969                });
6970                if !has_cci_zb3 {
6971                    continue;
6972                }
6973                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6974                for cav in &cavs {
6975                    if cav
6976                        .elements
6977                        .first()
6978                        .and_then(|e: &Vec<String>| e.first())
6979                        .is_some_and(|v: &String| v == "Z91")
6980                    {
6981                        let cav_mp_id = cav
6982                            .elements
6983                            .first()
6984                            .and_then(|e| e.get(1))
6985                            .map(|s| s.as_str())
6986                            .unwrap_or("");
6987                        if cav_mp_id == mr_mp_id.as_str() {
6988                            return ConditionResult::False;
6989                        }
6990                    }
6991                }
6992            }
6993        }
6994
6995        ConditionResult::True
6996    }
6997
6998    /// [405] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
6999    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7000        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7001        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7002        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7003        let found = pia_segs.iter().any(|s| {
7004            s.elements
7005                .first()
7006                .and_then(|e| e.first())
7007                .is_some_and(|v| v == "5")
7008                && s.elements
7009                    .get(1)
7010                    .and_then(|e| e.first())
7011                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7012        });
7013        ConditionResult::from(found)
7014    }
7015
7016    /// [406] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und höchste 1/4 Stunde im Monat im PIA+5 vorhanden
7017    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7018        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7019        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7020        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7021        let found = pia_segs.iter().any(|s| {
7022            s.elements
7023                .first()
7024                .and_then(|e| e.first())
7025                .is_some_and(|v| v == "5")
7026                && s.elements
7027                    .get(1)
7028                    .and_then(|e| e.first())
7029                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7030        });
7031        ConditionResult::from(found)
7032    }
7033
7034    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7035    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7036        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7037        // Check: OBIS Blindarbeit code in PIA+5
7038        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7039        let found = pia_segs.iter().any(|s| {
7040            s.elements
7041                .first()
7042                .and_then(|e| e.first())
7043                .is_some_and(|v| v == "5")
7044                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7045                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7046                })
7047        });
7048        ConditionResult::from(found)
7049    }
7050
7051    /// [408] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
7052    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7053        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7054        // reaches an SG8; the message as before otherwise.
7055        if let Some(scope) = ctx.enclosing_scope("SG8") {
7056            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7057                ctx,
7058                scope,
7059                is_obis_wirkarbeit_kumuliert,
7060            ));
7061        }
7062        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7063        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7064        ConditionResult::from(has_seq_z02)
7065    }
7066
7067    /// [409] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
7068    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7069        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7070        // reaches an SG8; the message as before otherwise.
7071        if let Some(scope) = ctx.enclosing_scope("SG8") {
7072            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7073                ctx,
7074                scope,
7075                is_obis_wirkarbeit_kumuliert,
7076            ));
7077        }
7078        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7079        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7080        ConditionResult::from(!has_seq_z02)
7081    }
7082
7083    /// [410] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
7084    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7085        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7086        // reaches an SG8; the message as before otherwise.
7087        if let Some(scope) = ctx.enclosing_scope("SG8") {
7088            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7089                ctx,
7090                scope,
7091                is_obis_wirkarbeit_quarter_hour,
7092            ));
7093        }
7094        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7095        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7096        ConditionResult::from(has_seq_z02)
7097    }
7098
7099    /// [411] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
7100    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7101        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7102        // reaches an SG8; the message as before otherwise.
7103        if let Some(scope) = ctx.enclosing_scope("SG8") {
7104            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7105                ctx,
7106                scope,
7107                is_obis_wirkarbeit_quarter_hour,
7108            ));
7109        }
7110        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7111        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7112        ConditionResult::from(!has_seq_z02)
7113    }
7114
7115    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7116    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7117        ctx.external.evaluate("code_list_membership_check")
7118    }
7119
7120    /// [413] Wenn eine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
7121    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7122        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7123        let found = ctx.find_segments("SEQ").iter().any(|s| {
7124            s.elements
7125                .first()
7126                .and_then(|e| e.first())
7127                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7128        });
7129        ConditionResult::from(found)
7130    }
7131
7132    /// [414] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
7133    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7134        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7135        let found = ctx.find_segments("SEQ").iter().any(|s| {
7136            s.elements
7137                .first()
7138                .and_then(|e| e.first())
7139                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7140        });
7141        ConditionResult::from(!found)
7142    }
7143
7144    /// [415] Wenn eine weitere SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
7145    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7146        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7147        let count = ctx
7148            .find_segments("SEQ")
7149            .iter()
7150            .filter(|s| {
7151                s.elements
7152                    .first()
7153                    .and_then(|e| e.first())
7154                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7155            })
7156            .count();
7157        ConditionResult::from(count > 1)
7158    }
7159
7160    /// [416] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
7161    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7162        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7163        let count = ctx
7164            .find_segments("SEQ")
7165            .iter()
7166            .filter(|s| {
7167                s.elements
7168                    .first()
7169                    .and_then(|e| e.first())
7170                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7171            })
7172            .count();
7173        ConditionResult::from(count <= 1)
7174    }
7175
7176    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7177    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7178        // TODO: implement
7179        ConditionResult::Unknown
7180    }
7181
7182    /// [419] Wenn in diesem Datenelement kein anderes Paket in dieser SG10 in derselben SG8 SEQ zur Möglichkeit der Angabe von mindestens einem anderen Code führt
7183    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7184        // TODO: implement
7185        ConditionResult::Unknown
7186    }
7187
7188    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7189    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7190        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7191        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7192        let found = rff_segs.iter().any(|s| {
7193            s.elements
7194                .first()
7195                .and_then(|e| e.first())
7196                .is_some_and(|v| v == "Z14")
7197        });
7198        ConditionResult::from(found)
7199    }
7200
7201    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7202    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7203        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7204        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7205        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7206    }
7207
7208    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7209    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7210        ctx.external.evaluate("code_list_membership_check")
7211    }
7212
7213    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7214    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7215        ctx.external.evaluate("code_list_membership_check")
7216    }
7217
7218    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7219    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7220        ctx.external.evaluate("code_list_membership_check")
7221    }
7222
7223    /// [428] Wenn in derselben SG8 SEQ+Z27/ ZE2 (Erforderliches Messprodukt der Marktlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.1.1 Standard-Messprodukt der Mar...
7224    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7225        ctx.external.evaluate("code_list_membership_check")
7226    }
7227
7228    /// [429] Wenn in derselben SG8 SEQ+Z19/ ZF4 (Erforderliches Messprodukt der Messlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.3.1 Standard-Messprodukt der Mess...
7229    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7230        ctx.external.evaluate("code_list_membership_check")
7231    }
7232
7233    /// [430] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+9...
7234    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7235        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7236        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7237        ConditionResult::from(seq_z27_count > 1)
7238    }
7239
7240    /// [431] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+...
7241    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7242        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7243        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7244        ConditionResult::from(seq_z27_count <= 1)
7245    }
7246
7247    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7248    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7249        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7250        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7251        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7252        let found = pia_segs.iter().any(|s| {
7253            s.elements
7254                .first()
7255                .and_then(|e| e.first())
7256                .is_some_and(|v| v == "5")
7257                && s.elements
7258                    .get(1)
7259                    .and_then(|e| e.first())
7260                    .is_some_and(|v| v == "9991000000078")
7261        });
7262        ConditionResult::from(found)
7263    }
7264
7265    /// [433] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 ...
7266    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7267        ctx.external.evaluate("code_list_membership_check")
7268    }
7269
7270    /// [435] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 D...
7271    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7272        ctx.external.evaluate("code_list_membership_check")
7273    }
7274
7275    /// [436] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000086:Z11 (Wirkarbeit höchste 1/4 Stunde im Monat) vorhanden ist
7276    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7277        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7278        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7279        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7280        let found = pia_segs.iter().any(|s| {
7281            s.elements
7282                .first()
7283                .and_then(|e| e.first())
7284                .is_some_and(|v| v == "5")
7285                && s.elements
7286                    .get(1)
7287                    .and_then(|e| e.first())
7288                    .is_some_and(|v| v == "9991000000086")
7289        });
7290        ConditionResult::from(found)
7291    }
7292
7293    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7294    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7295        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7296        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7297        let found = sts_segs.iter().any(|s| {
7298            s.elements
7299                .first()
7300                .and_then(|e| e.first())
7301                .is_some_and(|v| v == "E01")
7302                && s.elements
7303                    .get(2)
7304                    .and_then(|e| e.first())
7305                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7306        });
7307        ConditionResult::from(found)
7308    }
7309
7310    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7311    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7312        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7313        // in tree validation), else the enclosing SG10's CCI.
7314        let is_z39 = |elements: &[Vec<String>]| {
7315            elements
7316                .first()
7317                .and_then(|e| e.first())
7318                .is_some_and(|v| v == "Z39")
7319        };
7320        if let Some(own) = ctx.resolved_segment.filter(|_| {
7321            ctx.scoped_find_segments_in("SG10", "CCI")
7322                .iter()
7323                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7324        }) {
7325            return ConditionResult::from(is_z39(own));
7326        }
7327        ConditionResult::from(
7328            ctx.scoped_find_segments_in("SG10", "CCI")
7329                .iter()
7330                .any(|s| is_z39(&s.elements)),
7331        )
7332    }
7333
7334    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7335    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7336        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7337        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7338        // alternative of the `⊻` held and the XOR failed on each RFF).
7339        same_rff_qualifier(ctx, "Z35")
7340    }
7341
7342    /// [441] Wenn im SG8+SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) für die in diesem RFF DE1154 genannte Gerätenummer eines Zählers das RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden ist
7343    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7344        // HAND-EDITED (#379): the Zähleinrichtungsdaten of the meter this RFF
7345        // names — the SEQ+Z03/ZF5 whose SG10 CAV+Z30 carries its number in
7346        // DE7110 — not any SEQ+Z03 of the message. No such SG8: no RFF+Z14
7347        // for that meter either.
7348        let Some(number) = rff_reference(ctx) else {
7349            return ConditionResult::Unknown;
7350        };
7351        let Some(blocks) = sg8_blocks(ctx) else {
7352            return ConditionResult::Unknown;
7353        };
7354        let meter = blocks
7355            .iter()
7356            .filter(|b| b.seq == "Z03" || b.seq == "ZF5")
7357            .find(|b| {
7358                b.sg10.iter().any(|(_, cavs)| {
7359                    cavs.iter().any(|c| {
7360                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
7361                    })
7362                })
7363            });
7364        ConditionResult::from(meter.map_or(true, |b| {
7365            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
7366        }))
7367    }
7368
7369    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7370    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7371        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7372        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7373        // alternative of the `⊻` held and the XOR failed on each RFF).
7374        same_rff_qualifier(ctx, "Z34")
7375    }
7376
7377    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7378    // REVIEW: Checks whether any SG8 instance carrying a 'Produkt-Daten der Netzlokation' SEQ qualifier (Z60, ZE0, ZG8, ZG9) lacks PIA+5. Iterates each qualifier separately using any_group_has_qualifier_without which checks: SEQ+code present AND PIA+5 absent in same SG8. Returns True on first match. Correctly handles the multi-qualifier case via OR iteration. (medium confidence)
7379    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7380        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7381        // scope reaches one; the message as before otherwise.
7382        if let Some(scope) = ctx.enclosing_scope("SG8") {
7383            return ConditionResult::from(
7384                ctx.find_segments_at(scope, "SEQ")
7385                    .iter()
7386                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7387                    && !ctx
7388                        .find_segments_at(scope, "PIA")
7389                        .iter()
7390                        .any(|s| s.get_element(0) == "5"),
7391            );
7392        }
7393        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7394            if matches!(
7395                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7396                ConditionResult::True
7397            ) {
7398                return ConditionResult::True;
7399            }
7400        }
7401        ConditionResult::False
7402    }
7403
7404    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7405    // REVIEW: Iterates all SG8 instances and checks whether any SG10 child group contains CCI with elements[0][0]=="11" (Details zum Produkt der Netzlokation). Returns True when an SG8 is found that lacks such a CCI+11 in its SG10 children. Navigator fallback uses filtered_parent_child_has_qualifier negated per qualifier. Approximation: the navigator path does not filter SG8 instances by Produkt-Daten-Netzlokation SEQ qualifier due to absence of find_segments_in_group; checks all SG8 instances instead. (medium confidence)
7406    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7407        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7408        // scope reaches one; the message as before otherwise.
7409        if let Some(scope) = ctx.enclosing_scope("SG8") {
7410            return ConditionResult::from(
7411                ctx.find_segments_at(scope, "SEQ")
7412                    .iter()
7413                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7414                    && !ctx
7415                        .find_segments_at(scope, "CCI")
7416                        .iter()
7417                        .any(|s| s.get_element(0) == "11"),
7418            );
7419        }
7420        let nav = match ctx.navigator {
7421            Some(n) => n,
7422            None => {
7423                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7424                    matches!(
7425                        ctx.filtered_parent_child_has_qualifier(
7426                            &["SG4", "SG8"],
7427                            "SEQ",
7428                            0,
7429                            code,
7430                            "SG10",
7431                            "CCI",
7432                            0,
7433                            "11",
7434                        ),
7435                        ConditionResult::True
7436                    )
7437                });
7438                return ConditionResult::from(!has_cci11);
7439            }
7440        };
7441
7442        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7443        for i in 0..sg8_count {
7444            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7445            let has_cci11 = (0..sg10_count).any(|j| {
7446                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7447                ccis.iter().any(|s| {
7448                    s.elements
7449                        .first()
7450                        .and_then(|e: &Vec<String>| e.first())
7451                        .is_some_and(|v: &String| v == "11")
7452                })
7453            });
7454            if !has_cci11 {
7455                return ConditionResult::True;
7456            }
7457        }
7458        ConditionResult::False
7459    }
7460
7461    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7462    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7463        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7464        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7465        let found = cci_segs.iter().any(|s| {
7466            s.elements
7467                .first()
7468                .and_then(|e| e.first())
7469                .is_some_and(|v| v == "Z17")
7470        });
7471        ConditionResult::from(found)
7472    }
7473
7474    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7475    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7476        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7477        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7478        let found = cci_segs.iter().any(|s| {
7479            s.elements
7480                .first()
7481                .and_then(|e| e.first())
7482                .is_some_and(|v| v == "Z50")
7483        });
7484        ConditionResult::from(found)
7485    }
7486
7487    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7488    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7489        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7490        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7491        let found = cci_segs.iter().any(|s| {
7492            s.elements
7493                .first()
7494                .and_then(|e| e.first())
7495                .is_some_and(|v| v == "Z56")
7496        });
7497        ConditionResult::from(found)
7498    }
7499
7500    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7501    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7502        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7503        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7504        let has_cci = cci_segs.iter().any(|s| {
7505            s.elements
7506                .first()
7507                .and_then(|e| e.first())
7508                .is_some_and(|v| v == "6")
7509                && s.elements
7510                    .get(2)
7511                    .and_then(|e| e.first())
7512                    .is_some_and(|v| v == "ZA9")
7513        });
7514        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7515        let has_cav = cav_segs.iter().any(|s| {
7516            s.elements
7517                .first()
7518                .and_then(|e| e.first())
7519                .is_some_and(|v| v == "ZG3")
7520        });
7521        ConditionResult::from(has_cci && has_cav)
7522    }
7523
7524    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7525    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7526        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7527        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7528        // alternative of the `⊻` held and the XOR failed on each RFF).
7529        same_rff_qualifier(ctx, "Z32")
7530    }
7531
7532    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7533    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7534        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7535        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7536        // alternative of the `⊻` held and the XOR failed on each RFF).
7537        same_rff_qualifier(ctx, "Z18")
7538    }
7539
7540    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7541    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7542        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7543        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7544        // alternative of the `⊻` held and the XOR failed on each RFF).
7545        same_rff_qualifier(ctx, "Z19")
7546    }
7547
7548    /// [453] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Auslöser „Bei Schwellwertunter-/ -überschreitung" mit selbständiger Änderungsmöglichke...
7549    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7550        ctx.external.evaluate("code_list_membership_check")
7551    }
7552
7553    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7554    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7555        // Check: begin date < end date (structural — always true for valid messages)
7556        ConditionResult::True
7557    }
7558
7559    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7560    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7561        // Check: end date > begin date (structural — always true for valid messages)
7562        ConditionResult::True
7563    }
7564
7565    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7566    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7567        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7568        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7569        // alternative of the `⊻` held and the XOR failed on each RFF).
7570        same_rff_qualifier(ctx, "Z37")
7571    }
7572
7573    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7574    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7575        // Check: PIA+5 with Messprodukt 9991000001498 exists
7576        let pia_segs = ctx.find_segments("PIA");
7577        let found = pia_segs.iter().any(|s| {
7578            s.elements
7579                .first()
7580                .and_then(|e| e.first())
7581                .is_some_and(|v| v == "5")
7582                && s.elements
7583                    .get(1)
7584                    .and_then(|e| e.first())
7585                    .is_some_and(|v| v == "9991000001498")
7586        });
7587        ConditionResult::from(found)
7588    }
7589
7590    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7591    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7592        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7593        let pia_segs = ctx.find_segments("PIA");
7594        let found = pia_segs.iter().any(|s| {
7595            s.elements
7596                .first()
7597                .and_then(|e| e.first())
7598                .is_some_and(|v| v == "5")
7599                && s.elements
7600                    .get(1)
7601                    .and_then(|e| e.first())
7602                    .is_some_and(|v| v == "9991000000739")
7603        });
7604        ConditionResult::from(found)
7605    }
7606
7607    /// [459] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"CLS-Direkt aus dem SMGW\" bestellt wurde
7608    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7609        ctx.external.evaluate("code_list_membership_check")
7610    }
7611
7612    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7613    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7614        ctx.external.evaluate("code_list_membership_check")
7615    }
7616
7617    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7618    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7619        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7620        // SG10, when the scope reaches one; of any one SG10 otherwise.
7621        this_sg10_has(ctx, |segs| {
7622            segs.iter()
7623                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7624                && sg4_instances::has_cav(segs, "ZG8")
7625        })
7626    }
7627
7628    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7629    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7630        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7631        // SG10, when the scope reaches one; of any one SG10 otherwise.
7632        this_sg10_has(ctx, |segs| {
7633            segs.iter()
7634                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7635                && sg4_instances::has_cav(segs, "ZH1")
7636        })
7637    }
7638
7639    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7640    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7641        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7642        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7643        let found = cci_segs.iter().any(|s| {
7644            s.elements
7645                .first()
7646                .and_then(|e| e.first())
7647                .is_some_and(|v| v == "Z61")
7648                && s.elements
7649                    .get(2)
7650                    .and_then(|e| e.first())
7651                    .is_some_and(|v| v == "ZF9")
7652        });
7653        ConditionResult::from(found)
7654    }
7655
7656    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7657    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7658        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7659        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7660        let found = rff_segs.iter().any(|s| {
7661            s.elements
7662                .first()
7663                .and_then(|e| e.first())
7664                .is_some_and(|v| v == "Z38")
7665        });
7666        ConditionResult::from(found)
7667    }
7668
7669    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7670    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7671        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7672        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7673        let found = rff_segs.iter().any(|s| {
7674            s.elements
7675                .first()
7676                .and_then(|e| e.first())
7677                .is_some_and(|v| v == "Z32")
7678        });
7679        ConditionResult::from(found)
7680    }
7681
7682    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7683    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7684        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7685        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7686        let found = segs.iter().any(|s| {
7687            s.elements
7688                .first()
7689                .and_then(|e| e.first())
7690                .is_some_and(|v| v == "ZG8")
7691        });
7692        ConditionResult::from(found)
7693    }
7694
7695    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7696    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7697        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7698        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7699        let found = segs.iter().any(|s| {
7700            s.elements
7701                .first()
7702                .and_then(|e| e.first())
7703                .is_some_and(|v| v == "ZH0")
7704        });
7705        ConditionResult::from(found)
7706    }
7707
7708    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7709    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7710        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7711        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7712        let found = segs.iter().any(|s| {
7713            s.elements
7714                .first()
7715                .and_then(|e| e.first())
7716                .is_some_and(|v| v == "ZH1")
7717        });
7718        ConditionResult::from(found)
7719    }
7720
7721    /// [470] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZH5 (Wechsel in das in das § 14a EnWG-Modell gem. Festlegung BK6-22-300 wurde durchgeführt) vorhanden ist
7722    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7723        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7724        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7725        let found = segs.iter().any(|s| {
7726            s.elements
7727                .first()
7728                .and_then(|e| e.first())
7729                .is_some_and(|v| v == "ZH5")
7730        });
7731        ConditionResult::from(found)
7732    }
7733
7734    /// [471] Wenn im selben SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6 RFF+Z49/ ...
7735    // HAND-EDITED: judges this SG6 against the other Z49/Z53 SG6 of its
7736    // SG4. The generated code was true message-wide once any Zeitraum-ID
7737    // exceeded 1, so the last, open Zeitraum had to carry DTM+Z26 (#476).
7738    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7739        periods::followed_by_later_period(ctx, "SG4", &["Z49", "Z53"])
7740    }
7741
7742    /// [472] Wenn im selben SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6...
7743    // HAND-EDITED: judges this SG6 against the other Z48/Z55 SG6 of its
7744    // SG4. The generated code was true message-wide once any Zeitraum-ID
7745    // exceeded 1, so the last, open Zeitraum had to carry DTM+Z26 (#476).
7746    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7747        periods::followed_by_later_period(ctx, "SG4", &["Z48", "Z55"])
7748    }
7749
7750    /// [473] Wenn im selben SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist al...
7751    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7752        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7753        let found = ctx.find_segments("RFF").iter().any(|s| {
7754            let qual = s
7755                .elements
7756                .first()
7757                .and_then(|e| e.first())
7758                .map(|v| v.as_str());
7759            matches!(qual, Some("Z47") | Some("Z54"))
7760                && s.elements
7761                    .first()
7762                    .and_then(|e| e.get(2))
7763                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7764        });
7765        ConditionResult::from(found)
7766    }
7767
7768    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7769    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7770        let sts_segs = ctx.find_segments("STS");
7771        let found = sts_segs.iter().any(|s| {
7772            s.elements
7773                .first()
7774                .and_then(|e| e.first())
7775                .is_some_and(|v| v == "7")
7776                && s.elements
7777                    .get(2)
7778                    .and_then(|e| e.first())
7779                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7780        });
7781        ConditionResult::from(found)
7782    }
7783
7784    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7785    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7786        let sts_segs = ctx.find_segments("STS");
7787        let found = sts_segs.iter().any(|s| {
7788            s.elements
7789                .first()
7790                .and_then(|e| e.first())
7791                .is_some_and(|v| v == "7")
7792                && s.elements
7793                    .get(2)
7794                    .and_then(|e| e.first())
7795                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7796        });
7797        ConditionResult::from(found)
7798    }
7799
7800    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7801    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7802        let sts_segs = ctx.find_segments("STS");
7803        let found = sts_segs.iter().any(|s| {
7804            s.elements
7805                .first()
7806                .and_then(|e| e.first())
7807                .is_some_and(|v| v == "7")
7808                && s.elements
7809                    .get(3)
7810                    .and_then(|e| e.first())
7811                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7812        });
7813        ConditionResult::from(found)
7814    }
7815
7816    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7817    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7818        let sts_segs = ctx.find_segments("STS");
7819        let found = sts_segs.iter().any(|s| {
7820            s.elements
7821                .first()
7822                .and_then(|e| e.first())
7823                .is_some_and(|v| v == "7")
7824                && s.elements
7825                    .get(3)
7826                    .and_then(|e| e.first())
7827                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7828        });
7829        ConditionResult::from(found)
7830    }
7831
7832    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
7833    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
7834        let sts_segs = ctx.find_segments("STS");
7835        let found = sts_segs.iter().any(|s| {
7836            s.elements
7837                .first()
7838                .and_then(|e| e.first())
7839                .is_some_and(|v| v == "7")
7840                && s.elements
7841                    .get(3)
7842                    .and_then(|e| e.first())
7843                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
7844        });
7845        ConditionResult::from(found)
7846    }
7847
7848    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
7849    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
7850        let sts_segs = ctx.find_segments("STS");
7851        let found = sts_segs.iter().any(|s| {
7852            s.elements
7853                .first()
7854                .and_then(|e| e.first())
7855                .is_some_and(|v| v == "7")
7856                && s.elements
7857                    .get(3)
7858                    .and_then(|e| e.first())
7859                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
7860        });
7861        ConditionResult::from(found)
7862    }
7863
7864    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
7865    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
7866        let sts_segs = ctx.find_segments("STS");
7867        let found = sts_segs.iter().any(|s| {
7868            s.elements
7869                .first()
7870                .and_then(|e| e.first())
7871                .is_some_and(|v| v == "7")
7872                && s.elements
7873                    .get(3)
7874                    .and_then(|e| e.first())
7875                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
7876        });
7877        ConditionResult::from(found)
7878    }
7879
7880    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
7881    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
7882        let sts_segs = ctx.find_segments("STS");
7883        let found = sts_segs.iter().any(|s| {
7884            s.elements
7885                .first()
7886                .and_then(|e| e.first())
7887                .is_some_and(|v| v == "7")
7888                && s.elements
7889                    .get(3)
7890                    .and_then(|e| e.first())
7891                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
7892        });
7893        ConditionResult::from(found)
7894    }
7895
7896    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
7897    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
7898        let sts_segs = ctx.find_segments("STS");
7899        let found = sts_segs.iter().any(|s| {
7900            s.elements
7901                .first()
7902                .and_then(|e| e.first())
7903                .is_some_and(|v| v == "7")
7904                && s.elements
7905                    .get(3)
7906                    .and_then(|e| e.first())
7907                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
7908        });
7909        ConditionResult::from(found)
7910    }
7911
7912    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
7913    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
7914        let sts_segs = ctx.find_segments("STS");
7915        let found = sts_segs.iter().any(|s| {
7916            s.elements
7917                .first()
7918                .and_then(|e| e.first())
7919                .is_some_and(|v| v == "7")
7920                && s.elements
7921                    .get(3)
7922                    .and_then(|e| e.first())
7923                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
7924        });
7925        ConditionResult::from(found)
7926    }
7927
7928    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
7929    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
7930        let sts_segs = ctx.find_segments("STS");
7931        let found = sts_segs.iter().any(|s| {
7932            s.elements
7933                .first()
7934                .and_then(|e| e.first())
7935                .is_some_and(|v| v == "7")
7936                && s.elements
7937                    .get(3)
7938                    .and_then(|e| e.first())
7939                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
7940        });
7941        ConditionResult::from(found)
7942    }
7943
7944    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
7945    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
7946        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
7947        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
7948        if rff_z49.is_empty() {
7949            return ConditionResult::Unknown;
7950        }
7951        // Approximate: condition applies when RFF+Z49 exists
7952        ConditionResult::True
7953    }
7954
7955    /// [490] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC" ist
7956    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
7957        ctx.format_check("DTM", 0, 1, is_mesz_utc)
7958    }
7959
7960    /// [491] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC"" ist
7961    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
7962        ctx.format_check("DTM", 0, 1, is_mez_utc)
7963    }
7964
7965    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
7966    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
7967        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
7968        // so lexicographic comparison works directly.
7969        let val = ctx.resolved_value.or_else(|| {
7970            ctx.find_segments_with_qualifier("DTM", 0, "137")
7971                .first()
7972                .and_then(|s| s.elements.first())
7973                .and_then(|e| e.get(1))
7974                .map(|v| v.as_str())
7975        });
7976        let Some(dtm_val) = val else {
7977            return ConditionResult::Unknown;
7978        };
7979        if dtm_val.len() < 12 {
7980            return ConditionResult::Unknown;
7981        }
7982        // HAND-EDITED: the provider's clock, not the wall clock, so a caller
7983        // on a simulated clock judges its own messages (mako-twin).
7984        let now_str = ctx.external.now().format("%Y%m%d%H%M").to_string();
7985        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
7986    }
7987
7988    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
7989    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
7990        ConditionResult::True
7991    }
7992
7993    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
7994    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
7995        ConditionResult::True
7996    }
7997
7998    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
7999    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8000        ConditionResult::True
8001    }
8002
8003    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8004    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8005        ConditionResult::True
8006    }
8007
8008    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8009    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8010        ConditionResult::True
8011    }
8012
8013    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8014    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8015        ConditionResult::True
8016    }
8017
8018    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8019    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8020        ConditionResult::True
8021    }
8022
8023    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8024    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8025        ConditionResult::True
8026    }
8027
8028    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8029    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8030        ConditionResult::True
8031    }
8032
8033    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8034    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8035        ConditionResult::True
8036    }
8037
8038    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8039    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8040        ConditionResult::True
8041    }
8042
8043    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8044    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8045        ConditionResult::True
8046    }
8047
8048    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8049    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8050        ConditionResult::True
8051    }
8052
8053    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8054    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8055        ConditionResult::True
8056    }
8057
8058    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8059    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8060        ConditionResult::True
8061    }
8062
8063    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8064    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8065        ConditionResult::True
8066    }
8067
8068    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8069    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8070        ConditionResult::True
8071    }
8072
8073    /// [517] Hinweis: Anzuwenden wenn einer Marktlokation eine Paket-ID zugeordnet wurde und diese wieder gelöscht werden soll, da sie nicht unter den genannten NB-Wechsel fällt
8074    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8075        ConditionResult::True
8076    }
8077
8078    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8079    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8080        ConditionResult::True
8081    }
8082
8083    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8084    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8085        ConditionResult::True
8086    }
8087
8088    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8089    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8090        ConditionResult::True
8091    }
8092
8093    /// [521] Hinweis: Wenn im zweiten DE 9013 des STS+7 (Transaktionsgrund) der Code ZAP vorhanden ist, so ist das hier angegebene Datum als Zuordnungsbeginn bei der Bildung der \"Ruhenden MaLo zu verstehen. Be...
8094    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8095        ConditionResult::True
8096    }
8097
8098    /// [522] Hinweis: Es ist der NBN anzugeben
8099    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8100        ConditionResult::True
8101    }
8102
8103    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8104    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8105        ConditionResult::True
8106    }
8107
8108    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8109    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8110        ConditionResult::True
8111    }
8112
8113    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8114    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8115        ConditionResult::True
8116    }
8117
8118    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8119    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8120        ConditionResult::True
8121    }
8122
8123    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8124    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8125        ConditionResult::True
8126    }
8127
8128    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8129    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8130        ConditionResult::True
8131    }
8132
8133    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8134    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8135        ConditionResult::True
8136    }
8137
8138    /// [530] Hinweis: Es sind alle an der Lokation vorhandenen Daten, die mit dieser Segmentgruppe übermittelt werden und zum Datum „Änderung zum" Gültigkeit haben, anzugeben. Dies kann zur Folge haben, ...
8139    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8140        ConditionResult::True
8141    }
8142
8143    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8144    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8145        ConditionResult::True
8146    }
8147
8148    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8149    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8150        ConditionResult::True
8151    }
8152
8153    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8154    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8155        ConditionResult::True
8156    }
8157
8158    /// [555] Die Anwendungsfälle für die Durchführung der BDEW-Anwendungshilfe „Marktprozesse Netzbetreiberwechsel Sparte Strom" sind ab dem 01.08.2025 für Netzbetreiberwechsel ab dem 01.01.2026 zu verw...
8159    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8160        // TODO: implement
8161        ConditionResult::Unknown
8162    }
8163
8164    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8165    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8166        ConditionResult::True
8167    }
8168
8169    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8170    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8171        ConditionResult::True
8172    }
8173
8174    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8175    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8176        ConditionResult::True
8177    }
8178
8179    /// [563] Hinweis: Für die ID der LieferantensummenZR
8180    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8181        ConditionResult::True
8182    }
8183
8184    /// [566] Hinweis: Altlieferant
8185    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8186        ConditionResult::True
8187    }
8188
8189    /// [567] Hinweis: Neulieferant
8190    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8191        ConditionResult::True
8192    }
8193
8194    /// [568] Hinweis: Lieferant der LieferantensummenZR
8195    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8196        ConditionResult::True
8197    }
8198
8199    /// [569] Hinweis: Dritter Nutzer
8200    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8201        ConditionResult::True
8202    }
8203
8204    /// [570] Hinweis: Netzbetreiber Alt
8205    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8206        ConditionResult::True
8207    }
8208
8209    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8210    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8211        ConditionResult::True
8212    }
8213
8214    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8215    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8216        ConditionResult::True
8217    }
8218
8219    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8220    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8221        ConditionResult::True
8222    }
8223
8224    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8225    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8226        ConditionResult::True
8227    }
8228
8229    /// [580] Hinweis:  Ersatzbelieferung gibt es nur bei  - Marktlokationen in der  Niederspannung, die kein Haushaltskunde gem. EnWG sind und die nicht mehr der gesetzlichen Ersatzversorgung (drei Monate) unte...
8230    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8231        ConditionResult::True
8232    }
8233
8234    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8235    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8236        ConditionResult::True
8237    }
8238
8239    /// [586] Hinweis: Die Messlokationsadresse ist der Messlokation zugeordnet, welche in SG8 SEQ+Z18/ ZF3 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokatio...
8240    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8241        ConditionResult::True
8242    }
8243
8244    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8245    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8246        ConditionResult::True
8247    }
8248
8249    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8250    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8251        ConditionResult::True
8252    }
8253
8254    /// [599] Hinweis: Es ist der Name und die Adresse des Ablesekartenempfängers für die Messlokation anzugeben, welche in SG8 SEQ+Z18 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der proz...
8255    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8256        ConditionResult::True
8257    }
8258
8259    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8260    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8261        ConditionResult::True
8262    }
8263
8264    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8265    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8266        ConditionResult::True
8267    }
8268
8269    /// [609] Hinweis: In diesem Segment bzw. SG sind die zwischen NB, LF und ÜNB im Rahmen der Marktkommunikation ausgetauschten Daten aus der Sicht des ÜNB anzugeben
8270    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8271        ConditionResult::True
8272    }
8273
8274    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8275    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8276        ConditionResult::True
8277    }
8278
8279    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8280    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8281        ConditionResult::True
8282    }
8283
8284    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8285    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8286        ConditionResult::True
8287    }
8288
8289    /// [618] Hinweis: Bisheriges Datum des MSBA. Sollte der MSBN ein abweichendes Datum verwenden wollen, so teilt er das neue Datum mit einer Stammdatenänderung mit
8290    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8291        ConditionResult::True
8292    }
8293
8294    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8295    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8296        ConditionResult::True
8297    }
8298
8299    /// [621] Hinweis: Es ist der MSB anzugeben, welcher ab dem Zeitpunkt der Lokation zugeordnet ist, der in DTM+76 (Datum zum geplanten Leistungsbeginn) genannt ist.
8300    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8301        ConditionResult::True
8302    }
8303
8304    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8305    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8306        ConditionResult::True
8307    }
8308
8309    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8310    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8311        ConditionResult::True
8312    }
8313
8314    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8315    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8316        ConditionResult::True
8317    }
8318
8319    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8320    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8321        ConditionResult::True
8322    }
8323
8324    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8325    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8326        ConditionResult::True
8327    }
8328
8329    /// [637] Hinweis: Bei Verpflichtungsanfrage
8330    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8331        ConditionResult::True
8332    }
8333
8334    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8335    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8336        ConditionResult::True
8337    }
8338
8339    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8340    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8341        ConditionResult::True
8342    }
8343
8344    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8345    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8346        ConditionResult::True
8347    }
8348
8349    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8350    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8351        ConditionResult::True
8352    }
8353
8354    /// [642] Hinweis: Wenn Abo vorliegt
8355    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8356        ConditionResult::True
8357    }
8358
8359    /// [643] Hinweis: Nachfolgender Netzbetreiber
8360    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8361        ConditionResult::True
8362    }
8363
8364    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8365    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8366        ConditionResult::True
8367    }
8368
8369    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8370    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8371        ConditionResult::True
8372    }
8373
8374    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8375    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8376        ConditionResult::True
8377    }
8378
8379    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8380    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8381        ConditionResult::True
8382    }
8383
8384    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8385    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8386        ConditionResult::True
8387    }
8388
8389    /// [653] Hinweis: Es sind alle Netzlokationen, Marktlokationen, Tranchen und Messlokationen zu nennen, die durch die Bestätigung der Abmeldung der prozessual behandelten Messlokation nicht mehr dem MSB zug...
8390    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8391        ConditionResult::True
8392    }
8393
8394    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8395    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8396        ConditionResult::True
8397    }
8398
8399    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8400    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8401        ConditionResult::True
8402    }
8403
8404    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8405    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8406        ConditionResult::True
8407    }
8408
8409    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8410    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8411        ConditionResult::True
8412    }
8413
8414    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8415    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8416        ConditionResult::True
8417    }
8418
8419    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8420    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8421        ConditionResult::True
8422    }
8423
8424    /// [665] Hinweis: Wenn ein Zähler mit einem SMGW parametriert ist werden an dem Zähler keine OBIS-Kennzahlen angegeben Hier gibt es nur OBIS Kennzahlen vom SMGW
8425    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8426        ConditionResult::True
8427    }
8428
8429    /// [667] Hinweis: Für Zeiten die bilanziert wurden, aber inzwischen keine Aggregationsverantwortung beim ÜNB vorliegt, ist eine SG8 mit SG8 - SG10 CCI+6=ZA8 oder SG 10 CCI+6 = ZA9 CAV=ZG4 und SG8 - SG9 QT...
8430    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8431        ConditionResult::True
8432    }
8433
8434    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8435    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8436        ConditionResult::True
8437    }
8438
8439    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8440    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8441        ConditionResult::True
8442    }
8443
8444    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8445    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8446        ConditionResult::True
8447    }
8448
8449    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8450    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8451        ConditionResult::True
8452    }
8453
8454    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8455    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8456        ConditionResult::True
8457    }
8458
8459    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8460    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8461        ConditionResult::True
8462    }
8463
8464    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8465    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8466        ConditionResult::True
8467    }
8468
8469    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8470    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8471        ConditionResult::True
8472    }
8473
8474    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8475    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8476        ConditionResult::True
8477    }
8478
8479    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8480    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8481        ConditionResult::True
8482    }
8483
8484    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8485    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8486        ConditionResult::True
8487    }
8488
8489    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8490    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8491        ConditionResult::True
8492    }
8493
8494    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8495    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8496        ConditionResult::True
8497    }
8498
8499    /// [684] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16/ Z22 angegebenen Marktlokation aktuell und / oder zukünftig zugehören, anzugeben
8500    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8501        ConditionResult::True
8502    }
8503
8504    /// [685] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen aktuell und / oder zukünftig zugeordnet sind, anzugeben.
8505    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8506        ConditionResult::True
8507    }
8508
8509    /// [687] Hinweis: Es sind alle Messlokationen zu nennen, die für die Energiemengenermittlung aktuell und / oder zukünftig der im LOC+Z16/ Z22 angegebenen Marktlokation notwendig sind
8510    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8511        ConditionResult::True
8512    }
8513
8514    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8515    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8516        ConditionResult::True
8517    }
8518
8519    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8520    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8521        ConditionResult::True
8522    }
8523
8524    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8525    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8526        ConditionResult::True
8527    }
8528
8529    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8530    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8531        ConditionResult::True
8532    }
8533
8534    /// [694] Hinweis: Wenn in einer Marktlokation eine ID für eine Technischen Ressource vergeben wird, müssen für alle ggf. weitere Technische Ressourcen in der Marktlokation ID vergeben werden. Hintergrund...
8535    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8536        ConditionResult::True
8537    }
8538
8539    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8540    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8541        ConditionResult::True
8542    }
8543
8544    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8545    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8546        ConditionResult::True
8547    }
8548
8549    /// [698] Hinweis: Für eine erzeugende Marktlokation muss für jede Technische Ressource eine ID der Technischen Ressourcen vergeben werden. Dies ist notwendig um die Nennleistung der Technische Ressource b...
8550    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8551        ConditionResult::True
8552    }
8553
8554    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8555    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8556        ConditionResult::True
8557    }
8558
8559    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8560    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8561        ConditionResult::True
8562    }
8563
8564    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8565    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8566        ConditionResult::True
8567    }
8568
8569    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8570    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8571        ConditionResult::True
8572    }
8573
8574    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8575    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8576        ctx.external.evaluate("data_clearing_required")
8577    }
8578
8579    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8580    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8581        ConditionResult::True
8582    }
8583
8584    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8585    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8586        ConditionResult::True
8587    }
8588
8589    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8590    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8591        ConditionResult::True
8592    }
8593
8594    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8595    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8596        ConditionResult::True
8597    }
8598
8599    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8600    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8601        ConditionResult::True
8602    }
8603
8604    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8605    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8606        ConditionResult::True
8607    }
8608
8609    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8610    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8611        ConditionResult::True
8612    }
8613
8614    /// [714] Hinweis: Es ist bei einer unterjährigen Gebietsübernahme möglich hier den bisherigen NB zu nennen um aufzuzeigen, dass die im PIA+5 dieser SG8 genannten Gruppenartikel-ID bzw. Artikel-ID auf die...
8615    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8616        ConditionResult::True
8617    }
8618
8619    /// [715] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn zuvor vom LF eine Anfrage Abr.-Daten BK-Abr. in die Vergangenheit erfolgte oder eine Korrektur zuvor m...
8620    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8621        ConditionResult::True
8622    }
8623
8624    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8625    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8626        ConditionResult::True
8627    }
8628
8629    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8630    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8631        ctx.external.evaluate("data_clearing_required")
8632    }
8633
8634    /// [718] Hinweis: Es sind alle Tranchen der im SG5 LOC+Z16 (Marktlokation) genannten Marktlokation anzugeben, die für die im SG6 (Verwendungszeitraum der Daten) genannten Zeiträume vorhanden sind
8635    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8636        ConditionResult::True
8637    }
8638
8639    /// [719] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn der LF auf eine Anfrage mit diesem Transaktionsgrund antwortet oder eine Anfrage Abr.-Daten BK-Abr. in...
8640    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8641        ConditionResult::True
8642    }
8643
8644    /// [902] Format: Möglicher Wert: ≥ 0
8645    fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
8646        // HAND-EDITED: judged on the value it annotates; without one it
8647        // says nothing.
8648        match ctx.resolved_value.filter(|v| !v.is_empty()) {
8649            Some(value) => validate_numeric(value, ">=", 0.0),
8650            None => ConditionResult::True,
8651        }
8652    }
8653
8654    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8655    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8656        // Format condition — informational, always applies
8657        ConditionResult::True
8658    }
8659
8660    /// [914] Format: Möglicher Wert: > 0
8661    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8662        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8663    }
8664
8665    /// [922] Format: TR-ID
8666    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8667        // Format condition — informational, always applies
8668        ConditionResult::True
8669    }
8670
8671    /// [926] Format: Möglicher Wert: 0
8672    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8673        // Format condition — informational, always applies
8674        ConditionResult::True
8675    }
8676
8677    /// [930] Format: max. 2 Nachkommastellen
8678    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8679        // Format condition — informational, always applies
8680        ConditionResult::True
8681    }
8682
8683    /// [931] Format: ZZZ = +00
8684    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8685        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8686    }
8687
8688    /// [932] Format: HHMM = 2200
8689    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8690        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8691    }
8692
8693    /// [933] Format: HHMM = 2300
8694    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8695        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8696    }
8697
8698    /// [937] Format: keine Nachkommastelle
8699    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8700        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8701    }
8702
8703    /// [938] Format: Möglicher Wert: <= 10
8704    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8705        ConditionResult::True
8706    }
8707
8708    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8709    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8710        ctx.format_check("COM", 0, 0, validate_email)
8711    }
8712
8713    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8714    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8715        ctx.format_check("COM", 0, 0, validate_phone)
8716    }
8717
8718    /// [942] Format: n1-n2-n1-n3
8719    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8720        // Format condition — informational, always applies
8721        ConditionResult::True
8722    }
8723
8724    /// [943] Format: n1-n2-n1
8725    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8726        // Format condition — informational, always applies
8727        ConditionResult::True
8728    }
8729
8730    /// [946] Format: max. 11 Nachkommastellen
8731    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8732        // Format condition — informational, always applies
8733        ConditionResult::True
8734    }
8735
8736    /// [948] Format: n1-n2-n1-n8-n2
8737    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8738        // Format condition — informational, always applies
8739        ConditionResult::True
8740    }
8741
8742    /// [950] Format: Marktlokations-ID
8743    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8744        // Format condition — informational, always applies
8745        ConditionResult::True
8746    }
8747
8748    /// [951] Format: Zählpunktbezeichnung
8749    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8750        // Format condition — informational, always applies
8751        ConditionResult::True
8752    }
8753
8754    /// [952] Format: Gerätenummer nach DIN 43863-5
8755    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8756        // Format condition — informational, always applies
8757        ConditionResult::True
8758    }
8759
8760    /// [955] Format: Möglicher Wert: < 100
8761    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8762        // Format condition — informational, always applies
8763        ConditionResult::True
8764    }
8765
8766    /// [957] Format: n1-n2-n1-n8
8767    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8768        // Format condition — informational, always applies
8769        ConditionResult::True
8770    }
8771
8772    /// [960] Format: Netzlokations-ID
8773    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8774        // Format condition — informational, always applies
8775        ConditionResult::True
8776    }
8777
8778    /// [961] Format: SR-ID
8779    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8780        // Format condition — informational, always applies
8781        ConditionResult::True
8782    }
8783
8784    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8785    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8786        // Format condition — informational, always applies
8787        ConditionResult::True
8788    }
8789
8790    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8791    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8792        // Cardinality: at least twice per SG4 IDE+24
8793        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8794        ConditionResult::from(ide_count > 0)
8795    }
8796
8797    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8798    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8799        let seq_segs = ctx.find_segments("SEQ");
8800        let has_z79 = seq_segs.iter().any(|s| {
8801            s.elements
8802                .first()
8803                .and_then(|e| e.first())
8804                .is_some_and(|v| v == "Z79")
8805        });
8806        ConditionResult::from(has_z79)
8807    }
8808
8809    /// [2003] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) ab dem Zeitpunkt aus dem SG4 DTM+92 (Beginn zum) untergeordnet ist
8810    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8811        // TODO: implement
8812        ConditionResult::Unknown
8813    }
8814
8815    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8816    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
8817    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("genau einmal")
8818    // is a count, not a condition on the group: the generated code counted
8819    // the SG8 of every variant naming the ID, so several variants made it
8820    // false and the group was never judged.
8821    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8822        let named = ctx
8823            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
8824            .iter()
8825            .any(|s| !s.get_component(0, 2).is_empty());
8826        ConditionResult::from(named)
8827    }
8828
8829    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8830    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
8831    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("mindestens einmal")
8832    // is a count, not a condition on the group: the generated code counted
8833    // the SG8 of every variant naming the ID, so several variants made it
8834    // false and the group was never judged.
8835    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
8836        let named = ctx
8837            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
8838            .iter()
8839            .any(|s| !s.get_component(0, 2).is_empty());
8840        ConditionResult::from(named)
8841    }
8842
8843    /// [2010] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] ∧ [479] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID ...
8844    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
8845        // TODO: implement
8846        ConditionResult::Unknown
8847    }
8848
8849    /// [2011] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 / Z53 (Verwendungszeitraum der Daten: "Gültige Daten" / "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN ...
8850    /// EXTERNAL: Requires context from outside the message.
8851    // REVIEW: Condition requires checking Zeitraum-IDs from DE1156 of SG6 RFF+Z49/Z53 ('Verwendungszeitraum der Daten: Gültige Daten / Keine Daten') that originated in the REQUEST message referenced by SG6 RFF+TN DE1154. The referenced request message is a separate EDIFACT interchange not present in the current message being validated. The cardinality constraint ('exactly once per Zeitraum-ID from the request') is therefore an external business context check that depends on data from a prior message in the business process. (medium confidence)
8852    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
8853        ctx.external
8854            .evaluate("request_message_zeitraum_ids_present")
8855    }
8856
8857    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
8858    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
8859        // TODO: implement
8860        ConditionResult::Unknown
8861    }
8862
8863    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
8864    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
8865        // Check: condition applies when no RFF+Z54 exists
8866        ctx.lacks_qualifier("RFF", 0, "Z54")
8867    }
8868
8869    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
8870    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
8871        // Check: condition applies when no RFF+Z47 exists
8872        ctx.lacks_qualifier("RFF", 0, "Z47")
8873    }
8874
8875    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
8876    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
8877        // TODO: implement
8878        ConditionResult::Unknown
8879    }
8880
8881    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
8882    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
8883        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
8884        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8885        ConditionResult::from(loc_count > 0)
8886    }
8887
8888    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
8889    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
8890        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
8891        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8892        ConditionResult::from(loc_count > 0)
8893    }
8894
8895    /// [2018] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
8896    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
8897        // TODO: implement
8898        ConditionResult::Unknown
8899    }
8900
8901    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
8902    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
8903        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
8904        let found = ctx.find_segments("CAV").iter().any(|s| {
8905            s.elements
8906                .first()
8907                .and_then(|e| e.first())
8908                .is_some_and(|v| v == "NZR")
8909        });
8910        ConditionResult::from(found)
8911    }
8912
8913    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
8914    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
8915        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
8916        let ide_count = ctx.find_segments("IDE").len();
8917        ConditionResult::from(ide_count > 0)
8918    }
8919
8920    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
8921    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
8922        // Cardinality: LieferantensummenZR once per SG4 IDE+24
8923        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8924        ConditionResult::from(ide_count > 0)
8925    }
8926
8927    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
8928    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
8929        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
8930        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8931        ConditionResult::from(ide_count > 0)
8932    }
8933
8934    /// [2075] Für die ID der Zeitreihen (nicht bei EEG-EUZ und EUZ der AAÜZ) in der Clearingliste einmal je SG4 IDE+24 (Vorgang)
8935    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
8936        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
8937        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8938        ConditionResult::from(ide_count > 0)
8939    }
8940
8941    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
8942    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
8943        // Cardinality: max twice per SG4 IDE+24
8944        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8945        ConditionResult::from(ide_count > 0)
8946    }
8947
8948    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
8949    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
8950        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
8951        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
8952        ConditionResult::from(loc_count > 0)
8953    }
8954
8955    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
8956    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
8957        // Cardinality: exactly twice per SG4 IDE
8958        let ide_count = ctx.find_segments("IDE").len();
8959        ConditionResult::from(ide_count > 0)
8960    }
8961
8962    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
8963    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
8964        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
8965        let found = ctx.find_segments("SEQ").iter().any(|s| {
8966            s.elements
8967                .first()
8968                .and_then(|e| e.first())
8969                .is_some_and(|v| v == "Z13" || v == "ZG0")
8970        });
8971        ConditionResult::from(found)
8972    }
8973
8974    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
8975    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
8976        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
8977        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8978        ConditionResult::from(ide_count > 0)
8979    }
8980
8981    /// [2182] Segmentgruppe ist genau einmal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
8982    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
8983        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
8984        let found = ctx.find_segments("SEQ").iter().any(|s| {
8985            s.elements
8986                .first()
8987                .and_then(|e| e.first())
8988                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
8989        });
8990        ConditionResult::from(found)
8991    }
8992
8993    /// [2183] Segmentgruppe ist genau zweimal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
8994    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
8995        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
8996        let found = ctx.find_segments("SEQ").iter().any(|s| {
8997            s.elements
8998                .first()
8999                .and_then(|e| e.first())
9000                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9001        });
9002        ConditionResult::from(found)
9003    }
9004
9005    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9006    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9007        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9008        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9009        ConditionResult::from(ide_count > 0)
9010    }
9011
9012    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9013    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9014        // TODO: implement
9015        ConditionResult::Unknown
9016    }
9017
9018    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9019    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9020        // Cardinality: code once per SG4 IDE+24
9021        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9022        ConditionResult::from(ide_count > 0)
9023    }
9024
9025    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9026    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9027        // TODO: implement
9028        ConditionResult::Unknown
9029    }
9030
9031    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9032    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9033        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9034        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9035        ConditionResult::from(loc_count > 0)
9036    }
9037
9038    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9039    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9040        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9041        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9042        ConditionResult::from(loc_count > 0)
9043    }
9044
9045    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9046    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9047        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9048        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9049        ConditionResult::from(found)
9050    }
9051
9052    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9053    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9054        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9055        let found = ctx.find_segments("SEQ").iter().any(|s| {
9056            s.elements
9057                .first()
9058                .and_then(|e| e.first())
9059                .is_some_and(|v| v == "Z03" || v == "ZF5")
9060        });
9061        ConditionResult::from(found)
9062    }
9063
9064    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9065    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9066        // TODO: implement
9067        ConditionResult::Unknown
9068    }
9069
9070    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9071    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9072        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9073        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9074        ConditionResult::from(loc_count > 0)
9075    }
9076
9077    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9078    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9079        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9080        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9081        ConditionResult::from(loc_count > 0)
9082    }
9083
9084    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9085    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9086        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9087        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9088        ConditionResult::from(loc_count > 0)
9089    }
9090
9091    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9092    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9093        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9094        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9095        ConditionResult::from(loc_count > 0)
9096    }
9097
9098    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9099    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9100        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9101        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9102        ConditionResult::from(loc_count > 0)
9103    }
9104
9105    /// [2312] Wenn der Objektcode \"9992000001256\" (Netzlokation) im DE1154 des selben RFF+Z33 nicht vorhanden ist, ist das RFF+Z33 in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des...
9106    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9107        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9108        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9109        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9110        let has_netzlokation = rff_z33.iter().any(|s| {
9111            s.elements
9112                .first()
9113                .and_then(|e| e.get(1))
9114                .is_some_and(|v| v == "9992000001256")
9115        });
9116        ConditionResult::from(!has_netzlokation)
9117    }
9118
9119    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9120    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9121        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9122        let found = ctx.find_segments("SEQ").iter().any(|s| {
9123            s.elements
9124                .first()
9125                .and_then(|e| e.first())
9126                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9127        });
9128        ConditionResult::from(found)
9129    }
9130
9131    /// [2317] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MIW/MPW/MUW vorhanden, ist das Segment mindestens zweimal anzugeben
9132    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9133        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9134        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9135        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9136            s.elements
9137                .first()
9138                .and_then(|e| e.first())
9139                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9140        });
9141        ConditionResult::from(has_cav)
9142    }
9143
9144    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9145    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9146        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9147        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9148        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9149            s.elements
9150                .first()
9151                .and_then(|e| e.first())
9152                .is_some_and(|v| v == "MBW")
9153        });
9154        ConditionResult::from(has_cav)
9155    }
9156
9157    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9158    fn evaluate_2344(&self, ctx: &EvaluationContext) -> ConditionResult {
9159        // HAND-EDITED (#343): a repetition bound per Marktlokation or Tranche of the
9160        // transaction; as a presence condition it holds where the transaction names one
9161        // (SG5 LOC+Z16 / LOC+Z21), within the enclosing SG4 when the scope reaches one.
9162        let loc = ctx.scoped_find_segments_in("SG4", "LOC");
9163        ConditionResult::from(
9164            loc.iter()
9165                .any(|s| ["Z16", "Z21"].contains(&s.get_element(0))),
9166        )
9167    }
9168
9169    /// [2350] Für jedes SMGW das im SEQ+Z13/ ZG0 (Smartmeter-Gateway) SG10 CCI+++Z75 CAV+Z30 (Gerätenummer) genannt ist, mindestens einmal je SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das mit SG8 RFF+Z14 (Referenz...
9170    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9171        // TODO: implement
9172        ConditionResult::Unknown
9173    }
9174
9175    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9176    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9177        // TODO: implement
9178        ConditionResult::Unknown
9179    }
9180
9181    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9182    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9183        // TODO: implement
9184        ConditionResult::Unknown
9185    }
9186
9187    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9188    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9189        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9190        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9191        ConditionResult::from(loc_count > 0)
9192    }
9193
9194    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9195    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9196        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9197        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9198        ConditionResult::from(loc_count > 0)
9199    }
9200
9201    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9202    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9203        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9204        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9205        ConditionResult::from(loc_count > 0)
9206    }
9207
9208    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9209    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9210        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9211        let found = ctx.find_segments("LOC").iter().any(|s| {
9212            s.elements
9213                .first()
9214                .and_then(|e| e.first())
9215                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9216        });
9217        ConditionResult::from(found)
9218    }
9219
9220    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9221    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9222        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9223        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9224        ConditionResult::from(loc_count > 0)
9225    }
9226
9227    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9228    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9229        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9230        let found = ctx.find_segments("LOC").iter().any(|s| {
9231            s.elements
9232                .first()
9233                .and_then(|e| e.first())
9234                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9235        });
9236        ConditionResult::from(found)
9237    }
9238}
9239
9240// HAND-EDITED: helpers for [223] / [265].
9241
9242use mig_types::segment::OwnedSegment;
9243
9244/// One SG8 repetition: its SEQ qualifier, its RFF segments and the
9245/// Marktlokation IDs of its RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9246struct Sg8Block {
9247    seq: String,
9248    /// The SEQ's Zeitraum-ID (DE1050).
9249    zeitraum: String,
9250    rffs: Vec<OwnedSegment>,
9251    malo_refs: Vec<String>,
9252    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9253}
9254
9255fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9256    seg.elements
9257        .get(element)
9258        .and_then(|e| e.get(comp))
9259        .map(String::as_str)
9260}
9261
9262/// The message's SG8 repetitions, from the group navigator when there is one,
9263/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9264/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9265/// evaluates BO4E input without a navigator.) `None` without either.
9266fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9267    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9268        rffs.iter()
9269            .filter(|s| component(s, 0, 0) == Some("Z18"))
9270            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9271            .collect()
9272    };
9273    if let Some(nav) = ctx.navigator() {
9274        let sg8 = ["SG4", "SG8"];
9275        return Some(
9276            (0..nav.group_instance_count(&sg8))
9277                .map(|i| Sg8Block {
9278                    seq: nav
9279                        .find_segments_in_group("SEQ", &sg8, i)
9280                        .first()
9281                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9282                        .unwrap_or_default(),
9283                    zeitraum: nav
9284                        .find_segments_in_group("SEQ", &sg8, i)
9285                        .first()
9286                        .and_then(|s| component(s, 1, 0).map(str::to_string))
9287                        .unwrap_or_default(),
9288                    rffs: nav.find_segments_in_group("RFF", &sg8, i),
9289                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9290                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9291                        .map(|j| {
9292                            (
9293                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9294                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9295                            )
9296                        })
9297                        .collect(),
9298                })
9299                .collect(),
9300        );
9301    }
9302    if ctx.segments.is_empty() {
9303        return None;
9304    }
9305    let mut blocks: Vec<Sg8Block> = Vec::new();
9306    let mut rffs: Vec<OwnedSegment> = Vec::new();
9307    for seg in ctx.segments {
9308        match seg.id.as_str() {
9309            "SEQ" => {
9310                if let Some(b) = blocks.last_mut() {
9311                    b.malo_refs = malo_refs(&rffs);
9312                    b.rffs = std::mem::take(&mut rffs);
9313                }
9314                rffs.clear();
9315                blocks.push(Sg8Block {
9316                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9317                    zeitraum: component(seg, 1, 0).unwrap_or_default().to_string(),
9318                    rffs: Vec::new(),
9319                    malo_refs: Vec::new(),
9320                    sg10: Vec::new(),
9321                });
9322            }
9323            // Segments after the SG8s end the last one.
9324            "NAD" | "UNT" | "IDE" => {
9325                if let Some(b) = blocks.last_mut() {
9326                    if b.rffs.is_empty() {
9327                        b.malo_refs = malo_refs(&rffs);
9328                        b.rffs = std::mem::take(&mut rffs);
9329                    }
9330                }
9331                rffs.clear();
9332                if seg.id == "IDE" {
9333                    continue;
9334                }
9335                break;
9336            }
9337            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9338            "CCI" => {
9339                if let Some(b) = blocks.last_mut() {
9340                    b.sg10.push((vec![seg.clone()], Vec::new()));
9341                }
9342            }
9343            "CAV" => {
9344                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9345                    cavs.push(seg.clone());
9346                }
9347            }
9348            _ => {}
9349        }
9350    }
9351    if let Some(b) = blocks.last_mut() {
9352        if b.rffs.is_empty() {
9353            b.malo_refs = malo_refs(&rffs);
9354            b.rffs = rffs;
9355        }
9356    }
9357    Some(blocks)
9358}
9359
9360// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9361// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9362
9363/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9364/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9365/// one; any SG10 of the message otherwise.
9366fn sg10_has(
9367    ctx: &EvaluationContext,
9368    this_sg8: bool,
9369    pred: impl Fn(&[OwnedSegment]) -> bool,
9370) -> ConditionResult {
9371    if this_sg8 {
9372        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9373            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9374        }
9375    }
9376    let Some(blocks) = sg8_blocks(ctx) else {
9377        return ConditionResult::False;
9378    };
9379    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9380        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9381        pred(&segs)
9382    }))
9383}
9384
9385/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9386/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9387/// otherwise.
9388fn this_sg10_has(
9389    ctx: &EvaluationContext,
9390    pred: impl Fn(&[OwnedSegment]) -> bool,
9391) -> ConditionResult {
9392    if let Some(path) = ctx.enclosing_scope("SG10") {
9393        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9394            .iter()
9395            .flat_map(|tag| ctx.find_segments_at(path, tag))
9396            .collect();
9397        return ConditionResult::from(pred(&segs));
9398    }
9399    sg10_has(ctx, false, pred)
9400}
9401
9402/// [114]–[116]: "das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV
9403/// `code` in dieser SG vorhanden".
9404fn spannungsebene_in_this_sg10(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9405    this_sg10_has(ctx, |segs| {
9406        segs.iter()
9407            .any(|s| s.id == "CCI" && component(s, 2, 0) == Some("E03"))
9408            && segs
9409                .iter()
9410                .any(|s| s.id == "CAV" && component(s, 0, 0) == Some(code))
9411    })
9412}
9413
9414/// The reference (C506 DE1154) of the RFF whose field is judged.
9415fn rff_reference<'c>(ctx: &EvaluationContext<'c>) -> Option<&'c str> {
9416    ctx.resolved_segment?
9417        .first()?
9418        .get(1)
9419        .map(String::as_str)
9420        .filter(|v| !v.is_empty())
9421}
9422
9423/// "Wenn in demselben RFF der Code … im DE1153 vorhanden": the qualifier of the
9424/// RFF whose field is judged; message-wide only where no segment is resolved.
9425fn same_rff_qualifier(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9426    match ctx.resolved_segment {
9427        Some(elements) => ConditionResult::from(
9428            elements
9429                .first()
9430                .and_then(|e| e.first())
9431                .is_some_and(|q| q == code),
9432        ),
9433        None => ctx.has_qualifier("RFF", 0, code),
9434    }
9435}
9436
9437/// HAND-EDITED: `[126]`/`[127]`/`[128]` — the value is the 1-based position of
9438/// the resolved RFF among the SG6 RFF of `qualifiers` in its SG4 ("Das erste
9439/// SG6 RFF+Z49/Z53 hat somit die 1, das zweite die 2").
9440fn repetition_ordinal(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9441    let (Some(value), Some(segment)) = (ctx.resolved_value, ctx.resolved_segment) else {
9442        return ConditionResult::Unknown;
9443    };
9444    let siblings: Vec<_> = ctx
9445        .scoped_find_segments_in("SG4", "RFF")
9446        .into_iter()
9447        .filter(|s| qualifiers.contains(&s.get_component(0, 0)))
9448        .collect();
9449    match siblings
9450        .iter()
9451        .position(|s| s.elements.as_slice() == segment)
9452    {
9453        Some(i) => ConditionResult::from(value == (i + 1).to_string()),
9454        None => ConditionResult::Unknown,
9455    }
9456}
9457
9458/// [131]/[132]: the DTM+Z25 of an SG6 whose RFF carries Zeitraum-ID "1"
9459/// (DE1156), against 0:00 German time of the day after DTM+137
9460/// (Nachrichtendatum). `False` when the SG6 has another Zeitraum-ID — [401],
9461/// [134] and [135] judge those.
9462///
9463/// "Dieses DTM+Z25" is the one of the enclosing SG6. Without a scope, every
9464/// SG6 with Zeitraum-ID 1 must pass.
9465// HAND-EDITED
9466fn first_period_start_vs_next_day(
9467    ctx: &EvaluationContext,
9468    holds: impl Fn(chrono::NaiveDateTime, chrono::NaiveDateTime) -> bool,
9469) -> ConditionResult {
9470    use chrono::Duration;
9471    let Some(sent) = ctx
9472        .find_segments_with_qualifier("DTM", 0, "137")
9473        .first()
9474        .and_then(|dtm| german_local_time(dtm.get_component(0, 1)))
9475    else {
9476        return ConditionResult::Unknown;
9477    };
9478    let next_day = (sent.date() + Duration::days(1)).and_hms_opt(0, 0, 0);
9479    let Some(next_day) = next_day else {
9480        return ConditionResult::Unknown;
9481    };
9482    let judge = |path: &[(&str, usize)]| -> Option<ConditionResult> {
9483        let first_period = ctx
9484            .find_segments_at(path, "RFF")
9485            .iter()
9486            .any(|rff| rff.get_component(0, 2) == "1");
9487        if !first_period {
9488            return None;
9489        }
9490        let start = ctx
9491            .find_segments_at(path, "DTM")
9492            .iter()
9493            .find(|dtm| dtm.get_component(0, 0) == "Z25")
9494            .and_then(|dtm| german_local_time(dtm.get_component(0, 1)));
9495        Some(match start {
9496            Some(start) => ConditionResult::from(holds(start, next_day)),
9497            None => ConditionResult::Unknown,
9498        })
9499    };
9500    if let Some(scope) = ctx.enclosing_scope("SG6") {
9501        return judge(scope).unwrap_or(ConditionResult::False);
9502    }
9503    if ctx.navigator().is_none() {
9504        return ConditionResult::Unknown;
9505    }
9506    let mut result = ConditionResult::False;
9507    for sg4 in 0..ctx.group_instance_count(&["SG4"]) {
9508        for sg6 in 0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6") {
9509            match judge(&[("SG4", sg4), ("SG6", sg6)]) {
9510                Some(ConditionResult::False) => return ConditionResult::False,
9511                Some(ConditionResult::Unknown) => result = ConditionResult::Unknown,
9512                Some(ConditionResult::True) if result == ConditionResult::False => {
9513                    result = ConditionResult::True
9514                }
9515                _ => {}
9516            }
9517        }
9518    }
9519    result
9520}
9521
9522/// [134]/[135]/[401]: the DTM+Z25 of an SG6 whose RFF has one of
9523/// `qualifiers` and a Zeitraum-ID (DE1156) other than "1" must equal the
9524/// DTM+Z26 of the SG6 with one of `qualifiers` and the next lower
9525/// Zeitraum-ID. `False` for an SG6 with another qualifier or Zeitraum-ID 1 —
9526/// [131] and the sibling conditions judge those — and when there is no such
9527/// earlier SG6.
9528///
9529/// "Dieses DTM+Z25" is the one of the enclosing SG6, compared with the SG6s
9530/// of its SG4. Without a scope, every such SG6 must pass.
9531// HAND-EDITED
9532fn continues_previous_period(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9533    // (Zeitraum-ID, DTM+Z25, DTM+Z26) of the SG6 at `path`, when its RFF
9534    // has one of `qualifiers`.
9535    let period = |path: &[(&str, usize)]| -> Option<(u32, Option<String>, Option<String>)> {
9536        let id = ctx.find_segments_at(path, "RFF").iter().find_map(|rff| {
9537            qualifiers
9538                .contains(&rff.get_component(0, 0))
9539                .then(|| rff.get_component(0, 2).parse::<u32>().ok())
9540                .flatten()
9541        })?;
9542        let dtms = ctx.find_segments_at(path, "DTM");
9543        let date = |qualifier: &str| {
9544            dtms.iter()
9545                .find(|dtm| dtm.get_component(0, 0) == qualifier)
9546                .map(|dtm| dtm.get_component(0, 1).to_string())
9547                .filter(|v| !v.is_empty())
9548        };
9549        Some((id, date("Z25"), date("Z26")))
9550    };
9551    let same_instant = |a: &str, b: &str| match (german_local_time(a), german_local_time(b)) {
9552        (Some(a), Some(b)) => a == b,
9553        _ => a == b,
9554    };
9555    let judge = |sg4: usize, sg6: usize| -> Option<ConditionResult> {
9556        let (id, start, _) = period(&[("SG4", sg4), ("SG6", sg6)])?;
9557        if id == 1 {
9558            return None;
9559        }
9560        let previous = (0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6"))
9561            .filter_map(|other| period(&[("SG4", sg4), ("SG6", other)]))
9562            .filter(|(other, _, _)| *other < id)
9563            .max_by_key(|(other, _, _)| *other);
9564        Some(match (start, previous) {
9565            (None, _) => ConditionResult::Unknown,
9566            (Some(_), None) | (Some(_), Some((_, _, None))) => ConditionResult::False,
9567            (Some(start), Some((_, _, Some(end)))) => {
9568                ConditionResult::from(same_instant(&start, &end))
9569            }
9570        })
9571    };
9572    if let Some(scope) = ctx.enclosing_scope("SG6") {
9573        let at = |id: &str| scope.iter().find(|(g, _)| *g == id).map(|(_, i)| *i);
9574        return match (at("SG4"), at("SG6")) {
9575            (Some(sg4), Some(sg6)) => judge(sg4, sg6).unwrap_or(ConditionResult::False),
9576            _ => ConditionResult::Unknown,
9577        };
9578    }
9579    if ctx.navigator().is_none() {
9580        return ConditionResult::Unknown;
9581    }
9582    let mut result = ConditionResult::False;
9583    for sg4 in 0..ctx.group_instance_count(&["SG4"]) {
9584        for sg6 in 0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6") {
9585            match judge(sg4, sg6) {
9586                Some(ConditionResult::False) => return ConditionResult::False,
9587                Some(ConditionResult::Unknown) => result = ConditionResult::Unknown,
9588                Some(ConditionResult::True) if result == ConditionResult::False => {
9589                    result = ConditionResult::True
9590                }
9591                _ => {}
9592            }
9593        }
9594    }
9595    result
9596}