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::sg4_instances;
16use crate::eval::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
17// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
18// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
19use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
20
21/// Generated condition evaluator for UTILMD_Strom FV2504.
22pub struct UtilmdStromConditionEvaluatorFV2504 {
23    // External condition IDs that require runtime context.
24    external_conditions: std::collections::HashSet<u32>,
25}
26
27impl Default for UtilmdStromConditionEvaluatorFV2504 {
28    fn default() -> Self {
29        let mut external_conditions = std::collections::HashSet::new();
30        external_conditions.insert(1);
31        external_conditions.insert(4);
32        external_conditions.insert(5);
33        external_conditions.insert(6);
34        external_conditions.insert(8);
35        external_conditions.insert(9);
36        external_conditions.insert(14);
37        external_conditions.insert(31);
38        external_conditions.insert(33);
39        external_conditions.insert(36);
40        external_conditions.insert(38);
41        external_conditions.insert(39);
42        external_conditions.insert(40);
43        external_conditions.insert(75);
44        external_conditions.insert(98);
45        external_conditions.insert(99);
46        external_conditions.insert(151);
47        external_conditions.insert(233);
48        external_conditions.insert(241);
49        external_conditions.insert(251);
50        external_conditions.insert(292);
51        external_conditions.insert(314);
52        external_conditions.insert(315);
53        external_conditions.insert(323);
54        external_conditions.insert(412);
55        external_conditions.insert(425);
56        external_conditions.insert(426);
57        external_conditions.insert(427);
58        external_conditions.insert(428);
59        external_conditions.insert(429);
60        external_conditions.insert(433);
61        external_conditions.insert(435);
62        external_conditions.insert(453);
63        external_conditions.insert(459);
64        external_conditions.insert(460);
65        external_conditions.insert(706);
66        external_conditions.insert(717);
67        external_conditions.insert(2011);
68        Self {
69            external_conditions,
70        }
71    }
72}
73
74impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
75    fn message_type(&self) -> &str {
76        "UTILMD_Strom"
77    }
78
79    fn format_version(&self) -> &str {
80        "FV2504"
81    }
82
83    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
84        match condition {
85            1 => self.evaluate_1(ctx),
86            4 => self.evaluate_4(ctx),
87            5 => self.evaluate_5(ctx),
88            6 => self.evaluate_6(ctx),
89            7 => self.evaluate_7(ctx),
90            8 => self.evaluate_8(ctx),
91            9 => self.evaluate_9(ctx),
92            10 => self.evaluate_10(ctx),
93            11 => self.evaluate_11(ctx),
94            12 => self.evaluate_12(ctx),
95            13 => self.evaluate_13(ctx),
96            14 => self.evaluate_14(ctx),
97            15 => self.evaluate_15(ctx),
98            16 => self.evaluate_16(ctx),
99            17 => self.evaluate_17(ctx),
100            18 => self.evaluate_18(ctx),
101            19 => self.evaluate_19(ctx),
102            20 => self.evaluate_20(ctx),
103            21 => self.evaluate_21(ctx),
104            22 => self.evaluate_22(ctx),
105            23 => self.evaluate_23(ctx),
106            24 => self.evaluate_24(ctx),
107            25 => self.evaluate_25(ctx),
108            27 => self.evaluate_27(ctx),
109            30 => self.evaluate_30(ctx),
110            31 => self.evaluate_31(ctx),
111            32 => self.evaluate_32(ctx),
112            33 => self.evaluate_33(ctx),
113            34 => self.evaluate_34(ctx),
114            35 => self.evaluate_35(ctx),
115            36 => self.evaluate_36(ctx),
116            37 => self.evaluate_37(ctx),
117            38 => self.evaluate_38(ctx),
118            39 => self.evaluate_39(ctx),
119            40 => self.evaluate_40(ctx),
120            41 => self.evaluate_41(ctx),
121            42 => self.evaluate_42(ctx),
122            43 => self.evaluate_43(ctx),
123            44 => self.evaluate_44(ctx),
124            45 => self.evaluate_45(ctx),
125            46 => self.evaluate_46(ctx),
126            47 => self.evaluate_47(ctx),
127            48 => self.evaluate_48(ctx),
128            49 => self.evaluate_49(ctx),
129            50 => self.evaluate_50(ctx),
130            51 => self.evaluate_51(ctx),
131            52 => self.evaluate_52(ctx),
132            53 => self.evaluate_53(ctx),
133            54 => self.evaluate_54(ctx),
134            55 => self.evaluate_55(ctx),
135            56 => self.evaluate_56(ctx),
136            57 => self.evaluate_57(ctx),
137            58 => self.evaluate_58(ctx),
138            60 => self.evaluate_60(ctx),
139            61 => self.evaluate_61(ctx),
140            62 => self.evaluate_62(ctx),
141            63 => self.evaluate_63(ctx),
142            64 => self.evaluate_64(ctx),
143            65 => self.evaluate_65(ctx),
144            66 => self.evaluate_66(ctx),
145            67 => self.evaluate_67(ctx),
146            68 => self.evaluate_68(ctx),
147            69 => self.evaluate_69(ctx),
148            70 => self.evaluate_70(ctx),
149            71 => self.evaluate_71(ctx),
150            72 => self.evaluate_72(ctx),
151            73 => self.evaluate_73(ctx),
152            74 => self.evaluate_74(ctx),
153            75 => self.evaluate_75(ctx),
154            76 => self.evaluate_76(ctx),
155            77 => self.evaluate_77(ctx),
156            78 => self.evaluate_78(ctx),
157            83 => self.evaluate_83(ctx),
158            84 => self.evaluate_84(ctx),
159            85 => self.evaluate_85(ctx),
160            86 => self.evaluate_86(ctx),
161            87 => self.evaluate_87(ctx),
162            88 => self.evaluate_88(ctx),
163            89 => self.evaluate_89(ctx),
164            90 => self.evaluate_90(ctx),
165            91 => self.evaluate_91(ctx),
166            92 => self.evaluate_92(ctx),
167            93 => self.evaluate_93(ctx),
168            94 => self.evaluate_94(ctx),
169            95 => self.evaluate_95(ctx),
170            96 => self.evaluate_96(ctx),
171            97 => self.evaluate_97(ctx),
172            98 => self.evaluate_98(ctx),
173            99 => self.evaluate_99(ctx),
174            100 => self.evaluate_100(ctx),
175            101 => self.evaluate_101(ctx),
176            102 => self.evaluate_102(ctx),
177            103 => self.evaluate_103(ctx),
178            104 => self.evaluate_104(ctx),
179            105 => self.evaluate_105(ctx),
180            106 => self.evaluate_106(ctx),
181            107 => self.evaluate_107(ctx),
182            108 => self.evaluate_108(ctx),
183            110 => self.evaluate_110(ctx),
184            111 => self.evaluate_111(ctx),
185            112 => self.evaluate_112(ctx),
186            113 => self.evaluate_113(ctx),
187            114 => self.evaluate_114(ctx),
188            115 => self.evaluate_115(ctx),
189            116 => self.evaluate_116(ctx),
190            117 => self.evaluate_117(ctx),
191            118 => self.evaluate_118(ctx),
192            119 => self.evaluate_119(ctx),
193            120 => self.evaluate_120(ctx),
194            121 => self.evaluate_121(ctx),
195            122 => self.evaluate_122(ctx),
196            123 => self.evaluate_123(ctx),
197            124 => self.evaluate_124(ctx),
198            125 => self.evaluate_125(ctx),
199            126 => self.evaluate_126(ctx),
200            127 => self.evaluate_127(ctx),
201            128 => self.evaluate_128(ctx),
202            129 => self.evaluate_129(ctx),
203            130 => self.evaluate_130(ctx),
204            131 => self.evaluate_131(ctx),
205            132 => self.evaluate_132(ctx),
206            133 => self.evaluate_133(ctx),
207            134 => self.evaluate_134(ctx),
208            135 => self.evaluate_135(ctx),
209            136 => self.evaluate_136(ctx),
210            137 => self.evaluate_137(ctx),
211            138 => self.evaluate_138(ctx),
212            139 => self.evaluate_139(ctx),
213            140 => self.evaluate_140(ctx),
214            141 => self.evaluate_141(ctx),
215            142 => self.evaluate_142(ctx),
216            143 => self.evaluate_143(ctx),
217            144 => self.evaluate_144(ctx),
218            145 => self.evaluate_145(ctx),
219            146 => self.evaluate_146(ctx),
220            147 => self.evaluate_147(ctx),
221            148 => self.evaluate_148(ctx),
222            149 => self.evaluate_149(ctx),
223            150 => self.evaluate_150(ctx),
224            151 => self.evaluate_151(ctx),
225            152 => self.evaluate_152(ctx),
226            153 => self.evaluate_153(ctx),
227            156 => self.evaluate_156(ctx),
228            161 => self.evaluate_161(ctx),
229            162 => self.evaluate_162(ctx),
230            163 => self.evaluate_163(ctx),
231            164 => self.evaluate_164(ctx),
232            165 => self.evaluate_165(ctx),
233            166 => self.evaluate_166(ctx),
234            167 => self.evaluate_167(ctx),
235            170 => self.evaluate_170(ctx),
236            172 => self.evaluate_172(ctx),
237            173 => self.evaluate_173(ctx),
238            174 => self.evaluate_174(ctx),
239            175 => self.evaluate_175(ctx),
240            176 => self.evaluate_176(ctx),
241            177 => self.evaluate_177(ctx),
242            178 => self.evaluate_178(ctx),
243            179 => self.evaluate_179(ctx),
244            180 => self.evaluate_180(ctx),
245            184 => self.evaluate_184(ctx),
246            190 => self.evaluate_190(ctx),
247            191 => self.evaluate_191(ctx),
248            192 => self.evaluate_192(ctx),
249            193 => self.evaluate_193(ctx),
250            194 => self.evaluate_194(ctx),
251            195 => self.evaluate_195(ctx),
252            196 => self.evaluate_196(ctx),
253            197 => self.evaluate_197(ctx),
254            198 => self.evaluate_198(ctx),
255            199 => self.evaluate_199(ctx),
256            201 => self.evaluate_201(ctx),
257            202 => self.evaluate_202(ctx),
258            203 => self.evaluate_203(ctx),
259            204 => self.evaluate_204(ctx),
260            205 => self.evaluate_205(ctx),
261            206 => self.evaluate_206(ctx),
262            209 => self.evaluate_209(ctx),
263            210 => self.evaluate_210(ctx),
264            212 => self.evaluate_212(ctx),
265            213 => self.evaluate_213(ctx),
266            215 => self.evaluate_215(ctx),
267            216 => self.evaluate_216(ctx),
268            217 => self.evaluate_217(ctx),
269            219 => self.evaluate_219(ctx),
270            220 => self.evaluate_220(ctx),
271            221 => self.evaluate_221(ctx),
272            223 => self.evaluate_223(ctx),
273            224 => self.evaluate_224(ctx),
274            227 => self.evaluate_227(ctx),
275            229 => self.evaluate_229(ctx),
276            232 => self.evaluate_232(ctx),
277            233 => self.evaluate_233(ctx),
278            234 => self.evaluate_234(ctx),
279            237 => self.evaluate_237(ctx),
280            238 => self.evaluate_238(ctx),
281            239 => self.evaluate_239(ctx),
282            240 => self.evaluate_240(ctx),
283            241 => self.evaluate_241(ctx),
284            243 => self.evaluate_243(ctx),
285            244 => self.evaluate_244(ctx),
286            248 => self.evaluate_248(ctx),
287            249 => self.evaluate_249(ctx),
288            251 => self.evaluate_251(ctx),
289            252 => self.evaluate_252(ctx),
290            254 => self.evaluate_254(ctx),
291            255 => self.evaluate_255(ctx),
292            256 => self.evaluate_256(ctx),
293            257 => self.evaluate_257(ctx),
294            259 => self.evaluate_259(ctx),
295            261 => self.evaluate_261(ctx),
296            262 => self.evaluate_262(ctx),
297            265 => self.evaluate_265(ctx),
298            266 => self.evaluate_266(ctx),
299            267 => self.evaluate_267(ctx),
300            268 => self.evaluate_268(ctx),
301            269 => self.evaluate_269(ctx),
302            270 => self.evaluate_270(ctx),
303            273 => self.evaluate_273(ctx),
304            279 => self.evaluate_279(ctx),
305            280 => self.evaluate_280(ctx),
306            282 => self.evaluate_282(ctx),
307            284 => self.evaluate_284(ctx),
308            285 => self.evaluate_285(ctx),
309            286 => self.evaluate_286(ctx),
310            287 => self.evaluate_287(ctx),
311            288 => self.evaluate_288(ctx),
312            291 => self.evaluate_291(ctx),
313            292 => self.evaluate_292(ctx),
314            293 => self.evaluate_293(ctx),
315            294 => self.evaluate_294(ctx),
316            295 => self.evaluate_295(ctx),
317            296 => self.evaluate_296(ctx),
318            297 => self.evaluate_297(ctx),
319            298 => self.evaluate_298(ctx),
320            299 => self.evaluate_299(ctx),
321            300 => self.evaluate_300(ctx),
322            301 => self.evaluate_301(ctx),
323            302 => self.evaluate_302(ctx),
324            303 => self.evaluate_303(ctx),
325            304 => self.evaluate_304(ctx),
326            305 => self.evaluate_305(ctx),
327            306 => self.evaluate_306(ctx),
328            307 => self.evaluate_307(ctx),
329            309 => self.evaluate_309(ctx),
330            314 => self.evaluate_314(ctx),
331            315 => self.evaluate_315(ctx),
332            316 => self.evaluate_316(ctx),
333            317 => self.evaluate_317(ctx),
334            318 => self.evaluate_318(ctx),
335            321 => self.evaluate_321(ctx),
336            322 => self.evaluate_322(ctx),
337            323 => self.evaluate_323(ctx),
338            327 => self.evaluate_327(ctx),
339            329 => self.evaluate_329(ctx),
340            331 => self.evaluate_331(ctx),
341            332 => self.evaluate_332(ctx),
342            333 => self.evaluate_333(ctx),
343            334 => self.evaluate_334(ctx),
344            335 => self.evaluate_335(ctx),
345            336 => self.evaluate_336(ctx),
346            337 => self.evaluate_337(ctx),
347            338 => self.evaluate_338(ctx),
348            339 => self.evaluate_339(ctx),
349            340 => self.evaluate_340(ctx),
350            341 => self.evaluate_341(ctx),
351            342 => self.evaluate_342(ctx),
352            344 => self.evaluate_344(ctx),
353            345 => self.evaluate_345(ctx),
354            346 => self.evaluate_346(ctx),
355            347 => self.evaluate_347(ctx),
356            348 => self.evaluate_348(ctx),
357            349 => self.evaluate_349(ctx),
358            350 => self.evaluate_350(ctx),
359            351 => self.evaluate_351(ctx),
360            352 => self.evaluate_352(ctx),
361            355 => self.evaluate_355(ctx),
362            356 => self.evaluate_356(ctx),
363            357 => self.evaluate_357(ctx),
364            358 => self.evaluate_358(ctx),
365            359 => self.evaluate_359(ctx),
366            360 => self.evaluate_360(ctx),
367            363 => self.evaluate_363(ctx),
368            365 => self.evaluate_365(ctx),
369            366 => self.evaluate_366(ctx),
370            367 => self.evaluate_367(ctx),
371            368 => self.evaluate_368(ctx),
372            370 => self.evaluate_370(ctx),
373            371 => self.evaluate_371(ctx),
374            372 => self.evaluate_372(ctx),
375            373 => self.evaluate_373(ctx),
376            375 => self.evaluate_375(ctx),
377            376 => self.evaluate_376(ctx),
378            377 => self.evaluate_377(ctx),
379            378 => self.evaluate_378(ctx),
380            379 => self.evaluate_379(ctx),
381            380 => self.evaluate_380(ctx),
382            384 => self.evaluate_384(ctx),
383            386 => self.evaluate_386(ctx),
384            387 => self.evaluate_387(ctx),
385            388 => self.evaluate_388(ctx),
386            391 => self.evaluate_391(ctx),
387            392 => self.evaluate_392(ctx),
388            393 => self.evaluate_393(ctx),
389            394 => self.evaluate_394(ctx),
390            395 => self.evaluate_395(ctx),
391            396 => self.evaluate_396(ctx),
392            397 => self.evaluate_397(ctx),
393            398 => self.evaluate_398(ctx),
394            399 => self.evaluate_399(ctx),
395            401 => self.evaluate_401(ctx),
396            402 => self.evaluate_402(ctx),
397            403 => self.evaluate_403(ctx),
398            404 => self.evaluate_404(ctx),
399            405 => self.evaluate_405(ctx),
400            406 => self.evaluate_406(ctx),
401            407 => self.evaluate_407(ctx),
402            408 => self.evaluate_408(ctx),
403            409 => self.evaluate_409(ctx),
404            410 => self.evaluate_410(ctx),
405            411 => self.evaluate_411(ctx),
406            412 => self.evaluate_412(ctx),
407            413 => self.evaluate_413(ctx),
408            414 => self.evaluate_414(ctx),
409            415 => self.evaluate_415(ctx),
410            416 => self.evaluate_416(ctx),
411            417 => self.evaluate_417(ctx),
412            419 => self.evaluate_419(ctx),
413            420 => self.evaluate_420(ctx),
414            421 => self.evaluate_421(ctx),
415            425 => self.evaluate_425(ctx),
416            426 => self.evaluate_426(ctx),
417            427 => self.evaluate_427(ctx),
418            428 => self.evaluate_428(ctx),
419            429 => self.evaluate_429(ctx),
420            430 => self.evaluate_430(ctx),
421            431 => self.evaluate_431(ctx),
422            432 => self.evaluate_432(ctx),
423            433 => self.evaluate_433(ctx),
424            435 => self.evaluate_435(ctx),
425            436 => self.evaluate_436(ctx),
426            437 => self.evaluate_437(ctx),
427            438 => self.evaluate_438(ctx),
428            440 => self.evaluate_440(ctx),
429            441 => self.evaluate_441(ctx),
430            442 => self.evaluate_442(ctx),
431            444 => self.evaluate_444(ctx),
432            445 => self.evaluate_445(ctx),
433            446 => self.evaluate_446(ctx),
434            447 => self.evaluate_447(ctx),
435            448 => self.evaluate_448(ctx),
436            449 => self.evaluate_449(ctx),
437            450 => self.evaluate_450(ctx),
438            451 => self.evaluate_451(ctx),
439            452 => self.evaluate_452(ctx),
440            453 => self.evaluate_453(ctx),
441            454 => self.evaluate_454(ctx),
442            455 => self.evaluate_455(ctx),
443            456 => self.evaluate_456(ctx),
444            457 => self.evaluate_457(ctx),
445            458 => self.evaluate_458(ctx),
446            459 => self.evaluate_459(ctx),
447            460 => self.evaluate_460(ctx),
448            461 => self.evaluate_461(ctx),
449            462 => self.evaluate_462(ctx),
450            463 => self.evaluate_463(ctx),
451            465 => self.evaluate_465(ctx),
452            466 => self.evaluate_466(ctx),
453            467 => self.evaluate_467(ctx),
454            468 => self.evaluate_468(ctx),
455            469 => self.evaluate_469(ctx),
456            470 => self.evaluate_470(ctx),
457            471 => self.evaluate_471(ctx),
458            472 => self.evaluate_472(ctx),
459            473 => self.evaluate_473(ctx),
460            474 => self.evaluate_474(ctx),
461            475 => self.evaluate_475(ctx),
462            476 => self.evaluate_476(ctx),
463            477 => self.evaluate_477(ctx),
464            478 => self.evaluate_478(ctx),
465            479 => self.evaluate_479(ctx),
466            480 => self.evaluate_480(ctx),
467            481 => self.evaluate_481(ctx),
468            483 => self.evaluate_483(ctx),
469            484 => self.evaluate_484(ctx),
470            487 => self.evaluate_487(ctx),
471            489 => self.evaluate_489(ctx),
472            490 => self.evaluate_490(ctx),
473            491 => self.evaluate_491(ctx),
474            494 => self.evaluate_494(ctx),
475            500 => self.evaluate_500(ctx),
476            501 => self.evaluate_501(ctx),
477            502 => self.evaluate_502(ctx),
478            503 => self.evaluate_503(ctx),
479            504 => self.evaluate_504(ctx),
480            505 => self.evaluate_505(ctx),
481            506 => self.evaluate_506(ctx),
482            507 => self.evaluate_507(ctx),
483            508 => self.evaluate_508(ctx),
484            509 => self.evaluate_509(ctx),
485            510 => self.evaluate_510(ctx),
486            511 => self.evaluate_511(ctx),
487            512 => self.evaluate_512(ctx),
488            513 => self.evaluate_513(ctx),
489            514 => self.evaluate_514(ctx),
490            515 => self.evaluate_515(ctx),
491            516 => self.evaluate_516(ctx),
492            517 => self.evaluate_517(ctx),
493            518 => self.evaluate_518(ctx),
494            519 => self.evaluate_519(ctx),
495            520 => self.evaluate_520(ctx),
496            521 => self.evaluate_521(ctx),
497            522 => self.evaluate_522(ctx),
498            523 => self.evaluate_523(ctx),
499            524 => self.evaluate_524(ctx),
500            525 => self.evaluate_525(ctx),
501            526 => self.evaluate_526(ctx),
502            527 => self.evaluate_527(ctx),
503            528 => self.evaluate_528(ctx),
504            529 => self.evaluate_529(ctx),
505            530 => self.evaluate_530(ctx),
506            531 => self.evaluate_531(ctx),
507            532 => self.evaluate_532(ctx),
508            533 => self.evaluate_533(ctx),
509            555 => self.evaluate_555(ctx),
510            556 => self.evaluate_556(ctx),
511            558 => self.evaluate_558(ctx),
512            559 => self.evaluate_559(ctx),
513            563 => self.evaluate_563(ctx),
514            566 => self.evaluate_566(ctx),
515            567 => self.evaluate_567(ctx),
516            568 => self.evaluate_568(ctx),
517            569 => self.evaluate_569(ctx),
518            570 => self.evaluate_570(ctx),
519            572 => self.evaluate_572(ctx),
520            576 => self.evaluate_576(ctx),
521            577 => self.evaluate_577(ctx),
522            579 => self.evaluate_579(ctx),
523            580 => self.evaluate_580(ctx),
524            581 => self.evaluate_581(ctx),
525            586 => self.evaluate_586(ctx),
526            590 => self.evaluate_590(ctx),
527            594 => self.evaluate_594(ctx),
528            599 => self.evaluate_599(ctx),
529            601 => self.evaluate_601(ctx),
530            606 => self.evaluate_606(ctx),
531            609 => self.evaluate_609(ctx),
532            611 => self.evaluate_611(ctx),
533            614 => self.evaluate_614(ctx),
534            617 => self.evaluate_617(ctx),
535            618 => self.evaluate_618(ctx),
536            619 => self.evaluate_619(ctx),
537            621 => self.evaluate_621(ctx),
538            622 => self.evaluate_622(ctx),
539            623 => self.evaluate_623(ctx),
540            630 => self.evaluate_630(ctx),
541            631 => self.evaluate_631(ctx),
542            632 => self.evaluate_632(ctx),
543            637 => self.evaluate_637(ctx),
544            638 => self.evaluate_638(ctx),
545            639 => self.evaluate_639(ctx),
546            640 => self.evaluate_640(ctx),
547            641 => self.evaluate_641(ctx),
548            642 => self.evaluate_642(ctx),
549            643 => self.evaluate_643(ctx),
550            645 => self.evaluate_645(ctx),
551            646 => self.evaluate_646(ctx),
552            647 => self.evaluate_647(ctx),
553            648 => self.evaluate_648(ctx),
554            651 => self.evaluate_651(ctx),
555            653 => self.evaluate_653(ctx),
556            655 => self.evaluate_655(ctx),
557            659 => self.evaluate_659(ctx),
558            660 => self.evaluate_660(ctx),
559            662 => self.evaluate_662(ctx),
560            663 => self.evaluate_663(ctx),
561            664 => self.evaluate_664(ctx),
562            665 => self.evaluate_665(ctx),
563            667 => self.evaluate_667(ctx),
564            668 => self.evaluate_668(ctx),
565            670 => self.evaluate_670(ctx),
566            671 => self.evaluate_671(ctx),
567            672 => self.evaluate_672(ctx),
568            673 => self.evaluate_673(ctx),
569            674 => self.evaluate_674(ctx),
570            675 => self.evaluate_675(ctx),
571            677 => self.evaluate_677(ctx),
572            678 => self.evaluate_678(ctx),
573            679 => self.evaluate_679(ctx),
574            680 => self.evaluate_680(ctx),
575            682 => self.evaluate_682(ctx),
576            683 => self.evaluate_683(ctx),
577            684 => self.evaluate_684(ctx),
578            685 => self.evaluate_685(ctx),
579            687 => self.evaluate_687(ctx),
580            688 => self.evaluate_688(ctx),
581            689 => self.evaluate_689(ctx),
582            690 => self.evaluate_690(ctx),
583            693 => self.evaluate_693(ctx),
584            694 => self.evaluate_694(ctx),
585            695 => self.evaluate_695(ctx),
586            696 => self.evaluate_696(ctx),
587            698 => self.evaluate_698(ctx),
588            699 => self.evaluate_699(ctx),
589            700 => self.evaluate_700(ctx),
590            704 => self.evaluate_704(ctx),
591            705 => self.evaluate_705(ctx),
592            706 => self.evaluate_706(ctx),
593            707 => self.evaluate_707(ctx),
594            708 => self.evaluate_708(ctx),
595            709 => self.evaluate_709(ctx),
596            710 => self.evaluate_710(ctx),
597            711 => self.evaluate_711(ctx),
598            712 => self.evaluate_712(ctx),
599            713 => self.evaluate_713(ctx),
600            714 => self.evaluate_714(ctx),
601            715 => self.evaluate_715(ctx),
602            716 => self.evaluate_716(ctx),
603            717 => self.evaluate_717(ctx),
604            718 => self.evaluate_718(ctx),
605            719 => self.evaluate_719(ctx),
606            902 => self.evaluate_902(ctx),
607            910 => self.evaluate_910(ctx),
608            914 => self.evaluate_914(ctx),
609            922 => self.evaluate_922(ctx),
610            926 => self.evaluate_926(ctx),
611            930 => self.evaluate_930(ctx),
612            931 => self.evaluate_931(ctx),
613            932 => self.evaluate_932(ctx),
614            933 => self.evaluate_933(ctx),
615            937 => self.evaluate_937(ctx),
616            938 => self.evaluate_938(ctx),
617            939 => self.evaluate_939(ctx),
618            940 => self.evaluate_940(ctx),
619            942 => self.evaluate_942(ctx),
620            943 => self.evaluate_943(ctx),
621            946 => self.evaluate_946(ctx),
622            948 => self.evaluate_948(ctx),
623            950 => self.evaluate_950(ctx),
624            951 => self.evaluate_951(ctx),
625            952 => self.evaluate_952(ctx),
626            955 => self.evaluate_955(ctx),
627            957 => self.evaluate_957(ctx),
628            960 => self.evaluate_960(ctx),
629            961 => self.evaluate_961(ctx),
630            967 => self.evaluate_967(ctx),
631            2001 => self.evaluate_2001(ctx),
632            2002 => self.evaluate_2002(ctx),
633            2003 => self.evaluate_2003(ctx),
634            2004 => self.evaluate_2004(ctx),
635            2005 => self.evaluate_2005(ctx),
636            2006 => self.evaluate_2006(ctx),
637            2007 => self.evaluate_2007(ctx),
638            2008 => self.evaluate_2008(ctx),
639            2009 => self.evaluate_2009(ctx),
640            2010 => self.evaluate_2010(ctx),
641            2011 => self.evaluate_2011(ctx),
642            2012 => self.evaluate_2012(ctx),
643            2013 => self.evaluate_2013(ctx),
644            2014 => self.evaluate_2014(ctx),
645            2015 => self.evaluate_2015(ctx),
646            2016 => self.evaluate_2016(ctx),
647            2017 => self.evaluate_2017(ctx),
648            2018 => self.evaluate_2018(ctx),
649            2060 => self.evaluate_2060(ctx),
650            2061 => self.evaluate_2061(ctx),
651            2071 => self.evaluate_2071(ctx),
652            2073 => self.evaluate_2073(ctx),
653            2075 => self.evaluate_2075(ctx),
654            2080 => self.evaluate_2080(ctx),
655            2095 => self.evaluate_2095(ctx),
656            2096 => self.evaluate_2096(ctx),
657            2119 => self.evaluate_2119(ctx),
658            2140 => self.evaluate_2140(ctx),
659            2182 => self.evaluate_2182(ctx),
660            2183 => self.evaluate_2183(ctx),
661            2207 => self.evaluate_2207(ctx),
662            2225 => self.evaluate_2225(ctx),
663            2236 => self.evaluate_2236(ctx),
664            2252 => self.evaluate_2252(ctx),
665            2261 => self.evaluate_2261(ctx),
666            2284 => self.evaluate_2284(ctx),
667            2286 => self.evaluate_2286(ctx),
668            2287 => self.evaluate_2287(ctx),
669            2288 => self.evaluate_2288(ctx),
670            2307 => self.evaluate_2307(ctx),
671            2308 => self.evaluate_2308(ctx),
672            2309 => self.evaluate_2309(ctx),
673            2310 => self.evaluate_2310(ctx),
674            2311 => self.evaluate_2311(ctx),
675            2312 => self.evaluate_2312(ctx),
676            2313 => self.evaluate_2313(ctx),
677            2317 => self.evaluate_2317(ctx),
678            2318 => self.evaluate_2318(ctx),
679            2344 => self.evaluate_2344(ctx),
680            2350 => self.evaluate_2350(ctx),
681            2351 => self.evaluate_2351(ctx),
682            2352 => self.evaluate_2352(ctx),
683            2356 => self.evaluate_2356(ctx),
684            2357 => self.evaluate_2357(ctx),
685            2358 => self.evaluate_2358(ctx),
686            2359 => self.evaluate_2359(ctx),
687            2360 => self.evaluate_2360(ctx),
688            2361 => self.evaluate_2361(ctx),
689            _ => ConditionResult::Unknown,
690        }
691    }
692
693    fn is_external(&self, condition: u32) -> bool {
694        self.external_conditions.contains(&condition)
695    }
696    fn is_known(&self, condition: u32) -> bool {
697        matches!(
698            condition,
699            1 | 4
700                | 5
701                | 6
702                | 7
703                | 8
704                | 9
705                | 10
706                | 11
707                | 12
708                | 13
709                | 14
710                | 15
711                | 16
712                | 17
713                | 18
714                | 19
715                | 20
716                | 21
717                | 22
718                | 23
719                | 24
720                | 25
721                | 27
722                | 30
723                | 31
724                | 32
725                | 33
726                | 34
727                | 35
728                | 36
729                | 37
730                | 38
731                | 39
732                | 40
733                | 41
734                | 42
735                | 43
736                | 44
737                | 45
738                | 46
739                | 47
740                | 48
741                | 49
742                | 50
743                | 51
744                | 52
745                | 53
746                | 54
747                | 55
748                | 56
749                | 57
750                | 58
751                | 60
752                | 61
753                | 62
754                | 63
755                | 64
756                | 65
757                | 66
758                | 67
759                | 68
760                | 69
761                | 70
762                | 71
763                | 72
764                | 73
765                | 74
766                | 75
767                | 76
768                | 77
769                | 78
770                | 83
771                | 84
772                | 85
773                | 86
774                | 87
775                | 88
776                | 89
777                | 90
778                | 91
779                | 92
780                | 93
781                | 94
782                | 95
783                | 96
784                | 97
785                | 98
786                | 99
787                | 100
788                | 101
789                | 102
790                | 103
791                | 104
792                | 105
793                | 106
794                | 107
795                | 108
796                | 110
797                | 111
798                | 112
799                | 113
800                | 114
801                | 115
802                | 116
803                | 117
804                | 118
805                | 119
806                | 120
807                | 121
808                | 122
809                | 123
810                | 124
811                | 125
812                | 126
813                | 127
814                | 128
815                | 129
816                | 130
817                | 131
818                | 132
819                | 133
820                | 134
821                | 135
822                | 136
823                | 137
824                | 138
825                | 139
826                | 140
827                | 141
828                | 142
829                | 143
830                | 144
831                | 145
832                | 146
833                | 147
834                | 148
835                | 149
836                | 150
837                | 151
838                | 152
839                | 153
840                | 156
841                | 161
842                | 162
843                | 163
844                | 164
845                | 165
846                | 166
847                | 167
848                | 170
849                | 172
850                | 173
851                | 174
852                | 175
853                | 176
854                | 177
855                | 178
856                | 179
857                | 180
858                | 184
859                | 190
860                | 191
861                | 192
862                | 193
863                | 194
864                | 195
865                | 196
866                | 197
867                | 198
868                | 199
869                | 201
870                | 202
871                | 203
872                | 204
873                | 205
874                | 206
875                | 209
876                | 210
877                | 212
878                | 213
879                | 215
880                | 216
881                | 217
882                | 219
883                | 220
884                | 221
885                | 223
886                | 224
887                | 227
888                | 229
889                | 232
890                | 233
891                | 234
892                | 237
893                | 238
894                | 239
895                | 240
896                | 241
897                | 243
898                | 244
899                | 248
900                | 249
901                | 251
902                | 252
903                | 254
904                | 255
905                | 256
906                | 257
907                | 259
908                | 261
909                | 262
910                | 265
911                | 266
912                | 267
913                | 268
914                | 269
915                | 270
916                | 273
917                | 279
918                | 280
919                | 282
920                | 284
921                | 285
922                | 286
923                | 287
924                | 288
925                | 291
926                | 292
927                | 293
928                | 294
929                | 295
930                | 296
931                | 297
932                | 298
933                | 299
934                | 300
935                | 301
936                | 302
937                | 303
938                | 304
939                | 305
940                | 306
941                | 307
942                | 309
943                | 314
944                | 315
945                | 316
946                | 317
947                | 318
948                | 321
949                | 322
950                | 323
951                | 327
952                | 329
953                | 331
954                | 332
955                | 333
956                | 334
957                | 335
958                | 336
959                | 337
960                | 338
961                | 339
962                | 340
963                | 341
964                | 342
965                | 344
966                | 345
967                | 346
968                | 347
969                | 348
970                | 349
971                | 350
972                | 351
973                | 352
974                | 355
975                | 356
976                | 357
977                | 358
978                | 359
979                | 360
980                | 363
981                | 365
982                | 366
983                | 367
984                | 368
985                | 370
986                | 371
987                | 372
988                | 373
989                | 375
990                | 376
991                | 377
992                | 378
993                | 379
994                | 380
995                | 384
996                | 386
997                | 387
998                | 388
999                | 391
1000                | 392
1001                | 393
1002                | 394
1003                | 395
1004                | 396
1005                | 397
1006                | 398
1007                | 399
1008                | 401
1009                | 402
1010                | 403
1011                | 404
1012                | 405
1013                | 406
1014                | 407
1015                | 408
1016                | 409
1017                | 410
1018                | 411
1019                | 412
1020                | 413
1021                | 414
1022                | 415
1023                | 416
1024                | 417
1025                | 419
1026                | 420
1027                | 421
1028                | 425
1029                | 426
1030                | 427
1031                | 428
1032                | 429
1033                | 430
1034                | 431
1035                | 432
1036                | 433
1037                | 435
1038                | 436
1039                | 437
1040                | 438
1041                | 440
1042                | 441
1043                | 442
1044                | 444
1045                | 445
1046                | 446
1047                | 447
1048                | 448
1049                | 449
1050                | 450
1051                | 451
1052                | 452
1053                | 453
1054                | 454
1055                | 455
1056                | 456
1057                | 457
1058                | 458
1059                | 459
1060                | 460
1061                | 461
1062                | 462
1063                | 463
1064                | 465
1065                | 466
1066                | 467
1067                | 468
1068                | 469
1069                | 470
1070                | 471
1071                | 472
1072                | 473
1073                | 474
1074                | 475
1075                | 476
1076                | 477
1077                | 478
1078                | 479
1079                | 480
1080                | 481
1081                | 483
1082                | 484
1083                | 487
1084                | 489
1085                | 490
1086                | 491
1087                | 494
1088                | 500
1089                | 501
1090                | 502
1091                | 503
1092                | 504
1093                | 505
1094                | 506
1095                | 507
1096                | 508
1097                | 509
1098                | 510
1099                | 511
1100                | 512
1101                | 513
1102                | 514
1103                | 515
1104                | 516
1105                | 517
1106                | 518
1107                | 519
1108                | 520
1109                | 521
1110                | 522
1111                | 523
1112                | 524
1113                | 525
1114                | 526
1115                | 527
1116                | 528
1117                | 529
1118                | 530
1119                | 531
1120                | 532
1121                | 533
1122                | 555
1123                | 556
1124                | 558
1125                | 559
1126                | 563
1127                | 566
1128                | 567
1129                | 568
1130                | 569
1131                | 570
1132                | 572
1133                | 576
1134                | 577
1135                | 579
1136                | 580
1137                | 581
1138                | 586
1139                | 590
1140                | 594
1141                | 599
1142                | 601
1143                | 606
1144                | 609
1145                | 611
1146                | 614
1147                | 617
1148                | 618
1149                | 619
1150                | 621
1151                | 622
1152                | 623
1153                | 630
1154                | 631
1155                | 632
1156                | 637
1157                | 638
1158                | 639
1159                | 640
1160                | 641
1161                | 642
1162                | 643
1163                | 645
1164                | 646
1165                | 647
1166                | 648
1167                | 651
1168                | 653
1169                | 655
1170                | 659
1171                | 660
1172                | 662
1173                | 663
1174                | 664
1175                | 665
1176                | 667
1177                | 668
1178                | 670
1179                | 671
1180                | 672
1181                | 673
1182                | 674
1183                | 675
1184                | 677
1185                | 678
1186                | 679
1187                | 680
1188                | 682
1189                | 683
1190                | 684
1191                | 685
1192                | 687
1193                | 688
1194                | 689
1195                | 690
1196                | 693
1197                | 694
1198                | 695
1199                | 696
1200                | 698
1201                | 699
1202                | 700
1203                | 704
1204                | 705
1205                | 706
1206                | 707
1207                | 708
1208                | 709
1209                | 710
1210                | 711
1211                | 712
1212                | 713
1213                | 714
1214                | 715
1215                | 716
1216                | 717
1217                | 718
1218                | 719
1219                | 902
1220                | 910
1221                | 914
1222                | 922
1223                | 926
1224                | 930
1225                | 931
1226                | 932
1227                | 933
1228                | 937
1229                | 938
1230                | 939
1231                | 940
1232                | 942
1233                | 943
1234                | 946
1235                | 948
1236                | 950
1237                | 951
1238                | 952
1239                | 955
1240                | 957
1241                | 960
1242                | 961
1243                | 967
1244                | 2001
1245                | 2002
1246                | 2003
1247                | 2004
1248                | 2005
1249                | 2006
1250                | 2007
1251                | 2008
1252                | 2009
1253                | 2010
1254                | 2011
1255                | 2012
1256                | 2013
1257                | 2014
1258                | 2015
1259                | 2016
1260                | 2017
1261                | 2018
1262                | 2060
1263                | 2061
1264                | 2071
1265                | 2073
1266                | 2075
1267                | 2080
1268                | 2095
1269                | 2096
1270                | 2119
1271                | 2140
1272                | 2182
1273                | 2183
1274                | 2207
1275                | 2225
1276                | 2236
1277                | 2252
1278                | 2261
1279                | 2284
1280                | 2286
1281                | 2287
1282                | 2288
1283                | 2307
1284                | 2308
1285                | 2309
1286                | 2310
1287                | 2311
1288                | 2312
1289                | 2313
1290                | 2317
1291                | 2318
1292                | 2344
1293                | 2350
1294                | 2351
1295                | 2352
1296                | 2356
1297                | 2357
1298                | 2358
1299                | 2359
1300                | 2360
1301                | 2361
1302        )
1303    }
1304}
1305
1306impl UtilmdStromConditionEvaluatorFV2504 {
1307    /// [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...
1308    // 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)
1309    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1310        let nav = match ctx.navigator() {
1311            Some(n) => n,
1312            None => return ConditionResult::Unknown,
1313        };
1314        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1315        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1316        for i in 0..sg8_count {
1317            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1318            let is_z01 = seq_segs.iter().any(|s| {
1319                s.elements
1320                    .first()
1321                    .and_then(|e| e.first())
1322                    .is_some_and(|v| v == "Z01")
1323            });
1324            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1325                for seq in &seq_segs {
1326                    if seq
1327                        .elements
1328                        .first()
1329                        .and_then(|e| e.first())
1330                        .is_some_and(|v| v == "Z01")
1331                    {
1332                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1333                            if !zid.is_empty() {
1334                                qualifying_zeitraum_ids.push(zid.clone());
1335                            }
1336                        }
1337                    }
1338                }
1339            }
1340        }
1341        if qualifying_zeitraum_ids.is_empty() {
1342            return ConditionResult::Unknown;
1343        }
1344        ConditionResult::True
1345    }
1346
1347    /// [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...
1348    // 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)
1349    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1350        let nav = match ctx.navigator() {
1351            Some(n) => n,
1352            None => return ConditionResult::Unknown,
1353        };
1354        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1355        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1356        for i in 0..sg8_count {
1357            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1358            let is_z01 = seq_segs.iter().any(|s| {
1359                s.elements
1360                    .first()
1361                    .and_then(|e| e.first())
1362                    .is_some_and(|v| v == "Z01")
1363            });
1364            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1365                for seq in &seq_segs {
1366                    if seq
1367                        .elements
1368                        .first()
1369                        .and_then(|e| e.first())
1370                        .is_some_and(|v| v == "Z01")
1371                    {
1372                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1373                            if !zid.is_empty() {
1374                                qualifying_zeitraum_ids.push(zid.clone());
1375                            }
1376                        }
1377                    }
1378                }
1379            }
1380        }
1381        if qualifying_zeitraum_ids.is_empty() {
1382            return ConditionResult::Unknown;
1383        }
1384        ConditionResult::True
1385    }
1386
1387    /// [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...
1388    // 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)
1389    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1390        let nav = match ctx.navigator() {
1391            Some(n) => n,
1392            None => return ConditionResult::Unknown,
1393        };
1394        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1395        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1396        for i in 0..sg8_count {
1397            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1398            let is_z01 = seq_segs.iter().any(|s| {
1399                s.elements
1400                    .first()
1401                    .and_then(|e| e.first())
1402                    .is_some_and(|v| v == "Z01")
1403            });
1404            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1405                for seq in &seq_segs {
1406                    if seq
1407                        .elements
1408                        .first()
1409                        .and_then(|e| e.first())
1410                        .is_some_and(|v| v == "Z01")
1411                    {
1412                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1413                            if !zid.is_empty() {
1414                                qualifying_zeitraum_ids.push(zid.clone());
1415                            }
1416                        }
1417                    }
1418                }
1419            }
1420        }
1421        if qualifying_zeitraum_ids.is_empty() {
1422            return ConditionResult::Unknown;
1423        }
1424        ConditionResult::True
1425    }
1426
1427    /// [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...
1428    // 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)
1429    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1430        let nav = match ctx.navigator() {
1431            Some(n) => n,
1432            None => return ConditionResult::Unknown,
1433        };
1434        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1435        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1436        for i in 0..sg8_count {
1437            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1438            let is_z01 = seq_segs.iter().any(|s| {
1439                s.elements
1440                    .first()
1441                    .and_then(|e| e.first())
1442                    .is_some_and(|v| v == "Z01")
1443            });
1444            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1445                for seq in &seq_segs {
1446                    if seq
1447                        .elements
1448                        .first()
1449                        .and_then(|e| e.first())
1450                        .is_some_and(|v| v == "Z01")
1451                    {
1452                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1453                            if !zid.is_empty() {
1454                                qualifying_zeitraum_ids.push(zid.clone());
1455                            }
1456                        }
1457                    }
1458                }
1459            }
1460        }
1461        if qualifying_zeitraum_ids.is_empty() {
1462            return ConditionResult::Unknown;
1463        }
1464        ConditionResult::True
1465    }
1466
1467    /// [1] Wenn Aufteilung vorhanden
1468    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1469        ctx.external.evaluate("message_splitting")
1470    }
1471
1472    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1473    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1474        ctx.external.evaluate("recipient_is_lf")
1475    }
1476
1477    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1478    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1479        ctx.external.evaluate("sender_is_lf")
1480    }
1481
1482    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1483    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1484        ctx.external.evaluate("recipient_role_check")
1485    }
1486
1487    /// [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
1488    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1489        let sts_segs = ctx.find_segments("STS");
1490        let found = sts_segs.iter().any(|s| {
1491            s.elements
1492                .first()
1493                .and_then(|e| e.first())
1494                .is_some_and(|v| v == "7")
1495                && s.elements
1496                    .get(2)
1497                    .and_then(|e| e.first())
1498                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1499        });
1500        ConditionResult::from(found)
1501    }
1502
1503    /// [8] Wenn für Datenclearing erforderlich
1504    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1505        ctx.external.evaluate("data_clearing_required")
1506    }
1507
1508    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1509    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1510        ctx.external.evaluate("sender_role_check")
1511    }
1512
1513    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1514    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1515        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1516        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1517    }
1518
1519    /// [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
1520    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        let sts_segs = ctx.find_segments("STS");
1522        let found = sts_segs.iter().any(|s| {
1523            s.elements
1524                .first()
1525                .and_then(|e| e.first())
1526                .is_some_and(|v| v == "7")
1527                && s.elements
1528                    .get(2)
1529                    .and_then(|e| e.first())
1530                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1531        });
1532        ConditionResult::from(!found)
1533    }
1534
1535    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1536    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        let dtm_segs = ctx.find_segments("DTM");
1538        let found = dtm_segs.iter().any(|s| {
1539            s.elements
1540                .first()
1541                .and_then(|e| e.first())
1542                .is_some_and(|v| v == "471")
1543        });
1544        ConditionResult::from(!found)
1545    }
1546
1547    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1548    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1549        let sts_segs = ctx.find_segments("STS");
1550        let found = sts_segs.iter().any(|s| {
1551            s.elements
1552                .first()
1553                .and_then(|e| e.first())
1554                .is_some_and(|v| v == "E01")
1555                && s.elements
1556                    .get(2)
1557                    .and_then(|e| e.first())
1558                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1559        });
1560        ConditionResult::from(!found)
1561    }
1562
1563    /// [14] Wenn Datum bekannt
1564    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1565        ctx.external.evaluate("date_known")
1566    }
1567
1568    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1569    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1570        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1571        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1572        let found = seq_segs.iter().any(|s| {
1573            s.elements
1574                .first()
1575                .and_then(|e| e.first())
1576                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1577        });
1578        ConditionResult::from(found)
1579    }
1580
1581    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1582    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1583        let sts_segs = ctx.find_segments("STS");
1584        let found = sts_segs.iter().any(|s| {
1585            s.elements
1586                .first()
1587                .and_then(|e| e.first())
1588                .is_some_and(|v| v == "E01")
1589                && s.elements
1590                    .get(2)
1591                    .and_then(|e| e.first())
1592                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1593        });
1594        ConditionResult::from(found)
1595    }
1596
1597    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1598    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1599        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1600        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1601        let found = seq_segs.iter().any(|s| {
1602            s.elements
1603                .first()
1604                .and_then(|e| e.first())
1605                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1606        });
1607        ConditionResult::from(found)
1608    }
1609
1610    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1611    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1612        let dtm_segs = ctx.find_segments("DTM");
1613        let found = dtm_segs.iter().any(|s| {
1614            s.elements
1615                .first()
1616                .and_then(|e| e.first())
1617                .is_some_and(|v| v == "93")
1618        });
1619        ConditionResult::from(!found)
1620    }
1621
1622    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1623    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1624        // HAND-EDITED (#336): the SEQ alone says nothing about a Prognose auf Basis von Werten; the CCI of one of its
1625        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1626        if ctx.navigator().is_none() {
1627            let seq = ctx
1628                .find_segments("SEQ")
1629                .iter()
1630                .any(|s| ["Z01", "Z98"].contains(&s.get_element(0)));
1631            return ConditionResult::from(
1632                seq && sg4_instances::has_cci(
1633                    &ctx.find_segments("CCI")
1634                        .into_iter()
1635                        .cloned()
1636                        .collect::<Vec<_>>(),
1637                    "",
1638                    "ZC0",
1639                ),
1640            );
1641        }
1642        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01", "Z98"], "", "ZC0"))
1643    }
1644
1645    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1646    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1647        // HAND-EDITED (#336): the SEQ alone is every MaLo, consumption or not; the CCI of one of its
1648        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1649        if ctx.navigator().is_none() {
1650            let seq = ctx
1651                .find_segments("SEQ")
1652                .iter()
1653                .any(|s| ["Z01"].contains(&s.get_element(0)));
1654            return ConditionResult::from(
1655                seq && sg4_instances::has_cci(
1656                    &ctx.find_segments("CCI")
1657                        .into_iter()
1658                        .cloned()
1659                        .collect::<Vec<_>>(),
1660                    "Z30",
1661                    "Z07",
1662                ),
1663            );
1664        }
1665        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z07"))
1666    }
1667
1668    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1669    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1670        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1671        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1672        let found = cci_segs.iter().any(|s| {
1673            s.elements
1674                .get(2)
1675                .and_then(|e| e.first())
1676                .is_some_and(|v| v == "ZA6")
1677        });
1678        ConditionResult::from(found)
1679    }
1680
1681    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1682    // HAND-EDITED: the value is one of the Zeitraum-IDs (DE1156) of the SG6
1683    // RFF+Z47/Z48/Z49 of its SG4. The generated code compared those IDs with
1684    // the SG8 SEQ DE1050 and never looked at the value.
1685    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1686        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
1687            return ConditionResult::Unknown;
1688        };
1689        let found = ["Z47", "Z48", "Z49"].iter().any(|q| {
1690            ctx.scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, q)
1691                .iter()
1692                .any(|s| s.get_component(0, 2) == value)
1693        });
1694        ConditionResult::from(found)
1695    }
1696
1697    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1698    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1699        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1700        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1701        let found = sts_segs.iter().any(|s| {
1702            s.elements
1703                .first()
1704                .and_then(|e| e.first())
1705                .is_some_and(|v| v == "E01")
1706                && s.elements
1707                    .get(2)
1708                    .and_then(|e| e.first())
1709                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1710        });
1711        ConditionResult::from(found)
1712    }
1713
1714    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1715    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1716        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1717        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1718        let found = sts_segs.iter().any(|s| {
1719            s.elements
1720                .first()
1721                .and_then(|e| e.first())
1722                .is_some_and(|v| v == "E01")
1723                && s.elements
1724                    .get(2)
1725                    .and_then(|e| e.first())
1726                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1727        });
1728        ConditionResult::from(found)
1729    }
1730
1731    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1732    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1733        // TODO: implement
1734        ConditionResult::Unknown
1735    }
1736
1737    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1738    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1739        let dtm_segs = ctx.find_segments("DTM");
1740        let found = dtm_segs.iter().any(|s| {
1741            s.elements
1742                .first()
1743                .and_then(|e| e.first())
1744                .is_some_and(|v| v == "Z01")
1745                && s.elements
1746                    .first()
1747                    .and_then(|e| e.get(1))
1748                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1749        });
1750        ConditionResult::from(found)
1751    }
1752
1753    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1754    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1755        // Antwort auf Aktivierung — depends on PID context
1756        ctx.external.evaluate("activation_response")
1757    }
1758
1759    /// [31] Wenn eine Korrektur erfolgt
1760    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1761        ctx.external.evaluate("correction_in_progress")
1762    }
1763
1764    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1765    // HAND-EDITED: counts the SG8 SEQ+Z78 (Referenz auf die
1766    // Lokationsbündelstruktur) of this SG4. The generated code counted every
1767    // SEQ of the message, so any 55043 held it (reported by mako-twin).
1768    // HAND-EDITED (#392): 55177/55180 give the reference as SEQ+ZC7
1769    // (erwartet), others as ZC8 (im System vorhanden) or ZD5 (informativ);
1770    // counting Z78 alone made [32] false there and refused the numbers of
1771    // several bundles. One bundle may stand once as erwartet and once as im
1772    // System, so the count is per variant.
1773    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1774        let several = ["Z78", "ZC7", "ZC8", "ZD5"].iter().any(|code| {
1775            ctx.scoped_find_segments_in_with_qualifier("SG4", "SEQ", 0, code)
1776                .len()
1777                > 1
1778        });
1779        ConditionResult::from(several)
1780    }
1781
1782    /// [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...
1783    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1784        ctx.external.evaluate("code_list_membership_check")
1785    }
1786
1787    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1788    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1789        // Antwort auf Deaktivierung — depends on PID context
1790        ctx.external.evaluate("deactivation_response")
1791    }
1792
1793    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1794    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1795        let dtm_segs = ctx.find_segments("DTM");
1796        let found = dtm_segs.iter().any(|s| {
1797            s.elements
1798                .first()
1799                .and_then(|e| e.first())
1800                .is_some_and(|v| v == "Z01")
1801                && s.elements
1802                    .first()
1803                    .and_then(|e| e.get(1))
1804                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1805        });
1806        ConditionResult::from(found)
1807    }
1808
1809    /// [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...
1810    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1811        ctx.external.evaluate("code_list_membership_check")
1812    }
1813
1814    /// [37] Wenn Anmeldung/ Änderung befristet
1815    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1816        // Same check as [10]: STS element[4] is befristete Anmeldung code
1817        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1818    }
1819
1820    /// [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...
1821    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1822        ctx.external.evaluate("code_list_membership_check")
1823    }
1824
1825    /// [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...
1826    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1827        ctx.external.evaluate("code_list_membership_check")
1828    }
1829
1830    /// [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...
1831    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1832        ctx.external.evaluate("code_list_membership_check")
1833    }
1834
1835    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1836    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1837        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1838        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1839        if seq_z79.is_empty() {
1840            return ConditionResult::Unknown;
1841        }
1842        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1843        let found = seq_z79.iter().any(|s| {
1844            s.elements
1845                .get(1)
1846                .and_then(|e| e.first())
1847                .is_some_and(|v| !v.is_empty())
1848        });
1849        ConditionResult::from(found)
1850    }
1851
1852    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1853    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1854        let seq_segs = ctx.find_segments("SEQ");
1855        let zh0_count = seq_segs
1856            .iter()
1857            .filter(|s| {
1858                s.elements
1859                    .first()
1860                    .and_then(|e| e.first())
1861                    .is_some_and(|v| v == "ZH0")
1862            })
1863            .count();
1864        ConditionResult::from(zh0_count > 1)
1865    }
1866
1867    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1868    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1869        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1870        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1871        let found = sts_segs.iter().any(|s| {
1872            s.elements
1873                .first()
1874                .and_then(|e| e.first())
1875                .is_some_and(|v| v == "E01")
1876                && s.elements
1877                    .get(2)
1878                    .and_then(|e| e.first())
1879                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1880        });
1881        ConditionResult::from(found)
1882    }
1883
1884    /// [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...
1885    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1886        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1887        ConditionResult::True
1888    }
1889
1890    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1891    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1892        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1893        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1894        let found = cci_segs.iter().any(|s| {
1895            s.elements
1896                .first()
1897                .and_then(|e| e.first())
1898                .is_some_and(|v| v == "Z01")
1899                && s.elements
1900                    .get(2)
1901                    .and_then(|e| e.first())
1902                    .is_some_and(|v| v == "Z82")
1903        });
1904        ConditionResult::from(found)
1905    }
1906
1907    /// [46] Wenn ID für Objekt vergeben wurde
1908    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1909        // TODO: implement
1910        ConditionResult::Unknown
1911    }
1912
1913    /// [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
1914    // 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)
1915    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1916        const CODES: &[&str] = &[
1917            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1918            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1919            "WNP", "WNT",
1920        ];
1921        let nav = match ctx.navigator {
1922            Some(n) => n,
1923            None => return ConditionResult::Unknown,
1924        };
1925        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1926        for i in 0..sg8_count {
1927            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1928            for j in 0..sg10_count {
1929                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1930                let has_cci_15_z21 = ccis.iter().any(|s| {
1931                    s.elements
1932                        .first()
1933                        .and_then(|e: &Vec<String>| e.first())
1934                        .is_some_and(|v: &String| v == "15")
1935                        && s.elements
1936                            .get(2)
1937                            .and_then(|e: &Vec<String>| e.first())
1938                            .is_some_and(|v: &String| v == "Z21")
1939                });
1940                if has_cci_15_z21 {
1941                    let cavs =
1942                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1943                    if cavs.iter().any(|s| {
1944                        s.elements
1945                            .first()
1946                            .and_then(|e: &Vec<String>| e.first())
1947                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1948                    }) {
1949                        return ConditionResult::True;
1950                    }
1951                }
1952            }
1953        }
1954        ConditionResult::False
1955    }
1956
1957    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1958    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1959        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1960        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1961        let found = sts_segs.iter().any(|s| {
1962            s.elements
1963                .first()
1964                .and_then(|e| e.first())
1965                .is_some_and(|v| v == "E01")
1966                && s.elements
1967                    .get(2)
1968                    .and_then(|e| e.first())
1969                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1970        });
1971        ConditionResult::from(found)
1972    }
1973
1974    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1975    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1976        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1977        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1978        let found = sts_segs.iter().any(|s| {
1979            s.elements
1980                .first()
1981                .and_then(|e| e.first())
1982                .is_some_and(|v| v == "E01")
1983                && s.elements
1984                    .get(2)
1985                    .and_then(|e| e.first())
1986                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1987        });
1988        ConditionResult::from(found)
1989    }
1990
1991    /// [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...
1992    // 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)
1993    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1994        use std::collections::HashMap;
1995
1996        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1997        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1998        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
1999        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
2000
2001        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
2002        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
2003        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
2004            rff_by_instance
2005                .entry(*idx)
2006                .or_default()
2007                .push((q.clone(), id.clone()));
2008        }
2009
2010        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
2011        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
2012        for (idx, z) in &seq_zeitraum {
2013            seq_zeitraum_map.insert(*idx, z.clone());
2014        }
2015
2016        let z98_data: Vec<(String, String)> = seq_quals
2017            .iter()
2018            .filter(|(_, q)| q == "Z98")
2019            .map(|(idx, _)| {
2020                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2021                let marktlok = rff_by_instance
2022                    .get(idx)
2023                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2024                    .map(|(_, id)| id.clone())
2025                    .unwrap_or_default();
2026                (zeitraum, marktlok)
2027            })
2028            .collect();
2029
2030        if z98_data.is_empty() {
2031            return ConditionResult::Unknown;
2032        }
2033
2034        let ze7_data: Vec<(String, String)> = seq_quals
2035            .iter()
2036            .filter(|(_, q)| q == "ZE7")
2037            .map(|(idx, _)| {
2038                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2039                let marktlok = rff_by_instance
2040                    .get(idx)
2041                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2042                    .map(|(_, id)| id.clone())
2043                    .unwrap_or_default();
2044                (zeitraum, marktlok)
2045            })
2046            .collect();
2047
2048        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2049            !ze7_data
2050                .iter()
2051                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2052        }))
2053    }
2054
2055    /// [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...
2056    // 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)
2057    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2058        // HAND-EDITED: "derselben SG8 SEQ+Z98 mit derselben Zeitraum-ID … nicht auf ein SG8 SEQ+Z78" — compared within the enclosing SG4 when the scope
2059        // reaches an SG8; the message as before otherwise.
2060        if let Some(scope) = ctx.enclosing_scope("SG8") {
2061            let Some((_, zeitraum)) =
2062                sg4_instances::seq_at(ctx, scope).filter(|(code, _)| code == "Z98")
2063            else {
2064                return ConditionResult::False;
2065            };
2066            let pauschal = sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
2067                sg4_instances::seq_at(ctx, p)
2068                    .is_some_and(|(c, z)| c == "Z78" && !z.is_empty() && z == zeitraum)
2069                    && sg4_instances::rff_values_at(ctx, p, "Z31")
2070                        .iter()
2071                        .any(|v| v.replace(' ', "") == "9992000000018")
2072            });
2073            return ConditionResult::from(!pauschal);
2074        }
2075        let nav = match ctx.navigator() {
2076            Some(n) => n,
2077            None => return ConditionResult::Unknown,
2078        };
2079        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2080        let pauschal_id = "9992000000018";
2081        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2082        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2083        for i in 0..sg8_count {
2084            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2085            let has_z78 = seqs.iter().any(|s| {
2086                s.elements
2087                    .first()
2088                    .and_then(|e| e.first())
2089                    .is_some_and(|v| v == "Z78")
2090            });
2091            if !has_z78 {
2092                continue;
2093            }
2094            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2095            let has_pauschal = rffs.iter().any(|s| {
2096                s.elements
2097                    .first()
2098                    .and_then(|e| e.first())
2099                    .is_some_and(|v| v == "Z31")
2100                    && s.elements
2101                        .first()
2102                        .and_then(|e| e.get(1))
2103                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2104            });
2105            if has_pauschal {
2106                for seq in &seqs {
2107                    if seq
2108                        .elements
2109                        .first()
2110                        .and_then(|e| e.first())
2111                        .is_some_and(|v| v == "Z78")
2112                    {
2113                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2114                            if !zid.is_empty() {
2115                                pauschal_zeitraum_ids.push(zid.clone());
2116                            }
2117                        }
2118                    }
2119                }
2120            }
2121        }
2122        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2123        for i in 0..sg8_count {
2124            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2125            for seq in &seqs {
2126                if seq
2127                    .elements
2128                    .first()
2129                    .and_then(|e| e.first())
2130                    .is_some_and(|v| v == "Z98")
2131                {
2132                    let zid = seq
2133                        .elements
2134                        .get(1)
2135                        .and_then(|e| e.first())
2136                        .map(|s| s.as_str())
2137                        .unwrap_or("");
2138                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2139                        // Z98 SG8 references a pauschal Z78 → condition is False
2140                        return ConditionResult::False;
2141                    }
2142                }
2143            }
2144        }
2145        // No Z98 SG8 references the pauschal bundle → condition is True
2146        ConditionResult::True
2147    }
2148
2149    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2150    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2151        let cci_segs = ctx.find_segments("CCI");
2152        let found = cci_segs.iter().any(|s| {
2153            s.elements
2154                .first()
2155                .and_then(|e| e.first())
2156                .is_some_and(|v| v == "Z07")
2157                && s.elements
2158                    .get(2)
2159                    .and_then(|e| e.first())
2160                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2161        });
2162        ConditionResult::from(found)
2163    }
2164
2165    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2166    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2167        // TODO: implement
2168        ConditionResult::Unknown
2169    }
2170
2171    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2172    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2173        // HAND-EDITED (#336): the SEQ alone is every MaLo, generation or not; the CCI of one of its
2174        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
2175        if ctx.navigator().is_none() {
2176            let seq = ctx
2177                .find_segments("SEQ")
2178                .iter()
2179                .any(|s| ["Z01"].contains(&s.get_element(0)));
2180            return ConditionResult::from(
2181                seq && sg4_instances::has_cci(
2182                    &ctx.find_segments("CCI")
2183                        .into_iter()
2184                        .cloned()
2185                        .collect::<Vec<_>>(),
2186                    "Z30",
2187                    "Z06",
2188                ),
2189            );
2190        }
2191        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z06"))
2192    }
2193
2194    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2195    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2196        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2197        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2198        let found = sts_segs.iter().any(|s| {
2199            s.elements
2200                .first()
2201                .and_then(|e| e.first())
2202                .is_some_and(|v| v == "E01")
2203                && s.elements
2204                    .get(2)
2205                    .and_then(|e| e.first())
2206                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2207        });
2208        ConditionResult::from(found)
2209    }
2210
2211    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2212    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2213        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2214        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2215        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2216        let found = sts_segs.iter().any(|s| {
2217            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2218                [
2219                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2220                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2221                ]
2222                .contains(&v.as_str())
2223            })
2224        });
2225        ConditionResult::from(found)
2226    }
2227
2228    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2229    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2230        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2231        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2232        let found = cci_segs.iter().any(|s| {
2233            s.elements
2234                .first()
2235                .and_then(|e| e.first())
2236                .is_some_and(|v| v == "Z45")
2237                && s.elements
2238                    .get(2)
2239                    .and_then(|e| e.first())
2240                    .is_some_and(|v| v == "ZD9")
2241        });
2242        ConditionResult::from(found)
2243    }
2244
2245    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2246    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2247        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2248        // in tree validation), else the enclosing SG10's CCI.
2249        let has_293 =
2250            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2251        if let Some(own) = ctx.resolved_segment.filter(|_| {
2252            ctx.scoped_find_segments_in("SG10", "CCI")
2253                .iter()
2254                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2255        }) {
2256            return ConditionResult::from(has_293(own));
2257        }
2258        ConditionResult::from(
2259            ctx.scoped_find_segments_in("SG10", "CCI")
2260                .iter()
2261                .any(|s| has_293(&s.elements)),
2262        )
2263    }
2264
2265    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2266    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2267        let bgm_segs = ctx.find_segments("BGM");
2268        let found = bgm_segs.iter().any(|s| {
2269            s.elements
2270                .first()
2271                .and_then(|e| e.first())
2272                .is_some_and(|v| v == "Z90")
2273        });
2274        ConditionResult::from(found)
2275    }
2276
2277    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2278    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2279        let bgm_segs = ctx.find_segments("BGM");
2280        let found = bgm_segs.iter().any(|s| {
2281            s.elements
2282                .first()
2283                .and_then(|e| e.first())
2284                .is_some_and(|v| v == "Z89")
2285        });
2286        ConditionResult::from(found)
2287    }
2288
2289    /// [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), ...
2290    // 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)
2291    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2292        let nav = match ctx.navigator {
2293            Some(n) => n,
2294            None => return ConditionResult::Unknown,
2295        };
2296        use std::collections::HashMap;
2297        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2298        let sg4_count = nav.group_instance_count(&["SG4"]);
2299        for sg4_idx in 0..sg4_count {
2300            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2301            for sg8_idx in 0..sg8_count {
2302                let seqs =
2303                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2304                let zeitraum_opt = seqs
2305                    .iter()
2306                    .filter(|s| {
2307                        s.elements
2308                            .first()
2309                            .and_then(|e: &Vec<String>| e.first())
2310                            .is_some_and(|v: &String| v == "Z45")
2311                    })
2312                    .filter_map(|s| {
2313                        s.elements
2314                            .get(1)
2315                            .and_then(|e: &Vec<String>| e.first())
2316                            .filter(|v| !v.is_empty())
2317                            .cloned()
2318                    })
2319                    .next();
2320                let Some(zid) = zeitraum_opt else { continue };
2321                let pias =
2322                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2323                let has_qualifying_pia = pias.iter().any(|s| {
2324                    s.elements
2325                        .first()
2326                        .and_then(|e: &Vec<String>| e.first())
2327                        .is_some_and(|v: &String| v == "Z02")
2328                        && s.elements
2329                            .get(1)
2330                            .and_then(|e: &Vec<String>| e.first())
2331                            .map(|code| {
2332                                code.starts_with("1-08-1")
2333                                    || code.starts_with("1-08-2")
2334                                    || code.starts_with("1-08-3")
2335                                    || code.starts_with("1-08-4")
2336                                    || code.starts_with("1-08-5")
2337                            })
2338                            .unwrap_or(false)
2339                });
2340                if has_qualifying_pia {
2341                    *count_by_zid.entry(zid).or_insert(0) += 1;
2342                }
2343            }
2344        }
2345        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2346    }
2347
2348    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2349    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2350        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2351        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2352        let found = sts_segs.iter().any(|s| {
2353            s.elements
2354                .first()
2355                .and_then(|e| e.first())
2356                .is_some_and(|v| v == "E01")
2357                && s.elements
2358                    .get(2)
2359                    .and_then(|e| e.first())
2360                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2361        });
2362        ConditionResult::from(found)
2363    }
2364
2365    /// [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...
2366    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2367        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2368        let pia_segs = ctx.find_segments("PIA");
2369        let count = pia_segs
2370            .iter()
2371            .filter(|s| {
2372                s.elements
2373                    .first()
2374                    .and_then(|e| e.first())
2375                    .is_some_and(|v| v == "Z02")
2376                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2377                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2378                            .iter()
2379                            .any(|p| v.starts_with(p))
2380                    })
2381            })
2382            .count();
2383        ConditionResult::from(count > 1)
2384    }
2385
2386    /// [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...
2387    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2388        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2389        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2390        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2391        let found = pia_segs.iter().any(|s| {
2392            s.elements
2393                .first()
2394                .and_then(|e| e.first())
2395                .is_some_and(|v| v == "Z02")
2396                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2397                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2398                        .iter()
2399                        .any(|p| v.starts_with(p))
2400                })
2401        });
2402        ConditionResult::from(found)
2403    }
2404
2405    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2406    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2407        let seq_segs = ctx.find_segments("SEQ");
2408        let count = seq_segs
2409            .iter()
2410            .filter(|s| {
2411                s.elements
2412                    .first()
2413                    .and_then(|e| e.first())
2414                    .is_some_and(|v| v == "ZH0")
2415            })
2416            .count();
2417        ConditionResult::from(count > 1)
2418    }
2419
2420    /// [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.
2421    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2422        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2423        let cav_segs = ctx.find_segments("CAV");
2424        let found = cav_segs.iter().any(|s| {
2425            s.elements
2426                .first()
2427                .and_then(|e| e.first())
2428                .is_some_and(|v| v == "ZH9")
2429                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2430                && s.elements
2431                    .first()
2432                    .and_then(|e| e.get(3))
2433                    .is_some_and(|v| v == "9991000002933")
2434        });
2435        ConditionResult::from(!found)
2436    }
2437
2438    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2439    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2440        let seq_segs = ctx.find_segments("SEQ");
2441        let count = seq_segs
2442            .iter()
2443            .filter(|s| {
2444                s.elements
2445                    .first()
2446                    .and_then(|e| e.first())
2447                    .is_some_and(|v| v == "ZH0")
2448            })
2449            .count();
2450        ConditionResult::from(count > 2)
2451    }
2452
2453    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2454    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2455        let seq_segs = ctx.find_segments("SEQ");
2456        let count = seq_segs
2457            .iter()
2458            .filter(|s| {
2459                s.elements
2460                    .first()
2461                    .and_then(|e| e.first())
2462                    .is_some_and(|v| v == "ZH0")
2463            })
2464            .count();
2465        ConditionResult::from(count > 3)
2466    }
2467
2468    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2469    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2470        let seq_segs = ctx.find_segments("SEQ");
2471        let count = seq_segs
2472            .iter()
2473            .filter(|s| {
2474                s.elements
2475                    .first()
2476                    .and_then(|e| e.first())
2477                    .is_some_and(|v| v == "ZH0")
2478            })
2479            .count();
2480        ConditionResult::from(count == 5)
2481    }
2482
2483    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2484    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2485        // Antwort auf Zuordnung — depends on PID context
2486        ctx.external.evaluate("assignment_response")
2487    }
2488
2489    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2490    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2491        // Antwort auf Beendigung der Zuordnung — depends on PID context
2492        ctx.external.evaluate("assignment_end_response")
2493    }
2494
2495    /// [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
2496    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2497    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2498    // Regenerating this function should preserve the shape, but re-verify
2499    // against the committed body if generate-conditions is run.
2500    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2501        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2502        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2503        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2504        //
2505        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2506        // narrows to the enclosing SG8 instance; otherwise falls back to
2507        // message-wide find_segments. Matches the AHB wording
2508        // "in derselben SG8 im PIA DE7140 ein Code...".
2509        use crate::eval::CodelistColumn;
2510        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2511            let is_z02 = s
2512                .elements
2513                .first()
2514                .and_then(|e| e.first())
2515                .is_some_and(|v| v == "Z02");
2516            if !is_z02 {
2517                return false;
2518            }
2519            let code = match s.elements.get(1).and_then(|e| e.first()) {
2520                Some(c) if !c.is_empty() => c,
2521                _ => return false,
2522            };
2523            ctx.codelist
2524                .flag(code, CodelistColumn::UtilmdPreisangabe)
2525                .unwrap_or(false)
2526        });
2527        ConditionResult::from(any_match)
2528    }
2529
2530    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2531    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2532        let seq_segs = ctx.find_segments("SEQ");
2533        let has_seq = seq_segs.iter().any(|s| {
2534            s.elements
2535                .first()
2536                .and_then(|e| e.first())
2537                .is_some_and(|v| v == "Z59")
2538        });
2539        let pia_segs = ctx.find_segments("PIA");
2540        let has_pia = pia_segs.iter().any(|s| {
2541            s.elements
2542                .first()
2543                .and_then(|e| e.first())
2544                .is_some_and(|v| v == "5")
2545        });
2546        ConditionResult::from(has_seq && has_pia)
2547    }
2548
2549    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2550    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2551        ctx.external.evaluate("code_list_membership_check")
2552    }
2553
2554    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2555    // 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)
2556    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2557        ctx.has_qualifier("SEQ", 0, "Z49")
2558    }
2559
2560    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2561    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2562        let seq_segs = ctx.find_segments("SEQ");
2563        let found = seq_segs.iter().any(|s| {
2564            s.elements
2565                .first()
2566                .and_then(|e| e.first())
2567                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2568        });
2569        ConditionResult::from(!found)
2570    }
2571
2572    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2573    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2574        let sts_segs = ctx.find_segments("STS");
2575        let found = sts_segs.iter().any(|s| {
2576            s.elements
2577                .first()
2578                .and_then(|e| e.first())
2579                .is_some_and(|v| v == "7")
2580                && s.elements
2581                    .get(2)
2582                    .and_then(|e| e.first())
2583                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2584        });
2585        ConditionResult::from(!found)
2586    }
2587
2588    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2589    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2590        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2591        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2592        let found = sts_segs.iter().any(|s| {
2593            s.elements
2594                .first()
2595                .and_then(|e| e.first())
2596                .is_some_and(|v| v == "E01")
2597                && s.elements
2598                    .get(2)
2599                    .and_then(|e| e.first())
2600                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2601        });
2602        ConditionResult::from(found)
2603    }
2604
2605    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2606    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2607        let sts_segs = ctx.find_segments("STS");
2608        let found = sts_segs.iter().any(|s| {
2609            s.elements
2610                .first()
2611                .and_then(|e| e.first())
2612                .is_some_and(|v| v == "E01")
2613                && s.elements
2614                    .get(2)
2615                    .and_then(|e| e.first())
2616                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2617        });
2618        ConditionResult::from(found)
2619    }
2620
2621    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2622    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2623        let dtm_segs = ctx.find_segments("DTM");
2624        let found = dtm_segs.iter().any(|s| {
2625            s.elements
2626                .first()
2627                .and_then(|e| e.first())
2628                .is_some_and(|v| v == "Z01")
2629                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2630                    v.chars()
2631                        .nth(3)
2632                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2633                })
2634        });
2635        ConditionResult::from(found)
2636    }
2637
2638    /// [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...
2639    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2640        // 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
2641        // reaches an SG6; the message as before otherwise.
2642        if let Some(scope) = ctx.enclosing_scope("SG6") {
2643            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
2644            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
2645                sg4_instances::has_cci(s, "", "ZA6")
2646            }));
2647        }
2648        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2649        ctx.groups_share_qualified_value(
2650            "RFF",
2651            0,
2652            "Z50",
2653            0,
2654            2,
2655            &["SG4", "SG6"],
2656            "RFF",
2657            0,
2658            2,
2659            &["SG4", "SG8"],
2660        )
2661    }
2662
2663    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2664    // HAND-EDITED: judges the DTM+157 value in German local time. The generated
2665    // code looked for raw `DD=01`/`HHMM=0000` in any DTM, which a correct UTC
2666    // value (`202610312300+00`) never has (mako-twin, 55018).
2667    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2668        use chrono::{Datelike, Timelike};
2669        let value = match ctx.resolved_value.filter(|v| !v.is_empty()) {
2670            Some(v) => v.to_string(),
2671            None => {
2672                let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2673                match dtm157.first() {
2674                    Some(dtm) => dtm.get_component(0, 1).to_string(),
2675                    None => return ConditionResult::Unknown,
2676                }
2677            }
2678        };
2679        match german_local_time(&value) {
2680            Some(t) => ConditionResult::from(t.day() == 1 && t.hour() == 0 && t.minute() == 0),
2681            None => ConditionResult::False,
2682        }
2683    }
2684
2685    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2686    // HAND-EDITED: compares the value; the generated code only checked that a
2687    // DTM+157 exists.
2688    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2689        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
2690            return ConditionResult::Unknown;
2691        };
2692        let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2693        match dtm157.first() {
2694            Some(dtm) => ConditionResult::from(dtm.get_component(0, 1) == value),
2695            None => ConditionResult::Unknown,
2696        }
2697    }
2698
2699    /// [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+...
2700    // 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)
2701    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2702        let nav = match ctx.navigator {
2703            Some(n) => n,
2704            None => {
2705                return ctx.filtered_parent_child_has_qualifier(
2706                    &["SG4", "SG8"],
2707                    "SEQ",
2708                    0,
2709                    "Z01",
2710                    "SG10",
2711                    "CCI",
2712                    2,
2713                    "ZA6",
2714                )
2715            }
2716        };
2717        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2718        for i in 0..sg8_count {
2719            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2720            for j in 0..sg10_count {
2721                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2722                let has_cci_za6 = ccis.iter().any(|s| {
2723                    s.elements
2724                        .get(2)
2725                        .and_then(|e: &Vec<String>| e.first())
2726                        .is_some_and(|v: &String| v == "ZA6")
2727                });
2728                if !has_cci_za6 {
2729                    continue;
2730                }
2731                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2732                if cavs.iter().any(|s| {
2733                    s.elements
2734                        .first()
2735                        .and_then(|e: &Vec<String>| e.first())
2736                        .is_some_and(|v: &String| v == "E14")
2737                }) {
2738                    return ConditionResult::True;
2739                }
2740            }
2741        }
2742        ConditionResult::False
2743    }
2744
2745    /// [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
2746    // 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)
2747    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2748        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2749        // scope reaches one; the message as before otherwise.
2750        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2751            return ConditionResult::from(
2752                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2753                    .unwrap_or_default()
2754                    .iter()
2755                    .any(|segs| {
2756                        segs.iter()
2757                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2758                            && segs
2759                                .iter()
2760                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2761                    })
2762                    && ctx
2763                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2764                        .unwrap_or_default()
2765                        .iter()
2766                        .any(|segs| {
2767                            segs.iter()
2768                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2769                                && segs
2770                                    .iter()
2771                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2772                        }),
2773            );
2774        }
2775        let nav = match ctx.navigator {
2776            Some(n) => n,
2777            None => return ConditionResult::Unknown,
2778        };
2779        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2780        for i in 0..sg8_count {
2781            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2782            let mut has_za6_e14 = false;
2783            let mut has_za6_e02 = false;
2784            for j in 0..sg10_count {
2785                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2786                let has_cci_za6 = ccis.iter().any(|s| {
2787                    s.elements
2788                        .get(2)
2789                        .and_then(|e: &Vec<String>| e.first())
2790                        .is_some_and(|v: &String| v == "ZA6")
2791                });
2792                if !has_cci_za6 {
2793                    continue;
2794                }
2795                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2796                for cav in &cavs {
2797                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2798                        if code == "E14" {
2799                            has_za6_e14 = true;
2800                        }
2801                        if code == "E02" {
2802                            has_za6_e02 = true;
2803                        }
2804                    }
2805                }
2806            }
2807            if has_za6_e14 && has_za6_e02 {
2808                return ConditionResult::True;
2809            }
2810        }
2811        ConditionResult::False
2812    }
2813
2814    /// [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
2815    // 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)
2816    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2817        match self.evaluate_90(ctx) {
2818            ConditionResult::True => ConditionResult::False,
2819            ConditionResult::False => ConditionResult::True,
2820            ConditionResult::Unknown => ConditionResult::Unknown,
2821        }
2822    }
2823
2824    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2825    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2826        // TODO: implement
2827        ConditionResult::Unknown
2828    }
2829
2830    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2831    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2832        // TODO: implement
2833        ConditionResult::Unknown
2834    }
2835
2836    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2837    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2838        // Structural: segment exists within SG (always true for valid messages)
2839        ConditionResult::True
2840    }
2841
2842    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2843    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2844        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2845        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2846        let has_cci = cci_segs.iter().any(|s| {
2847            s.elements
2848                .first()
2849                .and_then(|e| e.first())
2850                .is_some_and(|v| v == "Z17")
2851        });
2852        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2853        let has_cav = cav_segs.iter().any(|s| {
2854            s.elements
2855                .first()
2856                .and_then(|e| e.first())
2857                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2858        });
2859        ConditionResult::from(has_cci && has_cav)
2860    }
2861
2862    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2863    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2864        let sts_segs = ctx.find_segments("STS");
2865        let found = sts_segs.iter().any(|s| {
2866            s.elements
2867                .first()
2868                .and_then(|e| e.first())
2869                .is_some_and(|v| v == "7")
2870                && s.elements
2871                    .get(3)
2872                    .and_then(|e| e.first())
2873                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2874        });
2875        ConditionResult::from(found)
2876    }
2877
2878    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2879    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2880        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2881        let cav_segs = ctx.find_segments("CAV");
2882        let found = cav_segs.iter().any(|s| {
2883            s.elements
2884                .first()
2885                .and_then(|e| e.first())
2886                .is_some_and(|v| v == "ZH9")
2887                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2888                && s.elements
2889                    .first()
2890                    .and_then(|e| e.get(3))
2891                    .is_some_and(|v| v == "9991000002420")
2892        });
2893        ConditionResult::from(found)
2894    }
2895
2896    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2897    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2898        ctx.external.evaluate("sender_role_check")
2899    }
2900
2901    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2902    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2903        ctx.external.evaluate("sender_role_check")
2904    }
2905
2906    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2907    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2908        let cav_segs = ctx.find_segments("CAV");
2909        let found = cav_segs.iter().any(|s| {
2910            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2911                [
2912                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2913                ]
2914                .contains(&v.as_str())
2915            })
2916        });
2917        ConditionResult::from(found)
2918    }
2919
2920    /// [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
2921    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2922        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2923        sg10_has(ctx, true, |segs| {
2924            sg4_instances::has_cci(segs, "", "ZA6")
2925                && (sg4_instances::has_cav(segs, "E14") || sg4_instances::has_cav(segs, "Z36"))
2926        })
2927    }
2928
2929    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2930    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2931        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2932        sg10_has(ctx, true, |segs| {
2933            sg4_instances::has_cci(segs, "", "ZA6") && sg4_instances::has_cav(segs, "Z36")
2934        })
2935    }
2936
2937    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2938    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2939        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2940        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2941        let found = qty_segs.iter().any(|s| {
2942            s.elements
2943                .first()
2944                .and_then(|e| e.first())
2945                .is_some_and(|v| v == "265")
2946        });
2947        ConditionResult::from(found)
2948    }
2949
2950    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2951    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2952        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2953        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2954        let found = qty_segs.iter().any(|s| {
2955            s.elements
2956                .first()
2957                .and_then(|e| e.first())
2958                .is_some_and(|v| v == "Z08")
2959        });
2960        ConditionResult::from(found)
2961    }
2962
2963    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2964    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2965        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2966        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2967        let found = qty_segs.iter().any(|s| {
2968            s.elements
2969                .first()
2970                .and_then(|e| e.first())
2971                .is_some_and(|v| v == "Z10")
2972        });
2973        ConditionResult::from(found)
2974    }
2975
2976    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2977    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2978        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2979        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2980        ctx.this_group_parent_child_has_qualifier(
2981            &["SG4", "SG8"],
2982            "SEQ",
2983            0,
2984            "Z01",
2985            "SG10",
2986            "CAV",
2987            0,
2988            "E02",
2989        )
2990    }
2991
2992    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2993    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2994        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2995        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2996        let found = cav_segs.iter().any(|s| {
2997            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2998                [
2999                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
3000                ]
3001                .contains(&v.as_str())
3002            })
3003        });
3004        ConditionResult::from(found)
3005    }
3006
3007    /// [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
3008    // HAND-EDITED — do not let generate-conditions regenerate this function
3009    // without first confirming prompt.rs B6 SCOPE RULES section is in the
3010    // system prompt. Regeneration without that guidance would emit
3011    // ctx.has_qualifier (message-wide) and silently reintroduce the
3012    // per-instance bug this function fixes.
3013    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
3014    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
3015        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
3016        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
3017        // SG8 instance when GroupScope is set; falls back to message-wide.
3018        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
3019        let found = ctx
3020            .scoped_find_segments_in("SG8", "PIA")
3021            .iter()
3022            .filter(|s| {
3023                s.elements
3024                    .first()
3025                    .and_then(|e| e.first())
3026                    .is_some_and(|q| q == "Z02")
3027            })
3028            .any(|s| {
3029                s.elements
3030                    .get(1)
3031                    .and_then(|e| e.first())
3032                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
3033            });
3034        ConditionResult::from(found)
3035    }
3036
3037    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
3038    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
3039        let cav_segs = ctx.find_segments("CAV");
3040        let found = cav_segs.iter().any(|s| {
3041            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
3042                [
3043                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
3044                ]
3045                .contains(&v.as_str())
3046            })
3047        });
3048        ConditionResult::from(found)
3049    }
3050
3051    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3052    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3053        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3054        // scope reaches one; the message as before otherwise.
3055        if let Some(scope) = ctx.enclosing_scope("SG8") {
3056            return ConditionResult::from(
3057                ctx.find_segments_at(scope, "SEQ")
3058                    .iter()
3059                    .any(|s| matches!(s.get_element(0), "Z59"))
3060                    && !ctx
3061                        .find_segments_at(scope, "PIA")
3062                        .iter()
3063                        .any(|s| s.get_element(0) == "5"),
3064            );
3065        }
3066        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3067    }
3068
3069    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3070    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3071        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3072        // scope reaches one; the message as before otherwise.
3073        if let Some(scope) = ctx.enclosing_scope("SG8") {
3074            return ConditionResult::from(
3075                ctx.find_segments_at(scope, "SEQ")
3076                    .iter()
3077                    .any(|s| matches!(s.get_element(0), "Z59"))
3078                    && !ctx
3079                        .find_segments_at(scope, "CCI")
3080                        .iter()
3081                        .any(|s| s.get_element(0) == "11"),
3082            );
3083        }
3084        let result = ctx.filtered_parent_child_has_qualifier(
3085            &["SG4", "SG8"],
3086            "SEQ",
3087            0,
3088            "Z59",
3089            "SG10",
3090            "CCI",
3091            0,
3092            "11",
3093        );
3094        match result {
3095            ConditionResult::True => ConditionResult::False,
3096            ConditionResult::False => ConditionResult::True,
3097            ConditionResult::Unknown => ConditionResult::Unknown,
3098        }
3099    }
3100
3101    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3102    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3103        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3104        ctx.external.evaluate("counting_time_order_response")
3105    }
3106
3107    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3108    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3109        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3110        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3111        spannungsebene_in_this_sg10(ctx, "E06")
3112    }
3113
3114    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3115    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3116        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3117        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3118        spannungsebene_in_this_sg10(ctx, "E05")
3119    }
3120
3121    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3122    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3123        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3124        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3125        spannungsebene_in_this_sg10(ctx, "E04")
3126    }
3127
3128    /// [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 (...
3129    // 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)
3130    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3131        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (TLP/TEP) — compared within the enclosing SG4 when the scope
3132        // reaches an SG8; the message as before otherwise.
3133        if let Some(scope) = ctx.enclosing_scope("SG8") {
3134            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
3135            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
3136                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E14")
3137            }));
3138        }
3139        let nav = match ctx.navigator {
3140            Some(n) => n,
3141            None => return ConditionResult::Unknown,
3142        };
3143        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3144        for i in 0..sg8_count {
3145            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3146            for j in 0..sg10_count {
3147                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3148                let has_cci_za6 = ccis.iter().any(|s| {
3149                    s.elements
3150                        .get(2)
3151                        .and_then(|e: &Vec<String>| e.first())
3152                        .is_some_and(|v: &String| v == "ZA6")
3153                });
3154                if has_cci_za6 {
3155                    let cavs =
3156                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3157                    if cavs.iter().any(|s| {
3158                        s.elements
3159                            .first()
3160                            .and_then(|e: &Vec<String>| e.first())
3161                            .is_some_and(|v: &String| v == "E14")
3162                    }) {
3163                        return ConditionResult::True;
3164                    }
3165                }
3166            }
3167        }
3168        ConditionResult::False
3169    }
3170
3171    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3172    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3173        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3174        // scope reaches one; the message as before otherwise.
3175        if let Some(scope) = ctx.enclosing_scope("SG8") {
3176            return ConditionResult::from(
3177                ctx.find_segments_at(scope, "SEQ")
3178                    .iter()
3179                    .any(|s| matches!(s.get_element(0), "Z98"))
3180                    && ctx
3181                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3182                        .unwrap_or_default()
3183                        .iter()
3184                        .any(|segs| {
3185                            segs.iter()
3186                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3187                                && segs
3188                                    .iter()
3189                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3190                        }),
3191            );
3192        }
3193        let has_cci = ctx.filtered_parent_child_has_qualifier(
3194            &["SG4", "SG8"],
3195            "SEQ",
3196            0,
3197            "Z98",
3198            "SG10",
3199            "CCI",
3200            2,
3201            "ZA6",
3202        );
3203        if has_cci != ConditionResult::True {
3204            return has_cci;
3205        }
3206        ctx.filtered_parent_child_has_qualifier(
3207            &["SG4", "SG8"],
3208            "SEQ",
3209            0,
3210            "Z98",
3211            "SG10",
3212            "CAV",
3213            0,
3214            "E02",
3215        )
3216    }
3217
3218    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3219    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3220        // HAND-EDITED (#337): the CAV+E02 of the SG10 CCI+++ZA6, as [265] asks
3221        // for its CAV+Z36 — not any CAV+E02 (CCI+++ZC0 CAV+E02 is no profile).
3222        let Some(blocks) = sg8_blocks(ctx) else {
3223            return ConditionResult::Unknown;
3224        };
3225        let found = blocks
3226            .iter()
3227            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
3228            .any(|b| {
3229                b.sg10.iter().any(|(ccis, cavs)| {
3230                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
3231                        && cavs.iter().any(|c| component(c, 0, 0) == Some("E02"))
3232                })
3233            });
3234        ConditionResult::from(found)
3235    }
3236
3237    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3238    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3239        // TODO: implement
3240        ConditionResult::Unknown
3241    }
3242
3243    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3244    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3245        let z03 =
3246            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3247        if z03 == ConditionResult::True {
3248            return ConditionResult::True;
3249        }
3250        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3251    }
3252
3253    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3254    // 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)
3255    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3256        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3257        // scope reaches one; the message as before otherwise.
3258        if let Some(scope) = ctx.enclosing_scope("SG8") {
3259            return ConditionResult::from(
3260                ctx.find_segments_at(scope, "SEQ")
3261                    .iter()
3262                    .any(|s| matches!(s.get_element(0), "Z98"))
3263                    && ctx
3264                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3265                        .unwrap_or_default()
3266                        .iter()
3267                        .any(|segs| {
3268                            segs.iter()
3269                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3270                                && segs
3271                                    .iter()
3272                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3273                        }),
3274            );
3275        }
3276        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3277        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3278            &["SG4", "SG8"],
3279            "SEQ",
3280            0,
3281            "Z98",
3282            "SG10",
3283            "CCI",
3284            2,
3285            "E03",
3286        );
3287        if has_cci_e03 != ConditionResult::True {
3288            return has_cci_e03;
3289        }
3290        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3291        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3292    }
3293
3294    /// [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...
3295    // 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)
3296    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3297        struct Sg8Z20Entry {
3298            mg_id: Option<String>,
3299        }
3300        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3301        let mut current: Option<Sg8Z20Entry> = None;
3302        for seg in ctx.segments {
3303            match seg.id.as_str() {
3304                "SEQ" => {
3305                    if let Some(entry) = current.take() {
3306                        entries.push(entry);
3307                    }
3308                    if seg
3309                        .elements
3310                        .first()
3311                        .and_then(|e: &Vec<String>| e.first())
3312                        .is_some_and(|v: &String| v == "Z20")
3313                    {
3314                        current = Some(Sg8Z20Entry { mg_id: None });
3315                    }
3316                }
3317                "RFF" if current.is_some() => {
3318                    if seg
3319                        .elements
3320                        .first()
3321                        .and_then(|e: &Vec<String>| e.first())
3322                        .is_some_and(|v: &String| v == "MG")
3323                    {
3324                        if let Some(ref mut entry) = current {
3325                            if entry.mg_id.is_none() {
3326                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3327                            }
3328                        }
3329                    }
3330                }
3331                _ => {}
3332            }
3333        }
3334        if let Some(entry) = current {
3335            entries.push(entry);
3336        }
3337        let ids: Vec<&str> = entries
3338            .iter()
3339            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3340            .collect();
3341        for i in 0..ids.len() {
3342            for j in (i + 1)..ids.len() {
3343                if ids[i] == ids[j] {
3344                    return ConditionResult::True;
3345                }
3346            }
3347        }
3348        ConditionResult::False
3349    }
3350
3351    /// [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...
3352    // 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)
3353    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3354        // HAND-EDITED: "noch mindestens eine weitere SG8 SEQ+Z20 mit derselben Zeitraum-ID … RFF+MG" — compared within the enclosing SG4 when the scope
3355        // reaches an SG8; the message as before otherwise.
3356        if let Some(scope) = ctx.enclosing_scope("SG8") {
3357            let Some((_, zeitraum)) = sg4_instances::seq_at(ctx, scope)
3358                .filter(|(code, zeitraum)| code == "Z20" && !zeitraum.is_empty())
3359            else {
3360                return ConditionResult::False;
3361            };
3362            let meters = sg4_instances::rff_values_at(ctx, scope, "MG");
3363            return ConditionResult::from(sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
3364                sg4_instances::seq_at(ctx, p).is_some_and(|(c, z)| c == "Z20" && z == zeitraum)
3365                    && sg4_instances::rff_values_at(ctx, p, "MG")
3366                        .iter()
3367                        .any(|m| meters.contains(m))
3368            }));
3369        }
3370        struct Sg8Z20Entry {
3371            zeitraum_id: String,
3372            mg_id: Option<String>,
3373        }
3374        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3375        let mut current: Option<Sg8Z20Entry> = None;
3376        for seg in ctx.segments {
3377            match seg.id.as_str() {
3378                "SEQ" => {
3379                    if let Some(entry) = current.take() {
3380                        entries.push(entry);
3381                    }
3382                    if seg
3383                        .elements
3384                        .first()
3385                        .and_then(|e: &Vec<String>| e.first())
3386                        .is_some_and(|v: &String| v == "Z20")
3387                    {
3388                        let zeitraum_id = seg
3389                            .elements
3390                            .get(1)
3391                            .and_then(|e: &Vec<String>| e.first())
3392                            .cloned()
3393                            .unwrap_or_default();
3394                        current = Some(Sg8Z20Entry {
3395                            zeitraum_id,
3396                            mg_id: None,
3397                        });
3398                    }
3399                }
3400                "RFF" if current.is_some() => {
3401                    if seg
3402                        .elements
3403                        .first()
3404                        .and_then(|e: &Vec<String>| e.first())
3405                        .is_some_and(|v: &String| v == "MG")
3406                    {
3407                        if let Some(ref mut entry) = current {
3408                            if entry.mg_id.is_none() {
3409                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3410                            }
3411                        }
3412                    }
3413                }
3414                _ => {}
3415            }
3416        }
3417        if let Some(entry) = current {
3418            entries.push(entry);
3419        }
3420        for i in 0..entries.len() {
3421            for j in (i + 1)..entries.len() {
3422                let ei = &entries[i];
3423                let ej = &entries[j];
3424                if !ei.zeitraum_id.is_empty()
3425                    && ei.zeitraum_id == ej.zeitraum_id
3426                    && ei.mg_id.is_some()
3427                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3428                    && ei.mg_id == ej.mg_id
3429                {
3430                    return ConditionResult::True;
3431                }
3432            }
3433        }
3434        ConditionResult::False
3435    }
3436
3437    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3438    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3439        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3440        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3441        let found = seq_segs.iter().any(|s| {
3442            s.elements
3443                .first()
3444                .and_then(|e| e.first())
3445                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3446        });
3447        ConditionResult::from(found)
3448    }
3449
3450    /// [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...
3451    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z49/Z53
3452    // of its SG4, counting from 1. The generated code only checked that one
3453    // existed, so any value passed.
3454    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3455        repetition_ordinal(ctx, &["Z49", "Z53"])
3456    }
3457
3458    /// [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...
3459    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z48/Z55
3460    // of its SG4, counting from 1. The generated code only checked that one
3461    // existed, so any value passed.
3462    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3463        repetition_ordinal(ctx, &["Z48", "Z55"])
3464    }
3465
3466    /// [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...
3467    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z47/Z54
3468    // of its SG4, counting from 1. The generated code only checked that one
3469    // existed, so any value passed.
3470    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3471        repetition_ordinal(ctx, &["Z47", "Z54"])
3472    }
3473
3474    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3475    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3476        // Check: all LOC+Z16 DE3225 values are identical
3477        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3478        if loc_segs.len() <= 1 {
3479            return ConditionResult::True;
3480        }
3481        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3482        let all_same = loc_segs
3483            .iter()
3484            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3485        ConditionResult::from(all_same)
3486    }
3487
3488    /// [130] Wenn an Messlokation vorhanden
3489    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3490        // TODO: implement
3491        ConditionResult::Unknown
3492    }
3493
3494    /// [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...
3495    // HAND-EDITED: compares the dates. The generated code only checked that
3496    // some RFF carries Zeitraum-ID 1 (mako-twin, 55643).
3497    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3498        first_period_start_vs_next_day(ctx, |start, next_day| start <= next_day)
3499    }
3500
3501    /// [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...
3502    // HAND-EDITED: judges the dates in German local time, in this SG6. The
3503    // generated code compared raw digits with `…0000`, which a correct UTC
3504    // value (`202610222200+00`) never has.
3505    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3506        first_period_start_vs_next_day(ctx, |start, next_day| start == next_day)
3507    }
3508
3509    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3510    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3511        // TODO: implement
3512        ConditionResult::Unknown
3513    }
3514
3515    /// [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 ...
3516    // HAND-EDITED: judges this SG6 against the Z48/Z55 SG6 with the next lower
3517    // Zeitraum-ID. The generated code checked every slice message-wide and was
3518    // true on a Z48 SG6 with Zeitraum-ID 1 (mako-twin, 55658).
3519    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3520        continues_previous_period(ctx, &["Z48", "Z55"])
3521    }
3522
3523    /// [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...
3524    // HAND-EDITED: as [134], for Z47/Z54.
3525    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3526        continues_previous_period(ctx, &["Z47", "Z54"])
3527    }
3528
3529    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3530    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3531        // HAND-EDITED: the CCI and the CAV of one SG10.
3532        sg10_has(ctx, false, |segs| {
3533            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "EHZ")
3534        })
3535    }
3536
3537    /// [137] Nicht bei Neuanlage
3538    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3539        // Check: not a Neuanlage (new registration) process
3540        // STS+7 with transaktionsgrund containing Neuanlage indicators
3541        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3542        let is_neuanlage = sts_segs.iter().any(|s| {
3543            s.elements
3544                .get(2)
3545                .and_then(|e| e.first())
3546                .is_some_and(|v| v == "E01" || v == "E03")
3547        });
3548        ConditionResult::from(!is_neuanlage)
3549    }
3550
3551    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3552    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3553        let qty_segs = ctx.find_segments("QTY");
3554        let found = qty_segs.iter().any(|s| {
3555            s.elements
3556                .first()
3557                .and_then(|e| e.first())
3558                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3559        });
3560        ConditionResult::from(found)
3561    }
3562
3563    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3564    // 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)
3565    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3566        let nav = match ctx.navigator {
3567            Some(n) => n,
3568            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3569        };
3570        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3571        for i in 0..sg8_count {
3572            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3573            for j in 0..sg10_count {
3574                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3575                if cavs.iter().any(|s| {
3576                    s.elements
3577                        .first()
3578                        .and_then(|e: &Vec<String>| e.first())
3579                        .is_some_and(|v: &String| v == "IVA")
3580                }) {
3581                    return ConditionResult::False;
3582                }
3583            }
3584        }
3585        ConditionResult::True
3586    }
3587
3588    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3589    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3590        let qty_segs = ctx.find_segments("QTY");
3591        let found = qty_segs.iter().any(|s| {
3592            s.elements
3593                .first()
3594                .and_then(|e| e.first())
3595                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3596        });
3597        ConditionResult::from(found)
3598    }
3599
3600    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3601    // HAND-EDITED (#392, reported by mako-twin): the CAVs of this SG8 (the
3602    // Wandler whose Faktor is asked for), not of the message: an MUW beside
3603    // an MIW in another SG8 was asked for a Faktor.
3604    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3605        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
3606        let found = cav_segs.iter().any(|s| {
3607            s.elements
3608                .first()
3609                .and_then(|e| e.first())
3610                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3611        });
3612        ConditionResult::from(found)
3613    }
3614
3615    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3616    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3617        let pia_segs = ctx.find_segments("PIA");
3618        let found = pia_segs.iter().any(|s| {
3619            s.elements
3620                .first()
3621                .and_then(|e| e.first())
3622                .is_some_and(|v| v == "Z01")
3623                && s.elements
3624                    .get(1)
3625                    .and_then(|e| e.get(1))
3626                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3627        });
3628        ConditionResult::from(found)
3629    }
3630
3631    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3632    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3633        let sts_segs = ctx.find_segments("STS");
3634        let found = sts_segs.iter().any(|s| {
3635            s.elements
3636                .first()
3637                .and_then(|e| e.first())
3638                .is_some_and(|v| v == "7")
3639                && s.elements
3640                    .get(2)
3641                    .and_then(|e| e.first())
3642                    .is_some_and(|v| v == "ZX3")
3643        });
3644        ConditionResult::from(found)
3645    }
3646
3647    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3648    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3649        let sts_segs = ctx.find_segments("STS");
3650        let found = sts_segs.iter().any(|s| {
3651            s.elements
3652                .first()
3653                .and_then(|e| e.first())
3654                .is_some_and(|v| v == "7")
3655                && s.elements
3656                    .get(2)
3657                    .and_then(|e| e.first())
3658                    .is_some_and(|v| v == "ZAN")
3659        });
3660        ConditionResult::from(found)
3661    }
3662
3663    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3664    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3665        let sts_segs = ctx.find_segments("STS");
3666        let found = sts_segs.iter().any(|s| {
3667            s.elements
3668                .first()
3669                .and_then(|e| e.first())
3670                .is_some_and(|v| v == "7")
3671                && s.elements
3672                    .get(2)
3673                    .and_then(|e| e.first())
3674                    .is_some_and(|v| v == "ZX2")
3675        });
3676        ConditionResult::from(found)
3677    }
3678
3679    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3680    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3681        let sts_segs = ctx.find_segments("STS");
3682        let found = sts_segs.iter().any(|s| {
3683            s.elements
3684                .first()
3685                .and_then(|e| e.first())
3686                .is_some_and(|v| v == "7")
3687                && s.elements
3688                    .get(2)
3689                    .and_then(|e| e.first())
3690                    .is_some_and(|v| v == "ZA0")
3691        });
3692        ConditionResult::from(found)
3693    }
3694
3695    /// [147] Wenn in Anfrage vorhanden
3696    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3697        // TODO: implement
3698        ConditionResult::Unknown
3699    }
3700
3701    /// [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
3702    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3703        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3704        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3705        let found = pia_segs.iter().any(|s| {
3706            s.elements
3707                .first()
3708                .and_then(|e| e.first())
3709                .is_some_and(|v| v == "Z02")
3710                && s.elements
3711                    .get(1)
3712                    .and_then(|e| e.first())
3713                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3714        });
3715        ConditionResult::from(found)
3716    }
3717
3718    /// [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...
3719    // 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)
3720    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3721        use std::collections::{HashMap, HashSet};
3722        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3723        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3724        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3725        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3726        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3727        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3728        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3729            if q == "Z45" && !zid.is_empty() {
3730                idx_to_zeitraum.insert(*gi, zid.clone());
3731            }
3732        }
3733        if idx_to_zeitraum.is_empty() {
3734            return ConditionResult::False;
3735        }
3736        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3737        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3738        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3739            if qual == "Z02" {
3740                if let Some(zid) = idx_to_zeitraum.get(gi) {
3741                    if let Some((_, prod)) = pia_prods.get(i) {
3742                        if !prod.is_empty() {
3743                            zeitraum_products
3744                                .entry(zid.clone())
3745                                .or_default()
3746                                .insert(prod.clone());
3747                        }
3748                    }
3749                }
3750            }
3751        }
3752        ConditionResult::from(
3753            zeitraum_products
3754                .values()
3755                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
3756        )
3757    }
3758
3759    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
3760    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
3761        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3762        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3763        let found = pia_segs.iter().any(|s| {
3764            s.elements
3765                .first()
3766                .and_then(|e| e.first())
3767                .is_some_and(|v| v == "Z02")
3768                && s.elements
3769                    .get(1)
3770                    .and_then(|e| e.first())
3771                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
3772        });
3773        ConditionResult::from(found)
3774    }
3775
3776    /// [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...
3777    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
3778        ctx.external.evaluate("code_list_membership_check")
3779    }
3780
3781    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
3782    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
3783        let ftx_segs = ctx.find_segments("FTX");
3784        let found = ftx_segs.iter().any(|s| {
3785            s.elements
3786                .first()
3787                .and_then(|e| e.first())
3788                .is_some_and(|v| v == "Z28")
3789        });
3790        ConditionResult::from(!found)
3791    }
3792
3793    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
3794    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
3795        let ftx_segs = ctx.find_segments("FTX");
3796        let found = ftx_segs.iter().any(|s| {
3797            s.elements
3798                .first()
3799                .and_then(|e| e.first())
3800                .is_some_and(|v| v == "Z27")
3801        });
3802        ConditionResult::from(!found)
3803    }
3804
3805    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
3806    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
3807        let ftx_segs = ctx.find_segments("FTX");
3808        let found = ftx_segs.iter().any(|s| {
3809            s.elements
3810                .first()
3811                .and_then(|e| e.first())
3812                .is_some_and(|v| v == "Z17")
3813        });
3814        ConditionResult::from(found)
3815    }
3816
3817    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
3818    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
3819        let sts_segs = ctx.find_segments("STS");
3820        let found = sts_segs.iter().any(|s| {
3821            s.elements
3822                .first()
3823                .and_then(|e| e.first())
3824                .is_some_and(|v| v == "7")
3825                && s.elements
3826                    .get(2)
3827                    .and_then(|e| e.first())
3828                    .is_some_and(|v| v == "E03")
3829                && s.elements
3830                    .get(3)
3831                    .and_then(|e| e.first())
3832                    .is_some_and(|v| v == "ZW8")
3833        });
3834        ConditionResult::from(found)
3835    }
3836
3837    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
3838    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
3839        let sts_segs = ctx.find_segments("STS");
3840        let found = sts_segs.iter().any(|s| {
3841            s.elements
3842                .first()
3843                .and_then(|e| e.first())
3844                .is_some_and(|v| v == "7")
3845                && s.elements
3846                    .get(2)
3847                    .and_then(|e| e.first())
3848                    .is_some_and(|v| v == "E03")
3849                && s.elements
3850                    .get(3)
3851                    .and_then(|e| e.first())
3852                    .is_some_and(|v| v == "ZW9")
3853        });
3854        ConditionResult::from(found)
3855    }
3856
3857    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
3858    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
3859        let sts_segs = ctx.find_segments("STS");
3860        let found = sts_segs.iter().any(|s| {
3861            s.elements
3862                .first()
3863                .and_then(|e| e.first())
3864                .is_some_and(|v| v == "7")
3865                && s.elements
3866                    .get(2)
3867                    .and_then(|e| e.first())
3868                    .is_some_and(|v| v == "E03")
3869                && s.elements
3870                    .get(3)
3871                    .and_then(|e| e.first())
3872                    .is_some_and(|v| v == "ZX0")
3873        });
3874        ConditionResult::from(found)
3875    }
3876
3877    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
3878    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
3879        let sts_segs = ctx.find_segments("STS");
3880        let found = sts_segs.iter().any(|s| {
3881            s.elements
3882                .first()
3883                .and_then(|e| e.first())
3884                .is_some_and(|v| v == "7")
3885                && s.elements
3886                    .get(2)
3887                    .and_then(|e| e.first())
3888                    .is_some_and(|v| v == "E03")
3889                && s.elements
3890                    .get(3)
3891                    .and_then(|e| e.first())
3892                    .is_some_and(|v| v == "ZX1")
3893        });
3894        ConditionResult::from(found)
3895    }
3896
3897    /// [165] Wenn bekannt
3898    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
3899        // TODO: implement
3900        ConditionResult::Unknown
3901    }
3902
3903    /// [166] Wenn vorhanden
3904    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
3905        // "If present" — always true when the field is being validated
3906        ConditionResult::True
3907    }
3908
3909    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
3910    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
3911        let loc_segs = ctx.find_segments("LOC");
3912        let found = loc_segs.iter().any(|s| {
3913            s.elements
3914                .first()
3915                .and_then(|e| e.first())
3916                .is_some_and(|v| v == "Z21")
3917        });
3918        ConditionResult::from(found)
3919    }
3920
3921    /// [170] Wenn Anschrift der Marktlokation vorhanden
3922    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
3923        // TODO: implement
3924        ConditionResult::Unknown
3925    }
3926
3927    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
3928    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
3929        let qty_segs = ctx.find_segments("QTY");
3930        let found = qty_segs.iter().any(|s| {
3931            s.elements
3932                .first()
3933                .and_then(|e| e.first())
3934                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
3935        });
3936        ConditionResult::from(found)
3937    }
3938
3939    /// [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 (...
3940    // 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)
3941    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
3942        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
3943        // RFF+Z33 without DE1156.
3944        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
3945        if !kinds
3946            .iter()
3947            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
3948        {
3949            return ConditionResult::Unknown;
3950        }
3951        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
3952        if z33.is_empty() {
3953            return ConditionResult::Unknown;
3954        }
3955        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
3956    }
3957
3958    /// [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...
3959    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
3960        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3961        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
3962        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
3963        let found = rff_z33.iter().any(|s| {
3964            s.elements
3965                .first()
3966                .and_then(|e| e.get(2))
3967                .is_some_and(|v| !v.is_empty())
3968        });
3969        ConditionResult::from(found)
3970    }
3971
3972    /// [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...
3973    // 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)
3974    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
3975        // HAND-EDITED: "in einem weiteren SG8 SEQ+Z58 mit derselben fortlaufenden Nummer" — compared within the enclosing SG4 when the scope
3976        // reaches an SG8; the message as before otherwise.
3977        if let Some(scope) = ctx.enclosing_scope("SG8") {
3978            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
3979                ctx,
3980                scope,
3981                &["Z58"],
3982                true,
3983                false,
3984            ));
3985        }
3986        let nav = match ctx.navigator {
3987            Some(n) => n,
3988            None => return ConditionResult::Unknown,
3989        };
3990        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3991        let mut entries: Vec<(usize, String, String)> = Vec::new();
3992        for i in 0..sg8_count {
3993            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
3994            if !seqs.iter().any(|s| {
3995                s.elements
3996                    .first()
3997                    .and_then(|e: &Vec<String>| e.first())
3998                    .is_some_and(|v: &String| v == "Z58")
3999            }) {
4000                continue;
4001            }
4002            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4003            for rff in &rffs {
4004                let elems = match rff.elements.first() {
4005                    Some(e) => e,
4006                    None => continue,
4007                };
4008                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4009                    continue;
4010                }
4011                let objektcode = elems.get(1).cloned().unwrap_or_default();
4012                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4013                if !objektcode.is_empty() {
4014                    entries.push((i, objektcode, fort_nr));
4015                }
4016            }
4017        }
4018        for a in 0..entries.len() {
4019            for b in (a + 1)..entries.len() {
4020                if entries[a].0 != entries[b].0
4021                    && entries[a].1 == entries[b].1
4022                    && entries[a].2 == entries[b].2
4023                {
4024                    return ConditionResult::True;
4025                }
4026            }
4027        }
4028        ConditionResult::False
4029    }
4030
4031    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4032    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4033        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4034    }
4035
4036    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4037    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4038        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4039    }
4040
4041    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4042    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4043        let loc_segs = ctx.find_segments("LOC");
4044        let found = loc_segs.iter().any(|s| {
4045            s.elements
4046                .first()
4047                .and_then(|e| e.first())
4048                .is_some_and(|v| v == "Z18")
4049        });
4050        ConditionResult::from(found)
4051    }
4052
4053    /// [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...
4054    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4055        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4056        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4057        // a group scope: "derselben SG8" cannot be told apart then.
4058        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4059        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4060            return ConditionResult::Unknown;
4061        };
4062        let seq_of = |segs: &[OwnedSegment]| {
4063            segs.iter()
4064                .find(|s| s.id == "SEQ")
4065                .map(|s| s.get_element(0).to_string())
4066        };
4067        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4068            segs.iter()
4069                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4070                .map(|s| s.get_component(0, 1).to_string())
4071                .filter(|v| !v.is_empty())
4072                .collect()
4073        };
4074        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4075        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4076            return ConditionResult::False;
4077        };
4078        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4079        if own.is_empty() {
4080            return ConditionResult::False;
4081        }
4082        let found = siblings.iter().any(|path| {
4083            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4084                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4085                    .iter()
4086                    .any(|code| own.contains(code))
4087        });
4088        ConditionResult::from(found)
4089    }
4090
4091    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4092    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4093        let qty_segs = ctx.find_segments("QTY");
4094        let found = qty_segs.iter().any(|s| {
4095            s.elements
4096                .first()
4097                .and_then(|e| e.first())
4098                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4099        });
4100        ConditionResult::from(found)
4101    }
4102
4103    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4104    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4105        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
4106        // and the CCI+Z01++Z82 the condition names is part of it: a second
4107        // Messlokation (CCI+Z01++Z81) needs no Messstellenbetreiber Alt.
4108        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4109        let seq_z18 = seq_segs.iter().any(|s| {
4110            s.elements
4111                .first()
4112                .and_then(|e| e.first())
4113                .is_some_and(|v| v == "Z18")
4114        });
4115        let cci_z82 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4116            s.elements
4117                .first()
4118                .and_then(|e| e.first())
4119                .is_some_and(|v| v == "Z01")
4120                && s.elements
4121                    .get(2)
4122                    .and_then(|e| e.first())
4123                    .is_some_and(|v| v == "Z82")
4124        });
4125        ConditionResult::from(seq_z18 && cci_z82)
4126    }
4127
4128    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4129    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4130        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4131        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4132        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4133        let found = pia_segs.iter().any(|s| {
4134            s.elements
4135                .first()
4136                .and_then(|e| e.first())
4137                .is_some_and(|v| v == "5")
4138                && s.elements
4139                    .get(1)
4140                    .and_then(|e| e.first())
4141                    .is_some_and(|v| v == "9991000000721")
4142        });
4143        ConditionResult::from(found)
4144    }
4145
4146    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4147    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4148        // HAND-EDITED (#343): the list's own STS+E01, in the SG4 opened by IDE+Z01; a
4149        // Vorgang's STS+E01 (IDE+24) answers the Vorgang, not the list.
4150        let mut in_list = false;
4151        let mut found = false;
4152        for s in ctx.segments.iter() {
4153            match s.id.as_str() {
4154                "IDE" => in_list = s.get_element(0) == "Z01",
4155                "STS" if in_list && s.get_element(0) == "E01" => found = true,
4156                _ => {}
4157            }
4158        }
4159        ConditionResult::from(!found)
4160    }
4161
4162    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4163    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4164        let sts_segs = ctx.find_segments("STS");
4165        let found = sts_segs.iter().any(|s| {
4166            s.elements
4167                .first()
4168                .and_then(|e| e.first())
4169                .is_some_and(|v| v == "7")
4170                && s.elements
4171                    .get(3)
4172                    .and_then(|e| e.first())
4173                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4174        });
4175        ConditionResult::from(found)
4176    }
4177
4178    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4179    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4180        let nav = match ctx.navigator {
4181            Some(n) => n,
4182            None => {
4183                let cavs = ctx.find_segments("CAV");
4184                return ConditionResult::from(cavs.iter().any(|s| {
4185                    s.elements
4186                        .first()
4187                        .and_then(|e: &Vec<String>| e.first())
4188                        .is_some_and(|v: &String| v == "E06")
4189                }));
4190            }
4191        };
4192        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4193        for i in 0..sg8_count {
4194            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4195            for j in 0..sg10_count {
4196                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4197                let has_cci_zb5 = ccis.iter().any(|s| {
4198                    s.elements
4199                        .get(2)
4200                        .and_then(|e: &Vec<String>| e.first())
4201                        .is_some_and(|v: &String| v == "ZB5")
4202                });
4203                if !has_cci_zb5 {
4204                    continue;
4205                }
4206                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4207                if cavs.iter().any(|s| {
4208                    s.elements
4209                        .first()
4210                        .and_then(|e: &Vec<String>| e.first())
4211                        .is_some_and(|v: &String| v == "E06")
4212                }) {
4213                    return ConditionResult::True;
4214                }
4215            }
4216        }
4217        ConditionResult::False
4218    }
4219
4220    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4221    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4222        let cci_segs = ctx.find_segments("CCI");
4223        let has_cci = cci_segs.iter().any(|s| {
4224            s.elements
4225                .iter()
4226                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4227        });
4228        let cav_segs = ctx.find_segments("CAV");
4229        let has_cav = cav_segs.iter().any(|s| {
4230            s.elements
4231                .first()
4232                .and_then(|e| e.first())
4233                .is_some_and(|v| v == "E05")
4234        });
4235        ConditionResult::from(has_cci && has_cav)
4236    }
4237
4238    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4239    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4240        let cci_segs = ctx.find_segments("CCI");
4241        let has_cci = cci_segs.iter().any(|s| {
4242            s.elements
4243                .iter()
4244                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4245        });
4246        let cav_segs = ctx.find_segments("CAV");
4247        let has_cav = cav_segs.iter().any(|s| {
4248            s.elements
4249                .first()
4250                .and_then(|e| e.first())
4251                .is_some_and(|v| v == "E04")
4252        });
4253        ConditionResult::from(has_cci && has_cav)
4254    }
4255
4256    /// [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...
4257    // 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)
4258    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4259        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4260        // reaches an SG8; the message as before otherwise.
4261        if let Some(scope) = ctx.enclosing_scope("SG8") {
4262            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4263                ctx,
4264                scope,
4265                &["Z58", "ZC9", "ZD0"],
4266                true,
4267                true,
4268            ));
4269        }
4270        let nav = match ctx.navigator {
4271            Some(n) => n,
4272            None => return ConditionResult::Unknown,
4273        };
4274        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4275        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4276        for i in 0..sg8_count {
4277            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4278            let seq_opt = seqs.iter().find(|s| {
4279                s.elements
4280                    .first()
4281                    .and_then(|e: &Vec<String>| e.first())
4282                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4283            });
4284            let seq = match seq_opt {
4285                Some(s) => s,
4286                None => continue,
4287            };
4288            let seq_code = seq
4289                .elements
4290                .first()
4291                .and_then(|e: &Vec<String>| e.first())
4292                .cloned()
4293                .unwrap_or_default();
4294            let zeitraum_id = seq
4295                .elements
4296                .get(1)
4297                .and_then(|e: &Vec<String>| e.first())
4298                .cloned()
4299                .unwrap_or_default();
4300            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4301            for rff in &rffs {
4302                let elems = match rff.elements.first() {
4303                    Some(e) => e,
4304                    None => continue,
4305                };
4306                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4307                    continue;
4308                }
4309                let objektcode = elems.get(1).cloned().unwrap_or_default();
4310                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4311                if !objektcode.is_empty() {
4312                    entries.push((
4313                        i,
4314                        seq_code.clone(),
4315                        zeitraum_id.clone(),
4316                        objektcode,
4317                        fort_nr,
4318                    ));
4319                }
4320            }
4321        }
4322        for a in 0..entries.len() {
4323            for b in (a + 1)..entries.len() {
4324                if entries[a].0 != entries[b].0
4325                    && entries[a].1 == entries[b].1
4326                    && entries[a].2 == entries[b].2
4327                    && entries[a].3 == entries[b].3
4328                    && entries[a].4 == entries[b].4
4329                {
4330                    return ConditionResult::True;
4331                }
4332            }
4333        }
4334        ConditionResult::False
4335    }
4336
4337    /// [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 (...
4338    // 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)
4339    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4340        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4341        // reaches an SG8; the message as before otherwise.
4342        if let Some(scope) = ctx.enclosing_scope("SG8") {
4343            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4344            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4345                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4346            }));
4347        }
4348        let nav = match ctx.navigator {
4349            Some(n) => n,
4350            None => return ConditionResult::Unknown,
4351        };
4352        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4353        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4354        for i in 0..sg8_count {
4355            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4356            if !seqs.iter().any(|s| {
4357                s.elements
4358                    .first()
4359                    .and_then(|e: &Vec<String>| e.first())
4360                    .is_some_and(|v: &String| v == "Z01")
4361            }) {
4362                continue;
4363            }
4364            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4365            let has_prog = (0..sg10_count).any(|j| {
4366                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4367                let has_za6 = ccis.iter().any(|s| {
4368                    s.elements
4369                        .get(2)
4370                        .and_then(|e: &Vec<String>| e.first())
4371                        .is_some_and(|v: &String| v == "ZA6")
4372                });
4373                if !has_za6 {
4374                    return false;
4375                }
4376                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4377                cavs.iter().any(|s| {
4378                    s.elements
4379                        .first()
4380                        .and_then(|e: &Vec<String>| e.first())
4381                        .is_some_and(|v: &String| v == "E02")
4382                })
4383            });
4384            if !has_prog {
4385                continue;
4386            }
4387            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4388            for rff in &rffs {
4389                let elems = match rff.elements.first() {
4390                    Some(e) => e,
4391                    None => continue,
4392                };
4393                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4394                    continue;
4395                }
4396                if let Some(malo_id) = elems.get(1) {
4397                    if !malo_id.is_empty() {
4398                        qualifying_malo_ids.push(malo_id.clone());
4399                    }
4400                }
4401            }
4402        }
4403        if qualifying_malo_ids.is_empty() {
4404            return ConditionResult::False;
4405        }
4406        for i in 0..sg8_count {
4407            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4408            for rff in &rffs {
4409                let elems = match rff.elements.first() {
4410                    Some(e) => e,
4411                    None => continue,
4412                };
4413                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4414                    continue;
4415                }
4416                if let Some(malo_id) = elems.get(1) {
4417                    if qualifying_malo_ids.contains(malo_id) {
4418                        return ConditionResult::True;
4419                    }
4420                }
4421            }
4422        }
4423        ConditionResult::False
4424    }
4425
4426    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4427    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4428        let sts_segs = ctx.find_segments("STS");
4429        let found = sts_segs.iter().any(|s| {
4430            s.elements
4431                .first()
4432                .and_then(|e| e.first())
4433                .is_some_and(|v| v == "7")
4434                && s.elements
4435                    .get(2)
4436                    .and_then(|e| e.first())
4437                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4438        });
4439        ConditionResult::from(found)
4440    }
4441
4442    /// [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
4443    // 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)
4444    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4445        let nav = match ctx.navigator {
4446            Some(n) => n,
4447            None => {
4448                return ctx.filtered_parent_child_has_qualifier(
4449                    &["SG4", "SG8"],
4450                    "SEQ",
4451                    0,
4452                    "Z01",
4453                    "SG10",
4454                    "CCI",
4455                    2,
4456                    "ZA6",
4457                )
4458            }
4459        };
4460        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4461        for i in 0..sg8_count {
4462            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4463            if !seqs.iter().any(|s| {
4464                s.elements
4465                    .first()
4466                    .and_then(|e: &Vec<String>| e.first())
4467                    .is_some_and(|v: &String| v == "Z01")
4468            }) {
4469                continue;
4470            }
4471            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4472            for j in 0..sg10_count {
4473                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4474                if !ccis.iter().any(|s| {
4475                    s.elements
4476                        .get(2)
4477                        .and_then(|e: &Vec<String>| e.first())
4478                        .is_some_and(|v: &String| v == "ZA6")
4479                }) {
4480                    continue;
4481                }
4482                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4483                if cavs.iter().any(|s| {
4484                    s.elements
4485                        .first()
4486                        .and_then(|e: &Vec<String>| e.first())
4487                        .is_some_and(|v: &String| v == "E02")
4488                }) {
4489                    return ConditionResult::True;
4490                }
4491            }
4492        }
4493        ConditionResult::False
4494    }
4495
4496    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4497    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4498        // TODO: implement
4499        ConditionResult::Unknown
4500    }
4501
4502    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4503    // 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)
4504    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4505        use std::collections::HashSet;
4506        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4507        let loc_zeitraum_ids: HashSet<String> = ctx
4508            .find_segments("LOC")
4509            .into_iter()
4510            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4511            .filter(|v| !v.is_empty())
4512            .collect();
4513        if loc_zeitraum_ids.is_empty() {
4514            return ConditionResult::False;
4515        }
4516        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4517        let matching_seq = ctx
4518            .find_segments_with_qualifier("SEQ", 0, "Z78")
4519            .into_iter()
4520            .any(|s| {
4521                s.elements
4522                    .get(1)
4523                    .and_then(|e: &Vec<String>| e.first())
4524                    .is_some_and(|v: &String| {
4525                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4526                    })
4527            });
4528        if !matching_seq {
4529            return ConditionResult::False;
4530        }
4531        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4532        ctx.has_qualifier("RFF", 0, "Z39")
4533    }
4534
4535    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4536    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4537        let sts_segs = ctx.find_segments("STS");
4538        let found = sts_segs.iter().any(|s| {
4539            s.elements
4540                .first()
4541                .and_then(|e| e.first())
4542                .is_some_and(|v| v == "7")
4543                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4544                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4545                })
4546        });
4547        ConditionResult::from(found)
4548    }
4549
4550    /// [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
4551    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4552        // HAND-EDITED (#392): the Zähleinrichtungsdaten of the meter this RFF
4553        // names (its number in the SG10 CAV+Z30 DE7110), with this SG8's
4554        // Zeitraum-ID and quality — erwartet (ZA3) beside an erwartete SG8
4555        // (ZA5, ZB7, …), im System vorhanden (ZA4) beside a vorhandene one —
4556        // not any Zähleinrichtungsdaten of the message. No such SG8: no
4557        // RFF+Z14 for that meter either.
4558        let Some(number) = rff_reference(ctx) else {
4559            return ConditionResult::Unknown;
4560        };
4561        let Some(blocks) = sg8_blocks(ctx) else {
4562            return ConditionResult::Unknown;
4563        };
4564        let own = ctx
4565            .enclosing_scope("SG8")
4566            .and_then(|scope| ctx.find_segments_at(scope, "SEQ").into_iter().next());
4567        let own_seq = own.as_ref().map(|s| s.get_element(0).to_string());
4568        let own_zeitraum = own
4569            .as_ref()
4570            .and_then(|s| component(s, 1, 0).map(str::to_string));
4571        let quality = |seq: &str| match seq {
4572            "ZA3" | "ZA5" | "ZB7" | "ZB9" | "ZC1" | "ZC3" | "ZG6" | "ZH3" => Some(true),
4573            "ZA4" | "ZA6" | "ZB8" | "ZC0" | "ZC2" | "ZC4" | "ZG7" | "ZH4" => Some(false),
4574            _ => None,
4575        };
4576        let meter = blocks
4577            .iter()
4578            .filter(|b| matches!(b.seq.as_str(), "Z03" | "ZA3" | "ZA4"))
4579            .filter(
4580                |b| match (quality(&b.seq), own_seq.as_deref().and_then(quality)) {
4581                    (Some(theirs), Some(ours)) => theirs == ours,
4582                    _ => true,
4583                },
4584            )
4585            .filter(|b| match own_zeitraum.as_deref() {
4586                Some(id) if !id.is_empty() && !b.zeitraum.is_empty() => b.zeitraum == id,
4587                _ => true,
4588            })
4589            .find(|b| {
4590                b.sg10.iter().any(|(_, cavs)| {
4591                    cavs.iter().any(|c| {
4592                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
4593                    })
4594                })
4595            });
4596        ConditionResult::from(meter.map_or(true, |b| {
4597            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
4598        }))
4599    }
4600
4601    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4602    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4603        let loc_segs = ctx.find_segments("LOC");
4604        let found = loc_segs.iter().any(|s| {
4605            s.elements
4606                .first()
4607                .and_then(|e| e.first())
4608                .is_some_and(|v| v == "Z19")
4609        });
4610        ConditionResult::from(found)
4611    }
4612
4613    /// [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
4614    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4615        let sts_segs = ctx.find_segments("STS");
4616        let found = sts_segs.iter().any(|s| {
4617            s.elements
4618                .first()
4619                .and_then(|e| e.first())
4620                .is_some_and(|v| v == "7")
4621                && s.elements
4622                    .get(2)
4623                    .and_then(|e| e.first())
4624                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4625        });
4626        ConditionResult::from(!found)
4627    }
4628
4629    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4630    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4631        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4632    }
4633
4634    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4635    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4636        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4637        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4638        let found = cci_segs.iter().any(|s| {
4639            s.elements
4640                .get(2)
4641                .and_then(|e| e.first())
4642                .is_some_and(|v| v == "ZA6")
4643        });
4644        ConditionResult::from(found)
4645    }
4646
4647    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4648    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4649        let nad_segs = ctx.find_segments("NAD");
4650        let found = nad_segs.iter().any(|s| {
4651            s.elements
4652                .get(3)
4653                .and_then(|e| e.first())
4654                .is_some_and(|v| !v.is_empty())
4655        });
4656        ConditionResult::from(!found)
4657    }
4658
4659    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4660    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4661        let nad_segs = ctx.find_segments("NAD");
4662        let found = nad_segs.iter().any(|s| {
4663            s.elements
4664                .first()
4665                .and_then(|e| e.first())
4666                .is_some_and(|v| v == "Z09")
4667        });
4668        ConditionResult::from(found)
4669    }
4670
4671    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4672    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4673        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4674        // SG10s included). Both segments are looked for anywhere in that SG8,
4675        // not within one SG10.
4676        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4677            s.elements
4678                .get(2)
4679                .and_then(|e| e.first())
4680                .is_some_and(|v| v == "E13")
4681        });
4682        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4683            s.elements
4684                .first()
4685                .and_then(|e| e.first())
4686                .is_some_and(|v| v == "MME")
4687        });
4688        ConditionResult::from(has_cci_e13 && has_cav_mme)
4689    }
4690
4691    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4692    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4693        // HAND-EDITED: the CCI and the CAV of one SG10; Z08 is DE7110 of the CAV.
4694        sg10_has(ctx, false, |segs| {
4695            sg4_instances::has_cci(segs, "", "Z88")
4696                && segs.iter().any(|s| {
4697                    s.id == "CAV" && s.get_element(0) == "Z74" && s.get_component(0, 3) == "Z08"
4698                })
4699        })
4700    }
4701
4702    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4703    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4704        let rff_segs = ctx.find_segments("RFF");
4705        let found = rff_segs.iter().any(|s| {
4706            s.elements
4707                .first()
4708                .and_then(|e| e.first())
4709                .is_some_and(|v| v == "Z49")
4710        });
4711        ConditionResult::from(found)
4712    }
4713
4714    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4715    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4716        let nad_segs = ctx.find_segments("NAD");
4717        let found = nad_segs.iter().any(|s| {
4718            s.elements
4719                .first()
4720                .and_then(|e| e.first())
4721                .is_some_and(|v| v == "Z65")
4722        });
4723        ConditionResult::from(found)
4724    }
4725
4726    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4727    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4728        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4729        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4730        let has_seq = seq_segs.iter().any(|s| {
4731            s.elements
4732                .first()
4733                .and_then(|e| e.first())
4734                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4735        });
4736        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4737        let has_cci = cci_segs.iter().any(|s| {
4738            s.elements
4739                .first()
4740                .and_then(|e| e.first())
4741                .is_some_and(|v| v == "Z30")
4742                && s.elements
4743                    .get(2)
4744                    .and_then(|e| e.first())
4745                    .is_some_and(|v| v == "Z06")
4746        });
4747        ConditionResult::from(has_seq && has_cci)
4748    }
4749
4750    /// [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)
4751    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4752        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4753        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4754        if !has_seq_z15 {
4755            return ConditionResult::True;
4756        }
4757        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4758        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4759    }
4760
4761    /// [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...
4762    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4763    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4764    // held for every message with a Marktlokation. The scope never narrows to
4765    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4766    // other than SEQ+Z01, as in the neighbouring conditions.
4767    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4768        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4769        // reaches an SG8; the message as before otherwise.
4770        if let Some(scope) = ctx.enclosing_scope("SG8") {
4771            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4772            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4773                sg4_instances::has_cci(s, "Z30", "Z06")
4774            }));
4775        }
4776        let Some(blocks) = sg8_blocks(ctx) else {
4777            return ConditionResult::Unknown;
4778        };
4779        let loc_ids: Vec<String> = ctx
4780            .find_segments_with_qualifier("LOC", 0, "Z16")
4781            .iter()
4782            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4783            .collect();
4784        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4785            b.malo_refs
4786                .iter()
4787                .filter(|id| loc_ids.contains(id))
4788                .cloned()
4789                .collect()
4790        };
4791        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
4792        let erzeugung: Vec<String> = blocks
4793            .iter()
4794            .filter(|b| b.seq == "Z01")
4795            .filter(|b| {
4796                b.sg10.iter().any(|(ccis, _)| {
4797                    ccis.iter().any(|c| {
4798                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
4799                    })
4800                })
4801            })
4802            .flat_map(|b| malo_refs(b))
4803            .collect();
4804        let referenced = blocks
4805            .iter()
4806            .filter(|b| b.seq != "Z01")
4807            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
4808        ConditionResult::from(referenced)
4809    }
4810
4811    /// [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)
4812    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
4813        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
4814        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
4815        if !has_seq_ze7 {
4816            return ConditionResult::True;
4817        }
4818        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
4819    }
4820
4821    /// [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
4822    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
4823        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
4824        let r1 = ctx.filtered_parent_child_has_qualifier(
4825            &["SG4", "SG8"],
4826            "SEQ",
4827            0,
4828            "Z98",
4829            "SG10",
4830            "CAV",
4831            0,
4832            "E14",
4833        );
4834        if r1 == ConditionResult::True {
4835            return ConditionResult::True;
4836        }
4837        ctx.filtered_parent_child_has_qualifier(
4838            &["SG4", "SG8"],
4839            "SEQ",
4840            0,
4841            "Z98",
4842            "SG10",
4843            "CAV",
4844            0,
4845            "ETK",
4846        )
4847    }
4848
4849    /// [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)...
4850    // 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)
4851    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
4852        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4853        // reaches an SG8; the message as before otherwise.
4854        if let Some(scope) = ctx.enclosing_scope("SG8") {
4855            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4856                ctx,
4857                scope,
4858                &["Z58", "ZC9", "ZD0", "ZD6"],
4859                false,
4860                true,
4861            ));
4862        }
4863        use std::collections::HashSet;
4864        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
4865        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
4866        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4867        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
4868        // Collect per-SG8-instance: RFF qualifier and DE1154 value
4869        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
4870        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
4871        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
4872        let mut seen: HashSet<(String, String, String)> = HashSet::new();
4873        for (idx, seq_qual) in &seq_quals {
4874            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
4875                continue;
4876            }
4877            let zeitraum_id = seq_zeitraums
4878                .iter()
4879                .find(|(i, _)| i == idx)
4880                .map(|(_, v)| v.as_str())
4881                .unwrap_or("");
4882            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
4883            for (ridx, rff_qual) in rff_quals.iter() {
4884                if ridx != idx || rff_qual != "Z33" {
4885                    continue;
4886                }
4887                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
4888                    if obj_code.is_empty() {
4889                        continue;
4890                    }
4891                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
4892                    if seen.contains(&key) {
4893                        return ConditionResult::True;
4894                    }
4895                    seen.insert(key);
4896                }
4897            }
4898        }
4899        ConditionResult::False
4900    }
4901
4902    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
4903    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
4904        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
4905        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
4906        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
4907        sg10_has(ctx, true, |segs| {
4908            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z52")
4909        })
4910    }
4911
4912    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
4913    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
4914        ctx.external.evaluate("recipient_role_check")
4915    }
4916
4917    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
4918    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
4919        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
4920        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
4921        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
4922        sg10_has(ctx, true, |segs| {
4923            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z53")
4924        })
4925    }
4926
4927    /// [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
4928    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
4929        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4930        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
4931        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4932        let found = pia_segs.iter().any(|s| {
4933            s.elements
4934                .first()
4935                .and_then(|e| e.first())
4936                .is_some_and(|v| v == "Z02")
4937                && s.elements
4938                    .get(1)
4939                    .and_then(|e| e.first())
4940                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
4941        });
4942        ConditionResult::from(found)
4943    }
4944
4945    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
4946    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
4947        let ide_segs = ctx.find_segments("IDE");
4948        let found = ide_segs.iter().any(|s| {
4949            s.elements
4950                .first()
4951                .and_then(|e| e.first())
4952                .is_some_and(|v| v == "24")
4953        });
4954        ConditionResult::from(!found)
4955    }
4956
4957    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
4958    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
4959        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
4960        // SG10, when the scope reaches one; of any one SG10 otherwise.
4961        this_sg10_has(ctx, |segs| {
4962            segs.iter()
4963                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
4964                && sg4_instances::has_cav(segs, "Z65")
4965        })
4966    }
4967
4968    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
4969    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
4970        let seq_segs = ctx.find_segments("SEQ");
4971        let found = seq_segs.iter().any(|s| {
4972            s.elements
4973                .first()
4974                .and_then(|e| e.first())
4975                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
4976        });
4977        ConditionResult::from(found)
4978    }
4979
4980    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
4981    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
4982        ctx.external.evaluate("recipient_role_check")
4983    }
4984
4985    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
4986    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
4987        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4988        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4989        let found = cci_segs.iter().any(|s| {
4990            s.elements
4991                .first()
4992                .and_then(|e| e.first())
4993                .is_some_and(|v| v == "6")
4994                && s.elements
4995                    .get(2)
4996                    .and_then(|e| e.first())
4997                    .is_some_and(|v| v == "ZA8")
4998        });
4999        ConditionResult::from(found)
5000    }
5001
5002    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5003    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5004        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5005        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5006        let found = cci_segs.iter().any(|s| {
5007            s.elements
5008                .first()
5009                .and_then(|e| e.first())
5010                .is_some_and(|v| v == "6")
5011                && s.elements
5012                    .get(2)
5013                    .and_then(|e| e.first())
5014                    .is_some_and(|v| v == "ZA9")
5015        });
5016        ConditionResult::from(found)
5017    }
5018
5019    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5020    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5021        // TODO: implement
5022        ConditionResult::Unknown
5023    }
5024
5025    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5026    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5027        // Check: all STS+E01 DE1131 values are identical
5028        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5029        if sts_segs.len() <= 1 {
5030            return ConditionResult::True;
5031        }
5032        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5033        let all_same = sts_segs
5034            .iter()
5035            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5036        ConditionResult::from(all_same)
5037    }
5038
5039    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5040    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5041        ctx.external.evaluate("sender_role_check")
5042    }
5043
5044    /// [252] Wenn DE0068 vorhanden
5045    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5046        let unb_segs = ctx.find_segments("UNB");
5047        let found = unb_segs.iter().any(|s| {
5048            s.elements
5049                .get(4)
5050                .and_then(|e| e.first())
5051                .is_some_and(|v| !v.is_empty())
5052        });
5053        ConditionResult::from(found)
5054    }
5055
5056    /// [254] Es ist der Code \"AUA\" einzutragen
5057    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5058        // Check: the code "AUA" is present
5059        ConditionResult::True // AUA is always the required code when this condition applies
5060    }
5061
5062    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5063    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5064        // TODO: implement
5065        ConditionResult::Unknown
5066    }
5067
5068    /// [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
5069    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5070        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5071        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5072        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5073        let found = pia_segs.iter().any(|s| {
5074            s.elements
5075                .first()
5076                .and_then(|e| e.first())
5077                .is_some_and(|v| v == "5")
5078                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5079                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5080                })
5081        });
5082        ConditionResult::from(found)
5083    }
5084
5085    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5086    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5087        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5088        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5089        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5090        let found = pia_segs.iter().any(|s| {
5091            s.elements
5092                .first()
5093                .and_then(|e| e.first())
5094                .is_some_and(|v| v == "5")
5095                && s.elements
5096                    .get(1)
5097                    .and_then(|e| e.first())
5098                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5099        });
5100        ConditionResult::from(found)
5101    }
5102
5103    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5104    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5105        let pia_segs = ctx.find_segments("PIA");
5106        let found = pia_segs.iter().any(|s| {
5107            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5108                let prefixes = ["1-01", "1-03", "1-05"];
5109                prefixes.iter().any(|p| v.starts_with(p))
5110            })
5111        });
5112        ConditionResult::from(found)
5113    }
5114
5115    /// [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
5116    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5117        let pia_segs = ctx.find_segments("PIA");
5118        let found = pia_segs.iter().any(|s| {
5119            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5120                let prefixes = [
5121                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5122                ];
5123                prefixes.iter().any(|p| v.starts_with(p))
5124            })
5125        });
5126        ConditionResult::from(found)
5127    }
5128
5129    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5130    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5131        // TODO: implement
5132        ConditionResult::Unknown
5133    }
5134
5135    /// [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
5136    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5137    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5138    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5139    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5140        let Some(blocks) = sg8_blocks(ctx) else {
5141            return ConditionResult::Unknown;
5142        };
5143        let found = blocks
5144            .iter()
5145            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5146            .any(|b| {
5147                b.sg10.iter().any(|(ccis, cavs)| {
5148                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5149                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5150                })
5151            });
5152        ConditionResult::from(found)
5153    }
5154
5155    /// [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...
5156    // 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)
5157    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5158        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5159        // reaches an SG8; the message as before otherwise.
5160        if let Some(scope) = ctx.enclosing_scope("SG8") {
5161            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5162            return ConditionResult::from(z01s.iter().any(|p| {
5163                sg4_instances::sg10_at(ctx, p, |s| {
5164                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5165                })
5166            }));
5167        }
5168        let nav = match ctx.navigator {
5169            Some(n) => n,
5170            None => {
5171                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5172                return ctx.filtered_parent_child_has_qualifier(
5173                    &["SG4", "SG8"],
5174                    "SEQ",
5175                    0,
5176                    "Z01",
5177                    "SG10",
5178                    "CAV",
5179                    0,
5180                    "Z36",
5181                );
5182            }
5183        };
5184        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5185        for i in 0..sg8_count {
5186            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5187            for j in 0..sg10_count {
5188                // CCI+++ZA6 means elements[2][0] == "ZA6"
5189                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5190                let has_cci_za6 = ccis.iter().any(|s| {
5191                    s.elements
5192                        .get(2)
5193                        .and_then(|e: &Vec<String>| e.first())
5194                        .is_some_and(|v: &String| v == "ZA6")
5195                });
5196                if has_cci_za6 {
5197                    let cavs =
5198                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5199                    if cavs.iter().any(|s| {
5200                        s.elements
5201                            .first()
5202                            .and_then(|e: &Vec<String>| e.first())
5203                            .is_some_and(|v: &String| v == "Z36")
5204                    }) {
5205                        return ConditionResult::True;
5206                    }
5207                }
5208            }
5209        }
5210        ConditionResult::False
5211    }
5212
5213    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5214    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5215        let seq_segs = ctx.find_segments("SEQ");
5216        let found = seq_segs.iter().any(|s| {
5217            s.elements
5218                .first()
5219                .and_then(|e| e.first())
5220                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5221        });
5222        ConditionResult::from(!found)
5223    }
5224
5225    /// [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
5226    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5227        // Most European countries have postal codes — default to true
5228        ConditionResult::True
5229    }
5230
5231    /// [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
5232    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5233        // TODO: implement
5234        ConditionResult::Unknown
5235    }
5236
5237    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5238    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5239        let pia_segs = ctx.find_segments("PIA");
5240        let found = pia_segs.iter().any(|s| {
5241            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5242                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5243                prefixes.iter().any(|p| v.starts_with(p))
5244            })
5245        });
5246        ConditionResult::from(found)
5247    }
5248
5249    /// [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 ...
5250    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5251        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5252        // The PIA+5 OBIS must be an energy register `1-b:c.8.e` (c = 1..8;
5253        // 1.8.63 is one of them). A load profile such as 1-1:1.29.0 is not
5254        // (mako-twin, 55168: it demanded `produkt` and Z63 of a 1.29.0 item).
5255        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5256        let found = pia_segs.iter().any(|s| {
5257            s.elements
5258                .first()
5259                .and_then(|e| e.first())
5260                .is_some_and(|v| v == "5")
5261                && s.elements
5262                    .get(1)
5263                    .and_then(|e| e.first())
5264                    .is_some_and(|v| is_energy_register_obis(v))
5265        });
5266        ConditionResult::from(found)
5267    }
5268
5269    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5270    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5271        let sts_segs = ctx.find_segments("STS");
5272        let found = sts_segs.iter().any(|s| {
5273            s.elements
5274                .first()
5275                .and_then(|e| e.first())
5276                .is_some_and(|v| v == "7")
5277                && s.elements
5278                    .get(3)
5279                    .and_then(|e| e.first())
5280                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5281        });
5282        ConditionResult::from(found)
5283    }
5284
5285    /// [280] Nur MP-ID aus Sparte Strom
5286    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5287        // Check: Marktpartner-ID is from Sparte Strom
5288        // Approximate: check NAD+MS C082 for Strom MP-ID format
5289        ctx.external.evaluate("sector_check")
5290    }
5291
5292    /// [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
5293    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5294        // Check: PIA DE7140 has one of the specific codes
5295        let pia_segs = ctx.find_segments("PIA");
5296        let found = pia_segs.iter().any(|s| {
5297            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5298                v.starts_with("1-01-6-005")
5299                    || v.starts_with("1-01-9-001")
5300                    || v.starts_with("1-01-9-002")
5301                    || v.starts_with("1-07-3-001")
5302                    || v.starts_with("1-08-1-001")
5303                    || v.starts_with("1-08-3-001")
5304                    || v.starts_with("1-08-4-001")
5305                    || v.starts_with("1-08-4-002")
5306                    || v.starts_with("1-08-4-003")
5307                    || v.starts_with("1-08-5")
5308            })
5309        });
5310        ConditionResult::from(found)
5311    }
5312
5313    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5314    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5315    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5316    // Regenerating this function should preserve the shape, but re-verify
5317    // against the committed body if generate-conditions is run.
5318    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5319        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5320        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5321        //
5322        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5323        // narrows to the enclosing SG8 instance; otherwise falls back to
5324        // message-wide find_segments. Matches the AHB wording
5325        // "in derselben SG8 im PIA DE7140 ein Code...".
5326        use crate::eval::CodelistColumn;
5327        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5328            let is_z02 = s
5329                .elements
5330                .first()
5331                .and_then(|e| e.first())
5332                .is_some_and(|v| v == "Z02");
5333            if !is_z02 {
5334                return false;
5335            }
5336            let code = match s.elements.get(1).and_then(|e| e.first()) {
5337                Some(c) if !c.is_empty() => c,
5338                _ => return false,
5339            };
5340            ctx.codelist
5341                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5342                .unwrap_or(false)
5343        });
5344        ConditionResult::from(any_match)
5345    }
5346
5347    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5348    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5349        // Check: PIA DE7140 starts with 1-08-3-AGS
5350        let pia_segs = ctx.find_segments("PIA");
5351        let found = pia_segs.iter().any(|s| {
5352            s.elements
5353                .get(1)
5354                .and_then(|e| e.first())
5355                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5356        });
5357        ConditionResult::from(found)
5358    }
5359
5360    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5361    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5362        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5363        let pia_segs = ctx.find_segments("PIA");
5364        let found = pia_segs.iter().any(|s| {
5365            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5366                (v.starts_with("1-08-1")
5367                    || v.starts_with("1-08-2")
5368                    || v.starts_with("1-08-4")
5369                    || v.starts_with("1-08-5"))
5370                    && v.contains("AGS")
5371                    && v.contains("KG")
5372            })
5373        });
5374        ConditionResult::from(found)
5375    }
5376
5377    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5378    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5379        // TODO: implement
5380        ConditionResult::Unknown
5381    }
5382
5383    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5384    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5385        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5386        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5387        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5388        let found = pia_segs.iter().any(|s| {
5389            s.elements
5390                .first()
5391                .and_then(|e| e.first())
5392                .is_some_and(|v| v == "5")
5393                && s.elements
5394                    .get(1)
5395                    .and_then(|e| e.first())
5396                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5397        });
5398        ConditionResult::from(found)
5399    }
5400
5401    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5402    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5403        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5404        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5405        let found = rff_segs.iter().any(|s| {
5406            s.elements
5407                .first()
5408                .and_then(|e| e.first())
5409                .is_some_and(|v| v == "Z37")
5410        });
5411        ConditionResult::from(found)
5412    }
5413
5414    /// [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...
5415    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5416        ctx.external.evaluate("code_list_membership_check")
5417    }
5418
5419    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5420    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5421        let loc_segs = ctx.find_segments("LOC");
5422        let found = loc_segs.iter().any(|s| {
5423            s.elements
5424                .first()
5425                .and_then(|e| e.first())
5426                .is_some_and(|v| v == "Z22")
5427        });
5428        ConditionResult::from(found)
5429    }
5430
5431    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5432    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5433        let loc_segs = ctx.find_segments("LOC");
5434        let found = loc_segs.iter().any(|s| {
5435            s.elements
5436                .first()
5437                .and_then(|e| e.first())
5438                .is_some_and(|v| v == "Z18")
5439        });
5440        ConditionResult::from(found)
5441    }
5442
5443    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5444    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5445        let loc_segs = ctx.find_segments("LOC");
5446        let found = loc_segs.iter().any(|s| {
5447            s.elements
5448                .first()
5449                .and_then(|e| e.first())
5450                .is_some_and(|v| v == "Z15")
5451        });
5452        ConditionResult::from(!found)
5453    }
5454
5455    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5456    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5457        let loc_segs = ctx.find_segments("LOC");
5458        let found = loc_segs.iter().any(|s| {
5459            s.elements
5460                .first()
5461                .and_then(|e| e.first())
5462                .is_some_and(|v| v == "Z20")
5463        });
5464        ConditionResult::from(found)
5465    }
5466
5467    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5468    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5469        let loc_segs = ctx.find_segments("LOC");
5470        let found = loc_segs.iter().any(|s| {
5471            s.elements
5472                .first()
5473                .and_then(|e| e.first())
5474                .is_some_and(|v| v == "Z18")
5475        });
5476        ConditionResult::from(!found)
5477    }
5478
5479    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5480    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5481        let loc_segs = ctx.find_segments("LOC");
5482        let found = loc_segs.iter().any(|s| {
5483            s.elements
5484                .first()
5485                .and_then(|e| e.first())
5486                .is_some_and(|v| v == "Z20")
5487        });
5488        ConditionResult::from(!found)
5489    }
5490
5491    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5492    fn evaluate_299(&self, ctx: &EvaluationContext) -> ConditionResult {
5493        let loc_segs = ctx.find_segments("LOC");
5494        let found = loc_segs.iter().any(|s| {
5495            s.elements
5496                .first()
5497                .and_then(|e| e.first())
5498                .is_some_and(|v| v == "Z19")
5499        });
5500        ConditionResult::from(!found)
5501    }
5502
5503    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5504    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5505        let seq_segs = ctx.find_segments("SEQ");
5506        let found = seq_segs.iter().any(|s| {
5507            s.elements
5508                .first()
5509                .and_then(|e| e.first())
5510                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5511        });
5512        ConditionResult::from(!found)
5513    }
5514
5515    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5516    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5517        let bgm_segs = ctx.find_segments("BGM");
5518        let found = bgm_segs.iter().any(|s| {
5519            s.elements
5520                .first()
5521                .and_then(|e| e.first())
5522                .is_some_and(|v| v == "E03")
5523        });
5524        ConditionResult::from(found)
5525    }
5526
5527    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5528    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5529        let seq_segs = ctx.find_segments("SEQ");
5530        let found = seq_segs.iter().any(|s| {
5531            s.elements
5532                .first()
5533                .and_then(|e| e.first())
5534                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5535        });
5536        ConditionResult::from(!found)
5537    }
5538
5539    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5540    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5541        let seq_segs = ctx.find_segments("SEQ");
5542        let found = seq_segs.iter().any(|s| {
5543            s.elements
5544                .first()
5545                .and_then(|e| e.first())
5546                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5547        });
5548        ConditionResult::from(!found)
5549    }
5550
5551    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5552    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5553        let seq_segs = ctx.find_segments("SEQ");
5554        let found = seq_segs.iter().any(|s| {
5555            s.elements
5556                .first()
5557                .and_then(|e| e.first())
5558                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5559        });
5560        ConditionResult::from(found)
5561    }
5562
5563    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5564    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5565        let seq_segs = ctx.find_segments("SEQ");
5566        let found = seq_segs.iter().any(|s| {
5567            s.elements
5568                .first()
5569                .and_then(|e| e.first())
5570                .is_some_and(|v| ["Z24"].contains(&v.as_str()))
5571        });
5572        ConditionResult::from(found)
5573    }
5574
5575    /// [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
5576    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5577        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5578        // reaches an SG8; the message as before otherwise.
5579        if let Some(scope) = ctx.enclosing_scope("SG8") {
5580            return ConditionResult::from(
5581                sg4_instances::seq_at(ctx, scope)
5582                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5583                    .is_some_and(|(_, zeitraum)| {
5584                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5585                    }),
5586            );
5587        }
5588        use std::collections::HashSet;
5589        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5590        let loc_z22_ids: HashSet<String> = ctx
5591            .find_segments_with_qualifier("LOC", 0, "Z22")
5592            .into_iter()
5593            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5594            .filter(|v| !v.is_empty())
5595            .collect();
5596        if loc_z22_ids.is_empty() {
5597            return ConditionResult::False;
5598        }
5599        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5600        let has_match = ctx
5601            .find_segments_with_qualifier("SEQ", 0, "Z58")
5602            .iter()
5603            .any(|s| {
5604                s.elements
5605                    .get(1)
5606                    .and_then(|e: &Vec<String>| e.first())
5607                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5608            });
5609        ConditionResult::from(has_match)
5610    }
5611
5612    /// [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
5613    // 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)
5614    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5615        // 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
5616        // reaches an SG8; the message as before otherwise.
5617        if let Some(scope) = ctx.enclosing_scope("SG8") {
5618            return ConditionResult::from(
5619                sg4_instances::seq_at(ctx, scope)
5620                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5621                    .is_some_and(|(_, zeitraum)| {
5622                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5623                    }),
5624            );
5625        }
5626        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5627        if seq_segs.is_empty() {
5628            return ConditionResult::Unknown;
5629        }
5630        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5631        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5632            .iter()
5633            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5634            .filter(|v| !v.is_empty())
5635            .collect();
5636        ConditionResult::from(seq_segs.iter().any(|seq| {
5637            let zid = seq
5638                .elements
5639                .get(1)
5640                .and_then(|e: &Vec<String>| e.first())
5641                .map(|v| v.as_str())
5642                .unwrap_or("");
5643            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5644        }))
5645    }
5646
5647    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5648    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5649        let loc_segs = ctx.find_segments("LOC");
5650        let found = loc_segs.iter().any(|s| {
5651            s.elements
5652                .first()
5653                .and_then(|e| e.first())
5654                .is_some_and(|v| v == "Z22")
5655        });
5656        ConditionResult::from(!found)
5657    }
5658
5659    /// [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...
5660    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5661        ctx.external.evaluate("code_list_membership_check")
5662    }
5663
5664    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5665    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5666        ctx.external.evaluate("data_clearing_required")
5667    }
5668
5669    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5670    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5671        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
5672        // whatever its SEQ (Z03, ZA3/ZA4, ZF5 all carry [316]; #305).
5673        ctx.this_group_lacks_qualifier("RFF", 0, "Z14", &["SG4", "SG8"])
5674    }
5675
5676    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5677    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5678        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5679        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5680        if found {
5681            ConditionResult::True
5682        } else {
5683            ConditionResult::Unknown
5684        }
5685    }
5686
5687    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5688    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5689        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5690        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5691        if found {
5692            ConditionResult::True
5693        } else {
5694            ConditionResult::Unknown
5695        }
5696    }
5697
5698    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5699    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5700        let com_segs = ctx.find_segments("COM");
5701        let has_em = com_segs.iter().any(|s| {
5702            s.elements
5703                .first()
5704                .and_then(|e| e.get(1))
5705                .is_some_and(|v| v == "EM")
5706        });
5707        ConditionResult::from(has_em)
5708    }
5709
5710    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5711    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5712        let com_segs = ctx.find_segments("COM");
5713        let has_phone = com_segs.iter().any(|s| {
5714            s.elements
5715                .first()
5716                .and_then(|e| e.get(1))
5717                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5718        });
5719        ConditionResult::from(has_phone)
5720    }
5721
5722    /// [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...
5723    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5724        ctx.external.evaluate("code_list_membership_check")
5725    }
5726
5727    /// [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...
5728    // 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)
5729    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5730        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5731        // reaches an SG8; the message as before otherwise.
5732        if let Some(scope) = ctx.enclosing_scope("SG8") {
5733            let Some((_, zeitraum)) =
5734                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5735            else {
5736                return ConditionResult::False;
5737            };
5738            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5739            return ConditionResult::from(sg6s.iter().any(|p| {
5740                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5741                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5742                        && s.get_component(0, 2) == zeitraum
5743                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5744                    s.get_element(0) == "Z25"
5745                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5746                })
5747            }));
5748        }
5749        const THRESHOLD: &str = "202212312300+00";
5750        let seq_zeitraum_ids: Vec<String> = ctx
5751            .find_segments("SEQ")
5752            .iter()
5753            .filter_map(|s| {
5754                s.elements
5755                    .get(1)
5756                    .and_then(|e: &Vec<String>| e.first())
5757                    .cloned()
5758            })
5759            .filter(|v| !v.is_empty())
5760            .collect();
5761        if seq_zeitraum_ids.is_empty() {
5762            return ConditionResult::Unknown;
5763        }
5764        let rff_segs = ctx.find_segments("RFF");
5765        let matching_rff = rff_segs.iter().any(|rff| {
5766            let qual = rff
5767                .elements
5768                .first()
5769                .and_then(|e: &Vec<String>| e.first())
5770                .map(|v| v.as_str())
5771                .unwrap_or("");
5772            if !["Z47", "Z48", "Z49"].contains(&qual) {
5773                return false;
5774            }
5775            let zid = rff
5776                .elements
5777                .first()
5778                .and_then(|e| e.get(2))
5779                .map(|v| v.as_str())
5780                .unwrap_or("");
5781            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5782        });
5783        if !matching_rff {
5784            return ConditionResult::False;
5785        }
5786        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5787            let val = dtm
5788                .elements
5789                .first()
5790                .and_then(|e| e.get(1))
5791                .map(|v| v.replace("?+", "+"))
5792                .unwrap_or_default();
5793            if !val.is_empty() && val.as_str() >= THRESHOLD {
5794                return ConditionResult::True;
5795            }
5796        }
5797        ConditionResult::False
5798    }
5799
5800    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5801    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5802        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
5803        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
5804        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
5805        let has_a03_e0096 = sts_segs.iter().any(|s| {
5806            s.elements
5807                .get(2)
5808                .and_then(|e| e.first())
5809                .is_some_and(|v| v == "A03")
5810                && s.elements
5811                    .get(2)
5812                    .and_then(|e| e.get(1))
5813                    .is_some_and(|v| v == "E_0096")
5814        });
5815        let has_a04_e0097 = sts_segs.iter().any(|s| {
5816            s.elements
5817                .get(2)
5818                .and_then(|e| e.first())
5819                .is_some_and(|v| v == "A04")
5820                && s.elements
5821                    .get(2)
5822                    .and_then(|e| e.get(1))
5823                    .is_some_and(|v| v == "E_0097")
5824        });
5825        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
5826    }
5827
5828    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5829    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
5830        let seq_segs = ctx.find_segments("SEQ");
5831        let has_seq = seq_segs.iter().any(|s| {
5832            s.elements
5833                .first()
5834                .and_then(|e| e.first())
5835                .is_some_and(|v| v == "Z98")
5836        });
5837        let cci_segs = ctx.find_segments("CCI");
5838        let has_cci = cci_segs.iter().any(|s| {
5839            s.elements
5840                .first()
5841                .and_then(|e| e.first())
5842                .is_some_and(|v| v == "6")
5843                && s.elements
5844                    .get(2)
5845                    .and_then(|e| e.first())
5846                    .is_some_and(|v| v == "ZA9")
5847        });
5848        ConditionResult::from(!(has_seq && has_cci))
5849    }
5850
5851    /// [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
5852    // 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)
5853    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
5854        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
5855        // reaches an SG8; the message as before otherwise.
5856        if let Some(scope) = ctx.enclosing_scope("SG8") {
5857            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5858            return ConditionResult::from(z01s.iter().any(|p| {
5859                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
5860            }));
5861        }
5862        let nav = match ctx.navigator {
5863            Some(n) => n,
5864            None => {
5865                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
5866                    return ConditionResult::Unknown;
5867                }
5868                let has_cci_za9 = ctx
5869                    .find_segments_with_qualifier("CCI", 0, "6")
5870                    .iter()
5871                    .any(|s| {
5872                        s.elements
5873                            .get(2)
5874                            .and_then(|e: &Vec<String>| e.first())
5875                            .map(|v| v.as_str())
5876                            == Some("ZA9")
5877                    });
5878                return ConditionResult::from(!has_cci_za9);
5879            }
5880        };
5881        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5882        if sg8_count == 0 {
5883            return ConditionResult::Unknown;
5884        }
5885        for sg8_idx in 0..sg8_count {
5886            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
5887            let mut has_cci_za9 = false;
5888            for sg10_idx in 0..sg10_count {
5889                let ccis = nav.find_segments_in_child_group(
5890                    "CCI",
5891                    &["SG4", "SG8"],
5892                    sg8_idx,
5893                    "SG10",
5894                    sg10_idx,
5895                );
5896                if ccis.iter().any(|s| {
5897                    s.elements
5898                        .first()
5899                        .and_then(|e: &Vec<String>| e.first())
5900                        .map(|v| v.as_str())
5901                        == Some("6")
5902                        && s.elements
5903                            .get(2)
5904                            .and_then(|e: &Vec<String>| e.first())
5905                            .map(|v| v.as_str())
5906                            == Some("ZA9")
5907                }) {
5908                    has_cci_za9 = true;
5909                    break;
5910                }
5911            }
5912            if !has_cci_za9 {
5913                return ConditionResult::True;
5914            }
5915        }
5916        ConditionResult::False
5917    }
5918
5919    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
5920    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
5921        let rff_segs = ctx.find_segments("RFF");
5922        let found = rff_segs.iter().any(|s| {
5923            s.elements
5924                .first()
5925                .and_then(|e| e.first())
5926                .is_some_and(|v| v == "Z31")
5927        });
5928        ConditionResult::from(found)
5929    }
5930
5931    /// [334] Nur MP-ID aus Sparte Gas
5932    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
5933        // Check: Marktpartner-ID is from Sparte Gas
5934        ctx.external.evaluate("sector_check")
5935    }
5936
5937    /// [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...
5938    // 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)
5939    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
5940        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
5941        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
5942        let z58_ids: Vec<String> = ctx
5943            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
5944            .iter()
5945            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
5946            .filter(|v| !v.is_empty())
5947            .collect();
5948        if z58_ids.is_empty() {
5949            return ConditionResult::Unknown;
5950        }
5951        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
5952            return ConditionResult::False;
5953        }
5954        let rff_z31_ids: Vec<String> = ctx
5955            .find_segments_with_qualifier("RFF", 0, "Z31")
5956            .iter()
5957            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
5958            .filter(|v| !v.is_empty())
5959            .collect();
5960        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
5961    }
5962
5963    /// [336] Wenn in Änderungsmeldung gefüllt
5964    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
5965        // TODO: implement
5966        ConditionResult::Unknown
5967    }
5968
5969    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
5970    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
5971        let cci_segs = ctx.find_segments("CCI");
5972        let has_cci = cci_segs.iter().any(|s| {
5973            s.elements
5974                .get(2)
5975                .and_then(|e| e.first())
5976                .is_some_and(|v| v == "ZB4")
5977        });
5978        let cav_segs = ctx.find_segments("CAV");
5979        let has_cav = cav_segs.iter().any(|s| {
5980            s.elements
5981                .first()
5982                .and_then(|e| e.first())
5983                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
5984        });
5985        ConditionResult::from(has_cci && has_cav)
5986    }
5987
5988    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
5989    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
5990        let cci_segs = ctx.find_segments("CCI");
5991        let has_cci = cci_segs.iter().any(|s| {
5992            s.elements
5993                .get(2)
5994                .and_then(|e| e.first())
5995                .is_some_and(|v| v == "ZB4")
5996        });
5997        let cav_segs = ctx.find_segments("CAV");
5998        let has_cav = cav_segs.iter().any(|s| {
5999            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6000                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6001            })
6002        });
6003        ConditionResult::from(has_cci && has_cav)
6004    }
6005
6006    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6007    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6008        let cci_segs = ctx.find_segments("CCI");
6009        let has_cci = cci_segs.iter().any(|s| {
6010            s.elements
6011                .get(2)
6012                .and_then(|e| e.first())
6013                .is_some_and(|v| v == "ZB4")
6014        });
6015        let cav_segs = ctx.find_segments("CAV");
6016        let has_cav = cav_segs.iter().any(|s| {
6017            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6018                [
6019                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6020                ]
6021                .contains(&v.as_str())
6022            })
6023        });
6024        ConditionResult::from(has_cci && has_cav)
6025    }
6026
6027    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6028    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6029        let cci_segs = ctx.find_segments("CCI");
6030        let has_cci = cci_segs.iter().any(|s| {
6031            s.elements
6032                .get(2)
6033                .and_then(|e| e.first())
6034                .is_some_and(|v| v == "ZB4")
6035        });
6036        let cav_segs = ctx.find_segments("CAV");
6037        let has_cav = cav_segs.iter().any(|s| {
6038            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6039                [
6040                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6041                ]
6042                .contains(&v.as_str())
6043            })
6044        });
6045        ConditionResult::from(has_cci && has_cav)
6046    }
6047
6048    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6049    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6050        let cci_segs = ctx.find_segments("CCI");
6051        let has_cci = cci_segs.iter().any(|s| {
6052            s.elements
6053                .get(2)
6054                .and_then(|e| e.first())
6055                .is_some_and(|v| v == "ZB4")
6056        });
6057        let cav_segs = ctx.find_segments("CAV");
6058        let has_cav = cav_segs.iter().any(|s| {
6059            s.elements
6060                .first()
6061                .and_then(|e| e.first())
6062                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6063        });
6064        ConditionResult::from(has_cci && has_cav)
6065    }
6066
6067    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6068    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6069        // TODO: implement
6070        ConditionResult::Unknown
6071    }
6072
6073    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6074    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6075        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6076        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6077        let has_cci = cci_segs.iter().any(|s| {
6078            s.elements
6079                .get(2)
6080                .and_then(|e| e.first())
6081                .is_some_and(|v| v == "ZB4")
6082        });
6083        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6084        let has_cav = cav_segs.iter().any(|s| {
6085            s.elements
6086                .first()
6087                .and_then(|e| e.first())
6088                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6089        });
6090        ConditionResult::from(has_cci && has_cav)
6091    }
6092
6093    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6094    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6095        let loc_segs = ctx.find_segments("LOC");
6096        let found = loc_segs.iter().any(|s| {
6097            s.elements
6098                .first()
6099                .and_then(|e| e.first())
6100                .is_some_and(|v| v == "Z17")
6101        });
6102        ConditionResult::from(found)
6103    }
6104
6105    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6106    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6107        // HAND-EDITED: the CCI and the CAV of one SG10.
6108        sg10_has(ctx, false, |segs| {
6109            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6110        })
6111    }
6112
6113    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6114    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6115        let loc_segs = ctx.find_segments("LOC");
6116        let found = loc_segs.iter().any(|s| {
6117            s.elements
6118                .first()
6119                .and_then(|e| e.first())
6120                .is_some_and(|v| v == "Z16")
6121        });
6122        ConditionResult::from(!found)
6123    }
6124
6125    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6126    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6127        let loc_segs = ctx.find_segments("LOC");
6128        let found = loc_segs.iter().any(|s| {
6129            s.elements
6130                .first()
6131                .and_then(|e| e.first())
6132                .is_some_and(|v| v == "Z21")
6133        });
6134        ConditionResult::from(!found)
6135    }
6136
6137    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6138    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6139        let loc_segs = ctx.find_segments("LOC");
6140        let found = loc_segs.iter().any(|s| {
6141            s.elements
6142                .first()
6143                .and_then(|e| e.first())
6144                .is_some_and(|v| v == "Z17")
6145        });
6146        ConditionResult::from(!found)
6147    }
6148
6149    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6150    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6151        let seq_segs = ctx.find_segments("SEQ");
6152        let has_seq = seq_segs.iter().any(|s| {
6153            s.elements
6154                .first()
6155                .and_then(|e| e.first())
6156                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6157        });
6158        let rff_segs = ctx.find_segments("RFF");
6159        let has_rff = rff_segs.iter().any(|s| {
6160            s.elements
6161                .first()
6162                .and_then(|e| e.first())
6163                .is_some_and(|v| v == "Z31")
6164        });
6165        ConditionResult::from(has_seq && has_rff)
6166    }
6167
6168    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6169    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6170        let sts_segs = ctx.find_segments("STS");
6171        let found = sts_segs.iter().any(|s| {
6172            s.elements
6173                .first()
6174                .and_then(|e| e.first())
6175                .is_some_and(|v| v == "E01")
6176                && s.elements
6177                    .get(2)
6178                    .and_then(|e| e.first())
6179                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6180        });
6181        ConditionResult::from(found)
6182    }
6183
6184    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6185    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6186        // HAND-EDITED: the answer stands in C556 DE9013 (element 2), as in [351].
6187        let sts_segs = ctx.find_segments("STS");
6188        let found = sts_segs.iter().any(|s| {
6189            s.elements
6190                .get(2)
6191                .and_then(|e| e.first())
6192                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6193        });
6194        ConditionResult::from(found)
6195    }
6196
6197    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6198    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6199        let sts_segs = ctx.find_segments("STS");
6200        let found = sts_segs.iter().any(|s| {
6201            s.elements
6202                .first()
6203                .and_then(|e| e.first())
6204                .is_some_and(|v| v == "E01")
6205                && s.elements
6206                    .get(2)
6207                    .and_then(|e| e.first())
6208                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6209        });
6210        ConditionResult::from(found)
6211    }
6212
6213    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6214    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6215        let sts_segs = ctx.find_segments("STS");
6216        let found = sts_segs.iter().any(|s| {
6217            s.elements
6218                .first()
6219                .and_then(|e| e.first())
6220                .is_some_and(|v| v == "E01")
6221                && s.elements
6222                    .get(2)
6223                    .and_then(|e| e.first())
6224                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6225        });
6226        ConditionResult::from(found)
6227    }
6228
6229    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6230    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6231        let sts_segs = ctx.find_segments("STS");
6232        let found = sts_segs.iter().any(|s| {
6233            s.elements
6234                .first()
6235                .and_then(|e| e.first())
6236                .is_some_and(|v| v == "E01")
6237                && s.elements
6238                    .get(2)
6239                    .and_then(|e| e.first())
6240                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6241        });
6242        ConditionResult::from(found)
6243    }
6244
6245    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6246    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6247        let sts_segs = ctx.find_segments("STS");
6248        let found = sts_segs.iter().any(|s| {
6249            s.elements
6250                .first()
6251                .and_then(|e| e.first())
6252                .is_some_and(|v| v == "E01")
6253                && s.elements
6254                    .get(2)
6255                    .and_then(|e| e.first())
6256                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6257        });
6258        ConditionResult::from(found)
6259    }
6260
6261    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6262    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6263    // Preserve across regeneration until the codegen prompt is updated.
6264    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6265        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6266    }
6267
6268    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6269    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6270    // Preserve across regeneration until the codegen prompt is updated.
6271    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6272        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6273    }
6274
6275    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6276    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6277        let sts_segs = ctx.find_segments("STS");
6278        let found = sts_segs.iter().any(|s| {
6279            s.elements
6280                .first()
6281                .and_then(|e| e.first())
6282                .is_some_and(|v| v == "E01")
6283                && s.elements
6284                    .get(2)
6285                    .and_then(|e| e.first())
6286                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6287        });
6288        ConditionResult::from(found)
6289    }
6290
6291    /// [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
6292    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6293        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6294        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6295        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6296        let has_excluded = sts_segs.iter().any(|s| {
6297            let code = s
6298                .elements
6299                .get(2)
6300                .and_then(|e| e.first())
6301                .map(|v| v.as_str());
6302            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6303            matches!(
6304                (code, ebd),
6305                (Some("A04"), Some("E_0047"))
6306                    | (Some("A04"), Some("E_0014"))
6307                    | (Some("A03"), Some("E_0049"))
6308                    | (Some("A03"), Some("E_0004"))
6309            )
6310        });
6311        ConditionResult::from(!has_excluded)
6312    }
6313
6314    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6315    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6316    // Preserve across regeneration until the codegen prompt is updated.
6317    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6318        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A30"])
6319    }
6320
6321    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6322    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6323        let sts_segs = ctx.find_segments("STS");
6324        let found = sts_segs.iter().any(|s| {
6325            s.elements
6326                .first()
6327                .and_then(|e| e.first())
6328                .is_some_and(|v| v == "E01")
6329                && s.elements
6330                    .get(2)
6331                    .and_then(|e| e.first())
6332                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6333        });
6334        ConditionResult::from(found)
6335    }
6336
6337    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6338    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6339    // Preserve across regeneration until the codegen prompt is updated.
6340    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6341        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A41"])
6342    }
6343
6344    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6345    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6346        let cav_segs = ctx.find_segments("CAV");
6347        let found = cav_segs.iter().any(|s| {
6348            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6349                [
6350                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6351                ]
6352                .contains(&v.as_str())
6353            })
6354        });
6355        ConditionResult::from(found)
6356    }
6357
6358    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6359    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6360        let seq_segs = ctx.find_segments("SEQ");
6361        let found = seq_segs.iter().any(|s| {
6362            s.elements
6363                .first()
6364                .and_then(|e| e.first())
6365                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6366        });
6367        ConditionResult::from(!found)
6368    }
6369
6370    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6371    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6372        let cav_segs = ctx.find_segments("CAV");
6373        let found = cav_segs.iter().any(|s| {
6374            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6375                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6376            })
6377        });
6378        ConditionResult::from(found)
6379    }
6380
6381    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6382    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6383        // TODO: implement
6384        ConditionResult::Unknown
6385    }
6386
6387    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6388    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6389        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6390        ctx.lacks_qualifier("SEQ", 0, "Z24")
6391    }
6392
6393    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6394    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6395        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6396        ctx.lacks_qualifier("SEQ", 0, "Z22")
6397    }
6398
6399    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6400    // 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)
6401    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6402        let nav = match ctx.navigator {
6403            Some(n) => n,
6404            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6405        };
6406        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6407        for i in 0..sg8_count {
6408            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6409            for j in 0..sg10_count {
6410                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6411                let has_cci = ccis.iter().any(|s| {
6412                    s.elements
6413                        .first()
6414                        .and_then(|e: &Vec<String>| e.first())
6415                        .is_some_and(|v: &String| v == "15")
6416                        && s.elements
6417                            .get(2)
6418                            .and_then(|e: &Vec<String>| e.first())
6419                            .is_some_and(|v: &String| v == "Z21")
6420                });
6421                if has_cci {
6422                    let cavs =
6423                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6424                    if cavs.iter().any(|s| {
6425                        s.elements
6426                            .first()
6427                            .and_then(|e: &Vec<String>| e.first())
6428                            .is_some_and(|v: &String| v == "AUS")
6429                    }) {
6430                        return ConditionResult::True;
6431                    }
6432                }
6433            }
6434        }
6435        ConditionResult::False
6436    }
6437
6438    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6439    // 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)
6440    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6441        let nav = match ctx.navigator {
6442            Some(n) => n,
6443            None => {
6444                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6445                return match r {
6446                    ConditionResult::True => ConditionResult::False,
6447                    ConditionResult::False => ConditionResult::True,
6448                    ConditionResult::Unknown => ConditionResult::Unknown,
6449                };
6450            }
6451        };
6452        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6453        for i in 0..sg8_count {
6454            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6455            for j in 0..sg10_count {
6456                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6457                let has_cci = ccis.iter().any(|s| {
6458                    s.elements
6459                        .first()
6460                        .and_then(|e: &Vec<String>| e.first())
6461                        .is_some_and(|v: &String| v == "15")
6462                        && s.elements
6463                            .get(2)
6464                            .and_then(|e: &Vec<String>| e.first())
6465                            .is_some_and(|v: &String| v == "Z21")
6466                });
6467                if has_cci {
6468                    let cavs =
6469                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6470                    if cavs.iter().any(|s| {
6471                        s.elements
6472                            .first()
6473                            .and_then(|e: &Vec<String>| e.first())
6474                            .is_some_and(|v: &String| v == "AUS")
6475                    }) {
6476                        return ConditionResult::False;
6477                    }
6478                }
6479            }
6480        }
6481        ConditionResult::True
6482    }
6483
6484    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6485    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6486        let cci_segs = ctx.find_segments("CCI");
6487        let found = cci_segs.iter().any(|s| {
6488            s.elements
6489                .first()
6490                .and_then(|e| e.first())
6491                .is_some_and(|v| v == "15")
6492                && s.elements
6493                    .get(2)
6494                    .and_then(|e| e.first())
6495                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6496        });
6497        ConditionResult::from(found)
6498    }
6499
6500    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6501    // 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)
6502    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6503        let nav = match ctx.navigator {
6504            Some(n) => n,
6505            None => {
6506                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6507            }
6508        };
6509        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6510        for i in 0..sg8_count {
6511            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6512            for j in 0..sg10_count {
6513                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6514                let has_cci = ccis.iter().any(|s| {
6515                    s.elements
6516                        .first()
6517                        .and_then(|e: &Vec<String>| e.first())
6518                        .is_some_and(|v: &String| v == "15")
6519                        && s.elements
6520                            .get(2)
6521                            .and_then(|e: &Vec<String>| e.first())
6522                            .is_some_and(|v: &String| v == "Z21")
6523                });
6524                if has_cci {
6525                    let cavs =
6526                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6527                    if cavs.iter().any(|s| {
6528                        s.elements
6529                            .first()
6530                            .and_then(|e: &Vec<String>| e.first())
6531                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6532                    }) {
6533                        return ConditionResult::True;
6534                    }
6535                }
6536            }
6537        }
6538        ConditionResult::False
6539    }
6540
6541    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6542    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6543        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6544        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6545        let found = cci_segs.iter().any(|s| {
6546            s.elements
6547                .first()
6548                .and_then(|e| e.first())
6549                .is_some_and(|v| v == "Z37")
6550                && s.elements
6551                    .get(2)
6552                    .and_then(|e| e.first())
6553                    .is_some_and(|v| v == "ZD1")
6554        });
6555        ConditionResult::from(found)
6556    }
6557
6558    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6559    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6560        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6561        let seq_count = ctx
6562            .find_segments("SEQ")
6563            .iter()
6564            .filter(|s| {
6565                s.elements
6566                    .first()
6567                    .and_then(|e| e.first())
6568                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6569            })
6570            .count();
6571        ConditionResult::from(seq_count > 1)
6572    }
6573
6574    /// [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...
6575    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6576        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6577        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6578        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6579        let found = pia_segs.iter().any(|s| {
6580            s.elements
6581                .first()
6582                .and_then(|e| e.first())
6583                .is_some_and(|v| v == "5")
6584                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6585                    v.starts_with("1-")
6586                        && (v.contains(":1.8.")
6587                            || v.contains(":2.8.")
6588                            || v.contains(":1.9.")
6589                            || v.contains(":2.9."))
6590                })
6591        });
6592        ConditionResult::from(found)
6593    }
6594
6595    /// [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...
6596    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6597        // Check: at least one RFF+Z49 (Zeitraum) exists
6598        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6599        ConditionResult::from(found)
6600    }
6601
6602    /// [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...
6603    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6604        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6605        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20,
6606        // with a tariff e = 1..62 (#351: 1-0:1.8.0 has no Zählzeit).
6607        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6608        let found = pia_segs.iter().any(|s| {
6609            s.elements
6610                .first()
6611                .and_then(|e| e.first())
6612                .is_some_and(|v| v == "5")
6613                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6614                    v.starts_with("1-")
6615                        && (v.contains(":1.8.") || v.contains(":2.8."))
6616                        && obis_tariff(v).is_some_and(|e| (1..=62).contains(&e))
6617                })
6618        });
6619        ConditionResult::from(found)
6620    }
6621
6622    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6623    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6624        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6625        let pia_segs = ctx.find_segments("PIA");
6626        let found = pia_segs.iter().any(|s| {
6627            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6628                let parts: Vec<&str> = v.split('-').collect();
6629                parts.len() == 3
6630                    && parts[0].len() == 1
6631                    && parts[0].chars().all(|c| c.is_ascii_digit())
6632                    && parts[1].len() == 2
6633                    && parts[1].chars().all(|c| c.is_ascii_digit())
6634                    && parts[2].len() == 1
6635                    && parts[2].chars().all(|c| c.is_ascii_digit())
6636            })
6637        });
6638        ConditionResult::from(found)
6639    }
6640
6641    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6642    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6643        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6644        let pia_segs = ctx.find_segments("PIA");
6645        let found = pia_segs.iter().any(|s| {
6646            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6647                let parts: Vec<&str> = v.split('-').collect();
6648                parts.len() == 4
6649                    && parts[0].len() == 1
6650                    && parts[0].chars().all(|c| c.is_ascii_digit())
6651                    && parts[1].len() == 2
6652                    && parts[1].chars().all(|c| c.is_ascii_digit())
6653                    && parts[2].len() == 1
6654                    && parts[2].chars().all(|c| c.is_ascii_digit())
6655                    && parts[3].len() == 3
6656                    && parts[3].chars().all(|c| c.is_ascii_digit())
6657            })
6658        });
6659        ConditionResult::from(found)
6660    }
6661
6662    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6663    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6664        // Check: PIA code in format n1-n2-n1-n8-n2
6665        let pia_segs = ctx.find_segments("PIA");
6666        let found = pia_segs.iter().any(|s| {
6667            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6668                let parts: Vec<&str> = v.split('-').collect();
6669                parts.len() == 5
6670                    && parts[0].len() == 1
6671                    && parts[0].chars().all(|c| c.is_ascii_digit())
6672                    && parts[1].len() == 2
6673                    && parts[1].chars().all(|c| c.is_ascii_digit())
6674                    && parts[2].len() == 1
6675                    && parts[2].chars().all(|c| c.is_ascii_digit())
6676                    && parts[3].len() == 8
6677                    && parts[3].chars().all(|c| c.is_ascii_digit())
6678                    && parts[4].len() == 2
6679                    && parts[4].chars().all(|c| c.is_ascii_digit())
6680            })
6681        });
6682        ConditionResult::from(found)
6683    }
6684
6685    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6686    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6687        // Check: PIA code in format n1-n2-n1-n8
6688        let pia_segs = ctx.find_segments("PIA");
6689        let found = pia_segs.iter().any(|s| {
6690            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6691                let parts: Vec<&str> = v.split('-').collect();
6692                parts.len() == 4
6693                    && parts[0].len() == 1
6694                    && parts[0].chars().all(|c| c.is_ascii_digit())
6695                    && parts[1].len() == 2
6696                    && parts[1].chars().all(|c| c.is_ascii_digit())
6697                    && parts[2].len() == 1
6698                    && parts[2].chars().all(|c| c.is_ascii_digit())
6699                    && parts[3].len() == 8
6700                    && parts[3].chars().all(|c| c.is_ascii_digit())
6701            })
6702        });
6703        ConditionResult::from(found)
6704    }
6705
6706    /// [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
6707    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6708        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6709        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6710        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6711        let found = pia_segs.iter().any(|s| {
6712            s.elements
6713                .first()
6714                .and_then(|e| e.first())
6715                .is_some_and(|v| v == "5")
6716                && s.elements
6717                    .get(1)
6718                    .and_then(|e| e.first())
6719                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6720        });
6721        ConditionResult::from(found)
6722    }
6723
6724    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6725    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6726        let pia_segs = ctx.find_segments("PIA");
6727        let found = pia_segs.iter().any(|s| {
6728            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6729                let prefixes = ["1-08-1", "1-08-4"];
6730                prefixes.iter().any(|p| v.starts_with(p))
6731            })
6732        });
6733        ConditionResult::from(found)
6734    }
6735
6736    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6737    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6738        let pia_segs = ctx.find_segments("PIA");
6739        let found = pia_segs.iter().any(|s| {
6740            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6741                let prefixes = ["1-08-2", "1-08-5"];
6742                prefixes.iter().any(|p| v.starts_with(p))
6743            })
6744        });
6745        ConditionResult::from(found)
6746    }
6747
6748    /// [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...
6749    // 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)
6750    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6751        let nav = match ctx.navigator() {
6752            Some(n) => n,
6753            None => return ConditionResult::Unknown,
6754        };
6755        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6756        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6757        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6758        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6759        for i in 0..sg5_count {
6760            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6761            for loc in &locs {
6762                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6763                    if !zid.is_empty() {
6764                        loc_zeitraum_ids.push(zid.clone());
6765                    }
6766                }
6767            }
6768        }
6769        if loc_zeitraum_ids.is_empty() {
6770            return ConditionResult::Unknown;
6771        }
6772        let pauschal_id = "9992000000018";
6773        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6774        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6775        // and that has RFF+Z31 with DE1154 != pauschal_id
6776        for i in 0..sg8_count {
6777            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6778            for seq in &seqs {
6779                if !seq
6780                    .elements
6781                    .first()
6782                    .and_then(|e| e.first())
6783                    .is_some_and(|v| v == "Z78")
6784                {
6785                    continue;
6786                }
6787                let zid = seq
6788                    .elements
6789                    .get(1)
6790                    .and_then(|e| e.first())
6791                    .map(|s| s.as_str())
6792                    .unwrap_or("");
6793                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6794                    continue;
6795                }
6796                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6797                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6798                for rff in &rffs {
6799                    let qual = rff
6800                        .elements
6801                        .first()
6802                        .and_then(|e| e.first())
6803                        .map(|v| v.as_str())
6804                        .unwrap_or("");
6805                    if qual == "Z31" {
6806                        let id = rff
6807                            .elements
6808                            .first()
6809                            .and_then(|e| e.get(1))
6810                            .map(|v| v.as_str())
6811                            .unwrap_or("");
6812                        let normalized = id.replace(' ', "");
6813                        if !normalized.is_empty() && normalized != pauschal_id {
6814                            return ConditionResult::True;
6815                        }
6816                    }
6817                }
6818            }
6819        }
6820        ConditionResult::False
6821    }
6822
6823    /// [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+...
6824    // HAND-EDITED: as [134], for Z49/Z53. The generated code was true whenever
6825    // the message had no RFF+Z49/Z53 at all, false on every Z49 SG6.
6826    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6827        continues_previous_period(ctx, &["Z49", "Z53"])
6828    }
6829
6830    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6831    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6832        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6833        // Check: OBIS Zählerstand code in PIA+5
6834        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6835        let found = pia_segs.iter().any(|s| {
6836            s.elements
6837                .first()
6838                .and_then(|e| e.first())
6839                .is_some_and(|v| v == "5")
6840                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6841                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6842                })
6843        });
6844        ConditionResult::from(found)
6845    }
6846
6847    /// [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
6848    // 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)
6849    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
6850        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
6851            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
6852                Some(id) if !id.is_empty() => id.clone(),
6853                _ => return ConditionResult::Unknown,
6854            },
6855            None => return ConditionResult::Unknown,
6856        };
6857        let nav = match ctx.navigator {
6858            Some(n) => n,
6859            None => return ConditionResult::Unknown,
6860        };
6861        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6862        for i in 0..sg8_count {
6863            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6864            for j in 0..sg10_count {
6865                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6866                let has_cci_zb3 = ccis.iter().any(|s| {
6867                    s.elements
6868                        .get(2)
6869                        .and_then(|e: &Vec<String>| e.first())
6870                        .is_some_and(|v: &String| v == "ZB3")
6871                });
6872                if !has_cci_zb3 {
6873                    continue;
6874                }
6875                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6876                for cav in &cavs {
6877                    if cav
6878                        .elements
6879                        .first()
6880                        .and_then(|e: &Vec<String>| e.first())
6881                        .is_some_and(|v: &String| v == "Z91")
6882                    {
6883                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
6884                            if !msb_id.is_empty() {
6885                                return ConditionResult::from(
6886                                    sender_mp_id.as_str() != msb_id.as_str(),
6887                                );
6888                            }
6889                        }
6890                    }
6891                }
6892            }
6893        }
6894        ConditionResult::Unknown
6895    }
6896
6897    /// [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
6898    // 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)
6899    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
6900        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
6901        let mr_mp_id = match mr_nads.first() {
6902            Some(nad) => nad
6903                .elements
6904                .get(1)
6905                .and_then(|e: &Vec<String>| e.first())
6906                .cloned()
6907                .unwrap_or_default(),
6908            None => return ConditionResult::Unknown,
6909        };
6910        if mr_mp_id.is_empty() {
6911            return ConditionResult::Unknown;
6912        }
6913        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
6914        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
6915        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
6916        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
6917        // otherwise, as before.
6918        if let Some(scope) = ctx.enclosing_scope("SG8") {
6919            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
6920            let names_recipient = |s: &[OwnedSegment]| {
6921                sg4_instances::has_cci(s, "", "ZB3")
6922                    && s.iter().any(|c| {
6923                        c.id == "CAV"
6924                            && c.get_element(0) == "Z91"
6925                            && c.get_component(0, 1) == mr_mp_id
6926                    })
6927            };
6928            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
6929                sg4_instances::seq_at(ctx, p)
6930                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
6931                    && (refs.is_empty()
6932                        || sg4_instances::rff_values_at(ctx, p, "Z18")
6933                            .iter()
6934                            .any(|v| refs.contains(v)))
6935                    && sg4_instances::sg10_at(ctx, p, names_recipient)
6936            });
6937            return ConditionResult::from(!same);
6938        }
6939
6940        let nav = match ctx.navigator {
6941            Some(n) => n,
6942            None => {
6943                let cavs = ctx.find_segments("CAV");
6944                let same_id_exists = cavs.iter().any(|s| {
6945                    s.elements
6946                        .first()
6947                        .and_then(|e: &Vec<String>| e.first())
6948                        .is_some_and(|v: &String| v == "Z91")
6949                        && s.elements
6950                            .first()
6951                            .and_then(|e| e.get(1))
6952                            .is_some_and(|v: &String| v == &mr_mp_id)
6953                });
6954                return ConditionResult::from(!same_id_exists);
6955            }
6956        };
6957
6958        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6959        for i in 0..sg8_count {
6960            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6961            for j in 0..sg10_count {
6962                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6963                let has_cci_zb3 = ccis.iter().any(|s| {
6964                    s.elements
6965                        .get(2)
6966                        .and_then(|e: &Vec<String>| e.first())
6967                        .is_some_and(|v: &String| v == "ZB3")
6968                });
6969                if !has_cci_zb3 {
6970                    continue;
6971                }
6972                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6973                for cav in &cavs {
6974                    if cav
6975                        .elements
6976                        .first()
6977                        .and_then(|e: &Vec<String>| e.first())
6978                        .is_some_and(|v: &String| v == "Z91")
6979                    {
6980                        let cav_mp_id = cav
6981                            .elements
6982                            .first()
6983                            .and_then(|e| e.get(1))
6984                            .map(|s| s.as_str())
6985                            .unwrap_or("");
6986                        if cav_mp_id == mr_mp_id.as_str() {
6987                            return ConditionResult::False;
6988                        }
6989                    }
6990                }
6991            }
6992        }
6993
6994        ConditionResult::True
6995    }
6996
6997    /// [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
6998    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
6999        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7000        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7001        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7002        let found = pia_segs.iter().any(|s| {
7003            s.elements
7004                .first()
7005                .and_then(|e| e.first())
7006                .is_some_and(|v| v == "5")
7007                && s.elements
7008                    .get(1)
7009                    .and_then(|e| e.first())
7010                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7011        });
7012        ConditionResult::from(found)
7013    }
7014
7015    /// [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
7016    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7017        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7018        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7019        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7020        let found = pia_segs.iter().any(|s| {
7021            s.elements
7022                .first()
7023                .and_then(|e| e.first())
7024                .is_some_and(|v| v == "5")
7025                && s.elements
7026                    .get(1)
7027                    .and_then(|e| e.first())
7028                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7029        });
7030        ConditionResult::from(found)
7031    }
7032
7033    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7034    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7035        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7036        // Check: OBIS Blindarbeit code in PIA+5
7037        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7038        let found = pia_segs.iter().any(|s| {
7039            s.elements
7040                .first()
7041                .and_then(|e| e.first())
7042                .is_some_and(|v| v == "5")
7043                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7044                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7045                })
7046        });
7047        ConditionResult::from(found)
7048    }
7049
7050    /// [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...
7051    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7052        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7053        // reaches an SG8; the message as before otherwise.
7054        if let Some(scope) = ctx.enclosing_scope("SG8") {
7055            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7056                ctx,
7057                scope,
7058                is_obis_wirkarbeit_kumuliert,
7059            ));
7060        }
7061        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7062        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7063        ConditionResult::from(has_seq_z02)
7064    }
7065
7066    /// [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 ...
7067    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7068        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7069        // reaches an SG8; the message as before otherwise.
7070        if let Some(scope) = ctx.enclosing_scope("SG8") {
7071            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7072                ctx,
7073                scope,
7074                is_obis_wirkarbeit_kumuliert,
7075            ));
7076        }
7077        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7078        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7079        ConditionResult::from(!has_seq_z02)
7080    }
7081
7082    /// [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...
7083    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7084        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7085        // reaches an SG8; the message as before otherwise.
7086        if let Some(scope) = ctx.enclosing_scope("SG8") {
7087            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7088                ctx,
7089                scope,
7090                is_obis_wirkarbeit_quarter_hour,
7091            ));
7092        }
7093        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7094        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7095        ConditionResult::from(has_seq_z02)
7096    }
7097
7098    /// [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 ...
7099    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7100        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7101        // reaches an SG8; the message as before otherwise.
7102        if let Some(scope) = ctx.enclosing_scope("SG8") {
7103            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7104                ctx,
7105                scope,
7106                is_obis_wirkarbeit_quarter_hour,
7107            ));
7108        }
7109        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7110        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7111        ConditionResult::from(!has_seq_z02)
7112    }
7113
7114    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7115    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7116        ctx.external.evaluate("code_list_membership_check")
7117    }
7118
7119    /// [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
7120    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7121        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7122        let found = ctx.find_segments("SEQ").iter().any(|s| {
7123            s.elements
7124                .first()
7125                .and_then(|e| e.first())
7126                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7127        });
7128        ConditionResult::from(found)
7129    }
7130
7131    /// [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
7132    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7133        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7134        let found = ctx.find_segments("SEQ").iter().any(|s| {
7135            s.elements
7136                .first()
7137                .and_then(|e| e.first())
7138                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7139        });
7140        ConditionResult::from(!found)
7141    }
7142
7143    /// [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
7144    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7145        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7146        let count = ctx
7147            .find_segments("SEQ")
7148            .iter()
7149            .filter(|s| {
7150                s.elements
7151                    .first()
7152                    .and_then(|e| e.first())
7153                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7154            })
7155            .count();
7156        ConditionResult::from(count > 1)
7157    }
7158
7159    /// [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
7160    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7161        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7162        let count = ctx
7163            .find_segments("SEQ")
7164            .iter()
7165            .filter(|s| {
7166                s.elements
7167                    .first()
7168                    .and_then(|e| e.first())
7169                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7170            })
7171            .count();
7172        ConditionResult::from(count <= 1)
7173    }
7174
7175    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7176    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7177        // TODO: implement
7178        ConditionResult::Unknown
7179    }
7180
7181    /// [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
7182    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7183        // TODO: implement
7184        ConditionResult::Unknown
7185    }
7186
7187    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7188    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7189        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7190        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7191        let found = rff_segs.iter().any(|s| {
7192            s.elements
7193                .first()
7194                .and_then(|e| e.first())
7195                .is_some_and(|v| v == "Z14")
7196        });
7197        ConditionResult::from(found)
7198    }
7199
7200    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7201    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7202        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7203        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7204        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7205    }
7206
7207    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7208    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7209        ctx.external.evaluate("code_list_membership_check")
7210    }
7211
7212    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7213    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7214        ctx.external.evaluate("code_list_membership_check")
7215    }
7216
7217    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7218    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7219        ctx.external.evaluate("code_list_membership_check")
7220    }
7221
7222    /// [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...
7223    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7224        ctx.external.evaluate("code_list_membership_check")
7225    }
7226
7227    /// [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...
7228    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7229        ctx.external.evaluate("code_list_membership_check")
7230    }
7231
7232    /// [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...
7233    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7234        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7235        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7236        ConditionResult::from(seq_z27_count > 1)
7237    }
7238
7239    /// [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+...
7240    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7241        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7242        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7243        ConditionResult::from(seq_z27_count <= 1)
7244    }
7245
7246    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7247    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7248        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7249        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7250        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7251        let found = pia_segs.iter().any(|s| {
7252            s.elements
7253                .first()
7254                .and_then(|e| e.first())
7255                .is_some_and(|v| v == "5")
7256                && s.elements
7257                    .get(1)
7258                    .and_then(|e| e.first())
7259                    .is_some_and(|v| v == "9991000000078")
7260        });
7261        ConditionResult::from(found)
7262    }
7263
7264    /// [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 ...
7265    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7266        ctx.external.evaluate("code_list_membership_check")
7267    }
7268
7269    /// [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...
7270    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7271        ctx.external.evaluate("code_list_membership_check")
7272    }
7273
7274    /// [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
7275    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7276        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7277        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7278        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7279        let found = pia_segs.iter().any(|s| {
7280            s.elements
7281                .first()
7282                .and_then(|e| e.first())
7283                .is_some_and(|v| v == "5")
7284                && s.elements
7285                    .get(1)
7286                    .and_then(|e| e.first())
7287                    .is_some_and(|v| v == "9991000000086")
7288        });
7289        ConditionResult::from(found)
7290    }
7291
7292    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7293    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7294        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7295        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7296        let found = sts_segs.iter().any(|s| {
7297            s.elements
7298                .first()
7299                .and_then(|e| e.first())
7300                .is_some_and(|v| v == "E01")
7301                && s.elements
7302                    .get(2)
7303                    .and_then(|e| e.first())
7304                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7305        });
7306        ConditionResult::from(found)
7307    }
7308
7309    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7310    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7311        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7312        // in tree validation), else the enclosing SG10's CCI.
7313        let is_z39 = |elements: &[Vec<String>]| {
7314            elements
7315                .first()
7316                .and_then(|e| e.first())
7317                .is_some_and(|v| v == "Z39")
7318        };
7319        if let Some(own) = ctx.resolved_segment.filter(|_| {
7320            ctx.scoped_find_segments_in("SG10", "CCI")
7321                .iter()
7322                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7323        }) {
7324            return ConditionResult::from(is_z39(own));
7325        }
7326        ConditionResult::from(
7327            ctx.scoped_find_segments_in("SG10", "CCI")
7328                .iter()
7329                .any(|s| is_z39(&s.elements)),
7330        )
7331    }
7332
7333    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7334    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7335        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7336        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7337        // alternative of the `⊻` held and the XOR failed on each RFF).
7338        same_rff_qualifier(ctx, "Z35")
7339    }
7340
7341    /// [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
7342    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7343        // HAND-EDITED (#379): the Zähleinrichtungsdaten of the meter this RFF
7344        // names — the SEQ+Z03/ZF5 whose SG10 CAV+Z30 carries its number in
7345        // DE7110 — not any SEQ+Z03 of the message. No such SG8: no RFF+Z14
7346        // for that meter either.
7347        let Some(number) = rff_reference(ctx) else {
7348            return ConditionResult::Unknown;
7349        };
7350        let Some(blocks) = sg8_blocks(ctx) else {
7351            return ConditionResult::Unknown;
7352        };
7353        let meter = blocks
7354            .iter()
7355            .filter(|b| b.seq == "Z03" || b.seq == "ZF5")
7356            .find(|b| {
7357                b.sg10.iter().any(|(_, cavs)| {
7358                    cavs.iter().any(|c| {
7359                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
7360                    })
7361                })
7362            });
7363        ConditionResult::from(meter.map_or(true, |b| {
7364            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
7365        }))
7366    }
7367
7368    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7369    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7370        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7371        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7372        // alternative of the `⊻` held and the XOR failed on each RFF).
7373        same_rff_qualifier(ctx, "Z34")
7374    }
7375
7376    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7377    // 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)
7378    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7379        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7380        // scope reaches one; the message as before otherwise.
7381        if let Some(scope) = ctx.enclosing_scope("SG8") {
7382            return ConditionResult::from(
7383                ctx.find_segments_at(scope, "SEQ")
7384                    .iter()
7385                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7386                    && !ctx
7387                        .find_segments_at(scope, "PIA")
7388                        .iter()
7389                        .any(|s| s.get_element(0) == "5"),
7390            );
7391        }
7392        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7393            if matches!(
7394                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7395                ConditionResult::True
7396            ) {
7397                return ConditionResult::True;
7398            }
7399        }
7400        ConditionResult::False
7401    }
7402
7403    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7404    // 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)
7405    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7406        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7407        // scope reaches one; the message as before otherwise.
7408        if let Some(scope) = ctx.enclosing_scope("SG8") {
7409            return ConditionResult::from(
7410                ctx.find_segments_at(scope, "SEQ")
7411                    .iter()
7412                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7413                    && !ctx
7414                        .find_segments_at(scope, "CCI")
7415                        .iter()
7416                        .any(|s| s.get_element(0) == "11"),
7417            );
7418        }
7419        let nav = match ctx.navigator {
7420            Some(n) => n,
7421            None => {
7422                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7423                    matches!(
7424                        ctx.filtered_parent_child_has_qualifier(
7425                            &["SG4", "SG8"],
7426                            "SEQ",
7427                            0,
7428                            code,
7429                            "SG10",
7430                            "CCI",
7431                            0,
7432                            "11",
7433                        ),
7434                        ConditionResult::True
7435                    )
7436                });
7437                return ConditionResult::from(!has_cci11);
7438            }
7439        };
7440
7441        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7442        for i in 0..sg8_count {
7443            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7444            let has_cci11 = (0..sg10_count).any(|j| {
7445                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7446                ccis.iter().any(|s| {
7447                    s.elements
7448                        .first()
7449                        .and_then(|e: &Vec<String>| e.first())
7450                        .is_some_and(|v: &String| v == "11")
7451                })
7452            });
7453            if !has_cci11 {
7454                return ConditionResult::True;
7455            }
7456        }
7457        ConditionResult::False
7458    }
7459
7460    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7461    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7462        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7463        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7464        let found = cci_segs.iter().any(|s| {
7465            s.elements
7466                .first()
7467                .and_then(|e| e.first())
7468                .is_some_and(|v| v == "Z17")
7469        });
7470        ConditionResult::from(found)
7471    }
7472
7473    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7474    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7475        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7476        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7477        let found = cci_segs.iter().any(|s| {
7478            s.elements
7479                .first()
7480                .and_then(|e| e.first())
7481                .is_some_and(|v| v == "Z50")
7482        });
7483        ConditionResult::from(found)
7484    }
7485
7486    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7487    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7488        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7489        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7490        let found = cci_segs.iter().any(|s| {
7491            s.elements
7492                .first()
7493                .and_then(|e| e.first())
7494                .is_some_and(|v| v == "Z56")
7495        });
7496        ConditionResult::from(found)
7497    }
7498
7499    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7500    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7501        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7502        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7503        let has_cci = cci_segs.iter().any(|s| {
7504            s.elements
7505                .first()
7506                .and_then(|e| e.first())
7507                .is_some_and(|v| v == "6")
7508                && s.elements
7509                    .get(2)
7510                    .and_then(|e| e.first())
7511                    .is_some_and(|v| v == "ZA9")
7512        });
7513        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7514        let has_cav = cav_segs.iter().any(|s| {
7515            s.elements
7516                .first()
7517                .and_then(|e| e.first())
7518                .is_some_and(|v| v == "ZG3")
7519        });
7520        ConditionResult::from(has_cci && has_cav)
7521    }
7522
7523    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7524    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7525        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7526        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7527        // alternative of the `⊻` held and the XOR failed on each RFF).
7528        same_rff_qualifier(ctx, "Z32")
7529    }
7530
7531    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7532    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7533        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7534        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7535        // alternative of the `⊻` held and the XOR failed on each RFF).
7536        same_rff_qualifier(ctx, "Z18")
7537    }
7538
7539    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7540    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7541        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7542        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7543        // alternative of the `⊻` held and the XOR failed on each RFF).
7544        same_rff_qualifier(ctx, "Z19")
7545    }
7546
7547    /// [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...
7548    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7549        ctx.external.evaluate("code_list_membership_check")
7550    }
7551
7552    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7553    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7554        // Check: begin date < end date (structural — always true for valid messages)
7555        ConditionResult::True
7556    }
7557
7558    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7559    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7560        // Check: end date > begin date (structural — always true for valid messages)
7561        ConditionResult::True
7562    }
7563
7564    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7565    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7566        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7567        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7568        // alternative of the `⊻` held and the XOR failed on each RFF).
7569        same_rff_qualifier(ctx, "Z37")
7570    }
7571
7572    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7573    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7574        // Check: PIA+5 with Messprodukt 9991000001498 exists
7575        let pia_segs = ctx.find_segments("PIA");
7576        let found = pia_segs.iter().any(|s| {
7577            s.elements
7578                .first()
7579                .and_then(|e| e.first())
7580                .is_some_and(|v| v == "5")
7581                && s.elements
7582                    .get(1)
7583                    .and_then(|e| e.first())
7584                    .is_some_and(|v| v == "9991000001498")
7585        });
7586        ConditionResult::from(found)
7587    }
7588
7589    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7590    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7591        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7592        let pia_segs = ctx.find_segments("PIA");
7593        let found = pia_segs.iter().any(|s| {
7594            s.elements
7595                .first()
7596                .and_then(|e| e.first())
7597                .is_some_and(|v| v == "5")
7598                && s.elements
7599                    .get(1)
7600                    .and_then(|e| e.first())
7601                    .is_some_and(|v| v == "9991000000739")
7602        });
7603        ConditionResult::from(found)
7604    }
7605
7606    /// [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
7607    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7608        ctx.external.evaluate("code_list_membership_check")
7609    }
7610
7611    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7612    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7613        ctx.external.evaluate("code_list_membership_check")
7614    }
7615
7616    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7617    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7618        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7619        // SG10, when the scope reaches one; of any one SG10 otherwise.
7620        this_sg10_has(ctx, |segs| {
7621            segs.iter()
7622                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7623                && sg4_instances::has_cav(segs, "ZG8")
7624        })
7625    }
7626
7627    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7628    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7629        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7630        // SG10, when the scope reaches one; of any one SG10 otherwise.
7631        this_sg10_has(ctx, |segs| {
7632            segs.iter()
7633                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7634                && sg4_instances::has_cav(segs, "ZH1")
7635        })
7636    }
7637
7638    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7639    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7640        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7641        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7642        let found = cci_segs.iter().any(|s| {
7643            s.elements
7644                .first()
7645                .and_then(|e| e.first())
7646                .is_some_and(|v| v == "Z61")
7647                && s.elements
7648                    .get(2)
7649                    .and_then(|e| e.first())
7650                    .is_some_and(|v| v == "ZF9")
7651        });
7652        ConditionResult::from(found)
7653    }
7654
7655    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7656    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7657        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7658        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7659        let found = rff_segs.iter().any(|s| {
7660            s.elements
7661                .first()
7662                .and_then(|e| e.first())
7663                .is_some_and(|v| v == "Z38")
7664        });
7665        ConditionResult::from(found)
7666    }
7667
7668    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7669    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7670        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7671        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7672        let found = rff_segs.iter().any(|s| {
7673            s.elements
7674                .first()
7675                .and_then(|e| e.first())
7676                .is_some_and(|v| v == "Z32")
7677        });
7678        ConditionResult::from(found)
7679    }
7680
7681    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7682    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7683        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7684        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7685        let found = segs.iter().any(|s| {
7686            s.elements
7687                .first()
7688                .and_then(|e| e.first())
7689                .is_some_and(|v| v == "ZG8")
7690        });
7691        ConditionResult::from(found)
7692    }
7693
7694    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7695    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7696        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7697        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7698        let found = segs.iter().any(|s| {
7699            s.elements
7700                .first()
7701                .and_then(|e| e.first())
7702                .is_some_and(|v| v == "ZH0")
7703        });
7704        ConditionResult::from(found)
7705    }
7706
7707    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7708    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7709        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7710        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7711        let found = segs.iter().any(|s| {
7712            s.elements
7713                .first()
7714                .and_then(|e| e.first())
7715                .is_some_and(|v| v == "ZH1")
7716        });
7717        ConditionResult::from(found)
7718    }
7719
7720    /// [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
7721    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7722        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7723        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7724        let found = segs.iter().any(|s| {
7725            s.elements
7726                .first()
7727                .and_then(|e| e.first())
7728                .is_some_and(|v| v == "ZH5")
7729        });
7730        ConditionResult::from(found)
7731    }
7732
7733    /// [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/ ...
7734    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7735        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7736        let found = ctx.find_segments("RFF").iter().any(|s| {
7737            let qual = s
7738                .elements
7739                .first()
7740                .and_then(|e| e.first())
7741                .map(|v| v.as_str());
7742            matches!(qual, Some("Z49") | Some("Z53"))
7743                && s.elements
7744                    .first()
7745                    .and_then(|e| e.get(2))
7746                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7747        });
7748        ConditionResult::from(found)
7749    }
7750
7751    /// [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...
7752    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7753        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7754        let found = ctx.find_segments("RFF").iter().any(|s| {
7755            let qual = s
7756                .elements
7757                .first()
7758                .and_then(|e| e.first())
7759                .map(|v| v.as_str());
7760            matches!(qual, Some("Z48") | Some("Z55"))
7761                && s.elements
7762                    .first()
7763                    .and_then(|e| e.get(2))
7764                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7765        });
7766        ConditionResult::from(found)
7767    }
7768
7769    /// [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...
7770    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7771        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7772        let found = ctx.find_segments("RFF").iter().any(|s| {
7773            let qual = s
7774                .elements
7775                .first()
7776                .and_then(|e| e.first())
7777                .map(|v| v.as_str());
7778            matches!(qual, Some("Z47") | Some("Z54"))
7779                && s.elements
7780                    .first()
7781                    .and_then(|e| e.get(2))
7782                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7783        });
7784        ConditionResult::from(found)
7785    }
7786
7787    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7788    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7789        let sts_segs = ctx.find_segments("STS");
7790        let found = sts_segs.iter().any(|s| {
7791            s.elements
7792                .first()
7793                .and_then(|e| e.first())
7794                .is_some_and(|v| v == "7")
7795                && s.elements
7796                    .get(2)
7797                    .and_then(|e| e.first())
7798                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7799        });
7800        ConditionResult::from(found)
7801    }
7802
7803    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7804    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7805        let sts_segs = ctx.find_segments("STS");
7806        let found = sts_segs.iter().any(|s| {
7807            s.elements
7808                .first()
7809                .and_then(|e| e.first())
7810                .is_some_and(|v| v == "7")
7811                && s.elements
7812                    .get(2)
7813                    .and_then(|e| e.first())
7814                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7815        });
7816        ConditionResult::from(found)
7817    }
7818
7819    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7820    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7821        let sts_segs = ctx.find_segments("STS");
7822        let found = sts_segs.iter().any(|s| {
7823            s.elements
7824                .first()
7825                .and_then(|e| e.first())
7826                .is_some_and(|v| v == "7")
7827                && s.elements
7828                    .get(3)
7829                    .and_then(|e| e.first())
7830                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7831        });
7832        ConditionResult::from(found)
7833    }
7834
7835    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7836    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7837        let sts_segs = ctx.find_segments("STS");
7838        let found = sts_segs.iter().any(|s| {
7839            s.elements
7840                .first()
7841                .and_then(|e| e.first())
7842                .is_some_and(|v| v == "7")
7843                && s.elements
7844                    .get(3)
7845                    .and_then(|e| e.first())
7846                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7847        });
7848        ConditionResult::from(found)
7849    }
7850
7851    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
7852    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
7853        let sts_segs = ctx.find_segments("STS");
7854        let found = sts_segs.iter().any(|s| {
7855            s.elements
7856                .first()
7857                .and_then(|e| e.first())
7858                .is_some_and(|v| v == "7")
7859                && s.elements
7860                    .get(3)
7861                    .and_then(|e| e.first())
7862                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
7863        });
7864        ConditionResult::from(found)
7865    }
7866
7867    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
7868    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
7869        let sts_segs = ctx.find_segments("STS");
7870        let found = sts_segs.iter().any(|s| {
7871            s.elements
7872                .first()
7873                .and_then(|e| e.first())
7874                .is_some_and(|v| v == "7")
7875                && s.elements
7876                    .get(3)
7877                    .and_then(|e| e.first())
7878                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
7879        });
7880        ConditionResult::from(found)
7881    }
7882
7883    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
7884    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
7885        let sts_segs = ctx.find_segments("STS");
7886        let found = sts_segs.iter().any(|s| {
7887            s.elements
7888                .first()
7889                .and_then(|e| e.first())
7890                .is_some_and(|v| v == "7")
7891                && s.elements
7892                    .get(3)
7893                    .and_then(|e| e.first())
7894                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
7895        });
7896        ConditionResult::from(found)
7897    }
7898
7899    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
7900    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
7901        let sts_segs = ctx.find_segments("STS");
7902        let found = sts_segs.iter().any(|s| {
7903            s.elements
7904                .first()
7905                .and_then(|e| e.first())
7906                .is_some_and(|v| v == "7")
7907                && s.elements
7908                    .get(3)
7909                    .and_then(|e| e.first())
7910                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
7911        });
7912        ConditionResult::from(found)
7913    }
7914
7915    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
7916    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
7917        let sts_segs = ctx.find_segments("STS");
7918        let found = sts_segs.iter().any(|s| {
7919            s.elements
7920                .first()
7921                .and_then(|e| e.first())
7922                .is_some_and(|v| v == "7")
7923                && s.elements
7924                    .get(3)
7925                    .and_then(|e| e.first())
7926                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
7927        });
7928        ConditionResult::from(found)
7929    }
7930
7931    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
7932    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
7933        let sts_segs = ctx.find_segments("STS");
7934        let found = sts_segs.iter().any(|s| {
7935            s.elements
7936                .first()
7937                .and_then(|e| e.first())
7938                .is_some_and(|v| v == "7")
7939                && s.elements
7940                    .get(3)
7941                    .and_then(|e| e.first())
7942                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
7943        });
7944        ConditionResult::from(found)
7945    }
7946
7947    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
7948    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
7949        let sts_segs = ctx.find_segments("STS");
7950        let found = sts_segs.iter().any(|s| {
7951            s.elements
7952                .first()
7953                .and_then(|e| e.first())
7954                .is_some_and(|v| v == "7")
7955                && s.elements
7956                    .get(3)
7957                    .and_then(|e| e.first())
7958                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
7959        });
7960        ConditionResult::from(found)
7961    }
7962
7963    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
7964    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
7965        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
7966        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
7967        if rff_z49.is_empty() {
7968            return ConditionResult::Unknown;
7969        }
7970        // Approximate: condition applies when RFF+Z49 exists
7971        ConditionResult::True
7972    }
7973
7974    /// [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
7975    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
7976        ctx.format_check("DTM", 0, 1, is_mesz_utc)
7977    }
7978
7979    /// [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
7980    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
7981        ctx.format_check("DTM", 0, 1, is_mez_utc)
7982    }
7983
7984    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
7985    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
7986        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
7987        // so lexicographic comparison works directly.
7988        let val = ctx.resolved_value.or_else(|| {
7989            ctx.find_segments_with_qualifier("DTM", 0, "137")
7990                .first()
7991                .and_then(|s| s.elements.first())
7992                .and_then(|e| e.get(1))
7993                .map(|v| v.as_str())
7994        });
7995        let Some(dtm_val) = val else {
7996            return ConditionResult::Unknown;
7997        };
7998        if dtm_val.len() < 12 {
7999            return ConditionResult::Unknown;
8000        }
8001        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8002        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8003    }
8004
8005    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8006    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8007        ConditionResult::True
8008    }
8009
8010    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8011    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8012        ConditionResult::True
8013    }
8014
8015    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8016    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8017        ConditionResult::True
8018    }
8019
8020    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8021    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8022        ConditionResult::True
8023    }
8024
8025    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8026    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8027        ConditionResult::True
8028    }
8029
8030    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8031    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8032        ConditionResult::True
8033    }
8034
8035    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8036    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8037        ConditionResult::True
8038    }
8039
8040    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8041    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8042        ConditionResult::True
8043    }
8044
8045    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8046    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8047        ConditionResult::True
8048    }
8049
8050    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8051    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8052        ConditionResult::True
8053    }
8054
8055    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8056    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8057        ConditionResult::True
8058    }
8059
8060    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8061    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8062        ConditionResult::True
8063    }
8064
8065    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8066    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8067        ConditionResult::True
8068    }
8069
8070    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8071    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8072        ConditionResult::True
8073    }
8074
8075    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8076    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8077        ConditionResult::True
8078    }
8079
8080    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8081    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8082        ConditionResult::True
8083    }
8084
8085    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8086    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8087        ConditionResult::True
8088    }
8089
8090    /// [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
8091    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8092        ConditionResult::True
8093    }
8094
8095    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8096    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8097        ConditionResult::True
8098    }
8099
8100    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8101    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8102        ConditionResult::True
8103    }
8104
8105    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8106    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8107        ConditionResult::True
8108    }
8109
8110    /// [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...
8111    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8112        ConditionResult::True
8113    }
8114
8115    /// [522] Hinweis: Es ist der NBN anzugeben
8116    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8117        ConditionResult::True
8118    }
8119
8120    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8121    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8122        ConditionResult::True
8123    }
8124
8125    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8126    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8127        ConditionResult::True
8128    }
8129
8130    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8131    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8132        ConditionResult::True
8133    }
8134
8135    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8136    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8137        ConditionResult::True
8138    }
8139
8140    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8141    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8142        ConditionResult::True
8143    }
8144
8145    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8146    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8147        ConditionResult::True
8148    }
8149
8150    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8151    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8152        ConditionResult::True
8153    }
8154
8155    /// [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, ...
8156    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8157        ConditionResult::True
8158    }
8159
8160    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8161    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8162        ConditionResult::True
8163    }
8164
8165    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8166    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8167        ConditionResult::True
8168    }
8169
8170    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8171    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8172        ConditionResult::True
8173    }
8174
8175    /// [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...
8176    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8177        // TODO: implement
8178        ConditionResult::Unknown
8179    }
8180
8181    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8182    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8183        ConditionResult::True
8184    }
8185
8186    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8187    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8188        ConditionResult::True
8189    }
8190
8191    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8192    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8193        ConditionResult::True
8194    }
8195
8196    /// [563] Hinweis: Für die ID der LieferantensummenZR
8197    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8198        ConditionResult::True
8199    }
8200
8201    /// [566] Hinweis: Altlieferant
8202    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8203        ConditionResult::True
8204    }
8205
8206    /// [567] Hinweis: Neulieferant
8207    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8208        ConditionResult::True
8209    }
8210
8211    /// [568] Hinweis: Lieferant der LieferantensummenZR
8212    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8213        ConditionResult::True
8214    }
8215
8216    /// [569] Hinweis: Dritter Nutzer
8217    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8218        ConditionResult::True
8219    }
8220
8221    /// [570] Hinweis: Netzbetreiber Alt
8222    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8223        ConditionResult::True
8224    }
8225
8226    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8227    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8228        ConditionResult::True
8229    }
8230
8231    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8232    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8233        ConditionResult::True
8234    }
8235
8236    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8237    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8238        ConditionResult::True
8239    }
8240
8241    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8242    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8243        ConditionResult::True
8244    }
8245
8246    /// [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...
8247    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8248        ConditionResult::True
8249    }
8250
8251    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8252    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8253        ConditionResult::True
8254    }
8255
8256    /// [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...
8257    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8258        ConditionResult::True
8259    }
8260
8261    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8262    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8263        ConditionResult::True
8264    }
8265
8266    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8267    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8268        ConditionResult::True
8269    }
8270
8271    /// [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...
8272    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8273        ConditionResult::True
8274    }
8275
8276    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8277    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8278        ConditionResult::True
8279    }
8280
8281    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8282    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8283        ConditionResult::True
8284    }
8285
8286    /// [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
8287    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8288        ConditionResult::True
8289    }
8290
8291    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8292    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8293        ConditionResult::True
8294    }
8295
8296    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8297    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8298        ConditionResult::True
8299    }
8300
8301    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8302    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8303        ConditionResult::True
8304    }
8305
8306    /// [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
8307    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8308        ConditionResult::True
8309    }
8310
8311    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8312    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8313        ConditionResult::True
8314    }
8315
8316    /// [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.
8317    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8318        ConditionResult::True
8319    }
8320
8321    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8322    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8323        ConditionResult::True
8324    }
8325
8326    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8327    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8328        ConditionResult::True
8329    }
8330
8331    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8332    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8333        ConditionResult::True
8334    }
8335
8336    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8337    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8338        ConditionResult::True
8339    }
8340
8341    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8342    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8343        ConditionResult::True
8344    }
8345
8346    /// [637] Hinweis: Bei Verpflichtungsanfrage
8347    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8348        ConditionResult::True
8349    }
8350
8351    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8352    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8353        ConditionResult::True
8354    }
8355
8356    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8357    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8358        ConditionResult::True
8359    }
8360
8361    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8362    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8363        ConditionResult::True
8364    }
8365
8366    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8367    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8368        ConditionResult::True
8369    }
8370
8371    /// [642] Hinweis: Wenn Abo vorliegt
8372    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8373        ConditionResult::True
8374    }
8375
8376    /// [643] Hinweis: Nachfolgender Netzbetreiber
8377    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8378        ConditionResult::True
8379    }
8380
8381    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8382    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8383        ConditionResult::True
8384    }
8385
8386    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8387    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8388        ConditionResult::True
8389    }
8390
8391    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8392    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8393        ConditionResult::True
8394    }
8395
8396    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8397    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8398        ConditionResult::True
8399    }
8400
8401    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8402    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8403        ConditionResult::True
8404    }
8405
8406    /// [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...
8407    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8408        ConditionResult::True
8409    }
8410
8411    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8412    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8413        ConditionResult::True
8414    }
8415
8416    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8417    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8418        ConditionResult::True
8419    }
8420
8421    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8422    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8423        ConditionResult::True
8424    }
8425
8426    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8427    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8428        ConditionResult::True
8429    }
8430
8431    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8432    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8433        ConditionResult::True
8434    }
8435
8436    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8437    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8438        ConditionResult::True
8439    }
8440
8441    /// [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
8442    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8443        ConditionResult::True
8444    }
8445
8446    /// [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...
8447    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8448        ConditionResult::True
8449    }
8450
8451    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8452    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8453        ConditionResult::True
8454    }
8455
8456    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8457    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8458        ConditionResult::True
8459    }
8460
8461    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8462    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8463        ConditionResult::True
8464    }
8465
8466    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8467    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8468        ConditionResult::True
8469    }
8470
8471    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8472    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8473        ConditionResult::True
8474    }
8475
8476    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8477    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8478        ConditionResult::True
8479    }
8480
8481    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8482    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8483        ConditionResult::True
8484    }
8485
8486    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8487    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8488        ConditionResult::True
8489    }
8490
8491    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8492    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8493        ConditionResult::True
8494    }
8495
8496    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8497    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8498        ConditionResult::True
8499    }
8500
8501    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8502    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8503        ConditionResult::True
8504    }
8505
8506    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8507    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8508        ConditionResult::True
8509    }
8510
8511    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8512    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8513        ConditionResult::True
8514    }
8515
8516    /// [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
8517    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8518        ConditionResult::True
8519    }
8520
8521    /// [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.
8522    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8523        ConditionResult::True
8524    }
8525
8526    /// [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
8527    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8528        ConditionResult::True
8529    }
8530
8531    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8532    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8533        ConditionResult::True
8534    }
8535
8536    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8537    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8538        ConditionResult::True
8539    }
8540
8541    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8542    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8543        ConditionResult::True
8544    }
8545
8546    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8547    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8548        ConditionResult::True
8549    }
8550
8551    /// [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...
8552    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8553        ConditionResult::True
8554    }
8555
8556    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8557    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8558        ConditionResult::True
8559    }
8560
8561    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8562    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8563        ConditionResult::True
8564    }
8565
8566    /// [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...
8567    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8568        ConditionResult::True
8569    }
8570
8571    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8572    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8573        ConditionResult::True
8574    }
8575
8576    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8577    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8578        ConditionResult::True
8579    }
8580
8581    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8582    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8583        ConditionResult::True
8584    }
8585
8586    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8587    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8588        ConditionResult::True
8589    }
8590
8591    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8592    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8593        ctx.external.evaluate("data_clearing_required")
8594    }
8595
8596    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8597    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8598        ConditionResult::True
8599    }
8600
8601    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8602    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8603        ConditionResult::True
8604    }
8605
8606    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8607    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8608        ConditionResult::True
8609    }
8610
8611    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8612    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8613        ConditionResult::True
8614    }
8615
8616    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8617    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8618        ConditionResult::True
8619    }
8620
8621    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8622    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8623        ConditionResult::True
8624    }
8625
8626    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8627    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8628        ConditionResult::True
8629    }
8630
8631    /// [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...
8632    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8633        ConditionResult::True
8634    }
8635
8636    /// [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...
8637    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8638        ConditionResult::True
8639    }
8640
8641    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8642    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8643        ConditionResult::True
8644    }
8645
8646    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8647    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8648        ctx.external.evaluate("data_clearing_required")
8649    }
8650
8651    /// [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
8652    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8653        ConditionResult::True
8654    }
8655
8656    /// [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...
8657    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8658        ConditionResult::True
8659    }
8660
8661    /// [902] Format: Möglicher Wert: ≥ 0
8662    fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
8663        // HAND-EDITED: judged on the value it annotates; without one it
8664        // says nothing.
8665        match ctx.resolved_value.filter(|v| !v.is_empty()) {
8666            Some(value) => validate_numeric(value, ">=", 0.0),
8667            None => ConditionResult::True,
8668        }
8669    }
8670
8671    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8672    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8673        // Format condition — informational, always applies
8674        ConditionResult::True
8675    }
8676
8677    /// [914] Format: Möglicher Wert: > 0
8678    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8679        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8680    }
8681
8682    /// [922] Format: TR-ID
8683    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8684        // Format condition — informational, always applies
8685        ConditionResult::True
8686    }
8687
8688    /// [926] Format: Möglicher Wert: 0
8689    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8690        // Format condition — informational, always applies
8691        ConditionResult::True
8692    }
8693
8694    /// [930] Format: max. 2 Nachkommastellen
8695    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8696        // Format condition — informational, always applies
8697        ConditionResult::True
8698    }
8699
8700    /// [931] Format: ZZZ = +00
8701    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8702        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8703    }
8704
8705    /// [932] Format: HHMM = 2200
8706    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8707        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8708    }
8709
8710    /// [933] Format: HHMM = 2300
8711    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8712        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8713    }
8714
8715    /// [937] Format: keine Nachkommastelle
8716    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8717        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8718    }
8719
8720    /// [938] Format: Möglicher Wert: <= 10
8721    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8722        ConditionResult::True
8723    }
8724
8725    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8726    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8727        ctx.format_check("COM", 0, 0, validate_email)
8728    }
8729
8730    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8731    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8732        ctx.format_check("COM", 0, 0, validate_phone)
8733    }
8734
8735    /// [942] Format: n1-n2-n1-n3
8736    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8737        // Format condition — informational, always applies
8738        ConditionResult::True
8739    }
8740
8741    /// [943] Format: n1-n2-n1
8742    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8743        // Format condition — informational, always applies
8744        ConditionResult::True
8745    }
8746
8747    /// [946] Format: max. 11 Nachkommastellen
8748    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8749        // Format condition — informational, always applies
8750        ConditionResult::True
8751    }
8752
8753    /// [948] Format: n1-n2-n1-n8-n2
8754    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8755        // Format condition — informational, always applies
8756        ConditionResult::True
8757    }
8758
8759    /// [950] Format: Marktlokations-ID
8760    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8761        // Format condition — informational, always applies
8762        ConditionResult::True
8763    }
8764
8765    /// [951] Format: Zählpunktbezeichnung
8766    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8767        // Format condition — informational, always applies
8768        ConditionResult::True
8769    }
8770
8771    /// [952] Format: Gerätenummer nach DIN 43863-5
8772    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8773        // Format condition — informational, always applies
8774        ConditionResult::True
8775    }
8776
8777    /// [955] Format: Möglicher Wert: < 100
8778    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8779        // Format condition — informational, always applies
8780        ConditionResult::True
8781    }
8782
8783    /// [957] Format: n1-n2-n1-n8
8784    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8785        // Format condition — informational, always applies
8786        ConditionResult::True
8787    }
8788
8789    /// [960] Format: Netzlokations-ID
8790    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8791        // Format condition — informational, always applies
8792        ConditionResult::True
8793    }
8794
8795    /// [961] Format: SR-ID
8796    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8797        // Format condition — informational, always applies
8798        ConditionResult::True
8799    }
8800
8801    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8802    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8803        // Format condition — informational, always applies
8804        ConditionResult::True
8805    }
8806
8807    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8808    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8809        // Cardinality: at least twice per SG4 IDE+24
8810        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8811        ConditionResult::from(ide_count > 0)
8812    }
8813
8814    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8815    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8816        let seq_segs = ctx.find_segments("SEQ");
8817        let has_z79 = seq_segs.iter().any(|s| {
8818            s.elements
8819                .first()
8820                .and_then(|e| e.first())
8821                .is_some_and(|v| v == "Z79")
8822        });
8823        ConditionResult::from(has_z79)
8824    }
8825
8826    /// [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
8827    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8828        // TODO: implement
8829        ConditionResult::Unknown
8830    }
8831
8832    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8833    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
8834    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("genau einmal")
8835    // is a count, not a condition on the group: the generated code counted
8836    // the SG8 of every variant naming the ID, so several variants made it
8837    // false and the group was never judged.
8838    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8839        let named = ctx
8840            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
8841            .iter()
8842            .any(|s| !s.get_component(0, 2).is_empty());
8843        ConditionResult::from(named)
8844    }
8845
8846    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8847    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
8848    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("mindestens einmal")
8849    // is a count, not a condition on the group: the generated code counted
8850    // the SG8 of every variant naming the ID, so several variants made it
8851    // false and the group was never judged.
8852    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
8853        let named = ctx
8854            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
8855            .iter()
8856            .any(|s| !s.get_component(0, 2).is_empty());
8857        ConditionResult::from(named)
8858    }
8859
8860    /// [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 ...
8861    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
8862        // TODO: implement
8863        ConditionResult::Unknown
8864    }
8865
8866    /// [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 ...
8867    /// EXTERNAL: Requires context from outside the message.
8868    // 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)
8869    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
8870        ctx.external
8871            .evaluate("request_message_zeitraum_ids_present")
8872    }
8873
8874    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
8875    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
8876        // TODO: implement
8877        ConditionResult::Unknown
8878    }
8879
8880    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
8881    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
8882        // Check: condition applies when no RFF+Z54 exists
8883        ctx.lacks_qualifier("RFF", 0, "Z54")
8884    }
8885
8886    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
8887    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
8888        // Check: condition applies when no RFF+Z47 exists
8889        ctx.lacks_qualifier("RFF", 0, "Z47")
8890    }
8891
8892    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
8893    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
8894        // TODO: implement
8895        ConditionResult::Unknown
8896    }
8897
8898    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
8899    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
8900        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
8901        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8902        ConditionResult::from(loc_count > 0)
8903    }
8904
8905    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
8906    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
8907        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
8908        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8909        ConditionResult::from(loc_count > 0)
8910    }
8911
8912    /// [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...
8913    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
8914        // TODO: implement
8915        ConditionResult::Unknown
8916    }
8917
8918    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
8919    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
8920        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
8921        let found = ctx.find_segments("CAV").iter().any(|s| {
8922            s.elements
8923                .first()
8924                .and_then(|e| e.first())
8925                .is_some_and(|v| v == "NZR")
8926        });
8927        ConditionResult::from(found)
8928    }
8929
8930    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
8931    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
8932        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
8933        let ide_count = ctx.find_segments("IDE").len();
8934        ConditionResult::from(ide_count > 0)
8935    }
8936
8937    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
8938    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
8939        // Cardinality: LieferantensummenZR once per SG4 IDE+24
8940        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8941        ConditionResult::from(ide_count > 0)
8942    }
8943
8944    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
8945    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
8946        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
8947        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8948        ConditionResult::from(ide_count > 0)
8949    }
8950
8951    /// [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)
8952    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
8953        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
8954        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8955        ConditionResult::from(ide_count > 0)
8956    }
8957
8958    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
8959    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
8960        // Cardinality: max twice per SG4 IDE+24
8961        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8962        ConditionResult::from(ide_count > 0)
8963    }
8964
8965    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
8966    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
8967        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
8968        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
8969        ConditionResult::from(loc_count > 0)
8970    }
8971
8972    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
8973    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
8974        // Cardinality: exactly twice per SG4 IDE
8975        let ide_count = ctx.find_segments("IDE").len();
8976        ConditionResult::from(ide_count > 0)
8977    }
8978
8979    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
8980    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
8981        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
8982        let found = ctx.find_segments("SEQ").iter().any(|s| {
8983            s.elements
8984                .first()
8985                .and_then(|e| e.first())
8986                .is_some_and(|v| v == "Z13" || v == "ZG0")
8987        });
8988        ConditionResult::from(found)
8989    }
8990
8991    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
8992    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
8993        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
8994        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8995        ConditionResult::from(ide_count > 0)
8996    }
8997
8998    /// [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
8999    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9000        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9001        let found = ctx.find_segments("SEQ").iter().any(|s| {
9002            s.elements
9003                .first()
9004                .and_then(|e| e.first())
9005                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9006        });
9007        ConditionResult::from(found)
9008    }
9009
9010    /// [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
9011    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9012        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9013        let found = ctx.find_segments("SEQ").iter().any(|s| {
9014            s.elements
9015                .first()
9016                .and_then(|e| e.first())
9017                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9018        });
9019        ConditionResult::from(found)
9020    }
9021
9022    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9023    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9024        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9025        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9026        ConditionResult::from(ide_count > 0)
9027    }
9028
9029    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9030    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9031        // TODO: implement
9032        ConditionResult::Unknown
9033    }
9034
9035    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9036    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9037        // Cardinality: code once per SG4 IDE+24
9038        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9039        ConditionResult::from(ide_count > 0)
9040    }
9041
9042    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9043    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9044        // TODO: implement
9045        ConditionResult::Unknown
9046    }
9047
9048    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9049    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9050        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9051        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9052        ConditionResult::from(loc_count > 0)
9053    }
9054
9055    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9056    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9057        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9058        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9059        ConditionResult::from(loc_count > 0)
9060    }
9061
9062    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9063    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9064        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9065        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9066        ConditionResult::from(found)
9067    }
9068
9069    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9070    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9071        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9072        let found = ctx.find_segments("SEQ").iter().any(|s| {
9073            s.elements
9074                .first()
9075                .and_then(|e| e.first())
9076                .is_some_and(|v| v == "Z03" || v == "ZF5")
9077        });
9078        ConditionResult::from(found)
9079    }
9080
9081    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9082    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9083        // TODO: implement
9084        ConditionResult::Unknown
9085    }
9086
9087    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9088    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9089        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9090        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9091        ConditionResult::from(loc_count > 0)
9092    }
9093
9094    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9095    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9096        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9097        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9098        ConditionResult::from(loc_count > 0)
9099    }
9100
9101    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9102    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9103        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9104        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9105        ConditionResult::from(loc_count > 0)
9106    }
9107
9108    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9109    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9110        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9111        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9112        ConditionResult::from(loc_count > 0)
9113    }
9114
9115    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9116    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9117        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9118        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9119        ConditionResult::from(loc_count > 0)
9120    }
9121
9122    /// [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...
9123    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9124        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9125        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9126        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9127        let has_netzlokation = rff_z33.iter().any(|s| {
9128            s.elements
9129                .first()
9130                .and_then(|e| e.get(1))
9131                .is_some_and(|v| v == "9992000001256")
9132        });
9133        ConditionResult::from(!has_netzlokation)
9134    }
9135
9136    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9137    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9138        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9139        let found = ctx.find_segments("SEQ").iter().any(|s| {
9140            s.elements
9141                .first()
9142                .and_then(|e| e.first())
9143                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9144        });
9145        ConditionResult::from(found)
9146    }
9147
9148    /// [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
9149    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9150        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9151        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9152        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9153            s.elements
9154                .first()
9155                .and_then(|e| e.first())
9156                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9157        });
9158        ConditionResult::from(has_cav)
9159    }
9160
9161    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9162    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9163        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9164        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9165        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9166            s.elements
9167                .first()
9168                .and_then(|e| e.first())
9169                .is_some_and(|v| v == "MBW")
9170        });
9171        ConditionResult::from(has_cav)
9172    }
9173
9174    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9175    fn evaluate_2344(&self, ctx: &EvaluationContext) -> ConditionResult {
9176        // HAND-EDITED (#343): a repetition bound per Marktlokation or Tranche of the
9177        // transaction; as a presence condition it holds where the transaction names one
9178        // (SG5 LOC+Z16 / LOC+Z21), within the enclosing SG4 when the scope reaches one.
9179        let loc = ctx.scoped_find_segments_in("SG4", "LOC");
9180        ConditionResult::from(
9181            loc.iter()
9182                .any(|s| ["Z16", "Z21"].contains(&s.get_element(0))),
9183        )
9184    }
9185
9186    /// [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...
9187    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9188        // TODO: implement
9189        ConditionResult::Unknown
9190    }
9191
9192    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9193    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9194        // TODO: implement
9195        ConditionResult::Unknown
9196    }
9197
9198    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9199    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9200        // TODO: implement
9201        ConditionResult::Unknown
9202    }
9203
9204    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9205    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9206        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9207        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9208        ConditionResult::from(loc_count > 0)
9209    }
9210
9211    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9212    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9213        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9214        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9215        ConditionResult::from(loc_count > 0)
9216    }
9217
9218    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9219    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9220        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9221        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9222        ConditionResult::from(loc_count > 0)
9223    }
9224
9225    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9226    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9227        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9228        let found = ctx.find_segments("LOC").iter().any(|s| {
9229            s.elements
9230                .first()
9231                .and_then(|e| e.first())
9232                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9233        });
9234        ConditionResult::from(found)
9235    }
9236
9237    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9238    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9239        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9240        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9241        ConditionResult::from(loc_count > 0)
9242    }
9243
9244    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9245    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9246        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9247        let found = ctx.find_segments("LOC").iter().any(|s| {
9248            s.elements
9249                .first()
9250                .and_then(|e| e.first())
9251                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9252        });
9253        ConditionResult::from(found)
9254    }
9255}
9256
9257// HAND-EDITED: helpers for [223] / [265].
9258
9259use mig_types::segment::OwnedSegment;
9260
9261/// One SG8 repetition: its SEQ qualifier, its RFF segments and the
9262/// Marktlokation IDs of its RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9263struct Sg8Block {
9264    seq: String,
9265    /// The SEQ's Zeitraum-ID (DE1050).
9266    zeitraum: String,
9267    rffs: Vec<OwnedSegment>,
9268    malo_refs: Vec<String>,
9269    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9270}
9271
9272fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9273    seg.elements
9274        .get(element)
9275        .and_then(|e| e.get(comp))
9276        .map(String::as_str)
9277}
9278
9279/// The message's SG8 repetitions, from the group navigator when there is one,
9280/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9281/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9282/// evaluates BO4E input without a navigator.) `None` without either.
9283fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9284    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9285        rffs.iter()
9286            .filter(|s| component(s, 0, 0) == Some("Z18"))
9287            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9288            .collect()
9289    };
9290    if let Some(nav) = ctx.navigator() {
9291        let sg8 = ["SG4", "SG8"];
9292        return Some(
9293            (0..nav.group_instance_count(&sg8))
9294                .map(|i| Sg8Block {
9295                    seq: nav
9296                        .find_segments_in_group("SEQ", &sg8, i)
9297                        .first()
9298                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9299                        .unwrap_or_default(),
9300                    zeitraum: nav
9301                        .find_segments_in_group("SEQ", &sg8, i)
9302                        .first()
9303                        .and_then(|s| component(s, 1, 0).map(str::to_string))
9304                        .unwrap_or_default(),
9305                    rffs: nav.find_segments_in_group("RFF", &sg8, i),
9306                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9307                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9308                        .map(|j| {
9309                            (
9310                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9311                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9312                            )
9313                        })
9314                        .collect(),
9315                })
9316                .collect(),
9317        );
9318    }
9319    if ctx.segments.is_empty() {
9320        return None;
9321    }
9322    let mut blocks: Vec<Sg8Block> = Vec::new();
9323    let mut rffs: Vec<OwnedSegment> = Vec::new();
9324    for seg in ctx.segments {
9325        match seg.id.as_str() {
9326            "SEQ" => {
9327                if let Some(b) = blocks.last_mut() {
9328                    b.malo_refs = malo_refs(&rffs);
9329                    b.rffs = std::mem::take(&mut rffs);
9330                }
9331                rffs.clear();
9332                blocks.push(Sg8Block {
9333                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9334                    zeitraum: component(seg, 1, 0).unwrap_or_default().to_string(),
9335                    rffs: Vec::new(),
9336                    malo_refs: Vec::new(),
9337                    sg10: Vec::new(),
9338                });
9339            }
9340            // Segments after the SG8s end the last one.
9341            "NAD" | "UNT" | "IDE" => {
9342                if let Some(b) = blocks.last_mut() {
9343                    if b.rffs.is_empty() {
9344                        b.malo_refs = malo_refs(&rffs);
9345                        b.rffs = std::mem::take(&mut rffs);
9346                    }
9347                }
9348                rffs.clear();
9349                if seg.id == "IDE" {
9350                    continue;
9351                }
9352                break;
9353            }
9354            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9355            "CCI" => {
9356                if let Some(b) = blocks.last_mut() {
9357                    b.sg10.push((vec![seg.clone()], Vec::new()));
9358                }
9359            }
9360            "CAV" => {
9361                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9362                    cavs.push(seg.clone());
9363                }
9364            }
9365            _ => {}
9366        }
9367    }
9368    if let Some(b) = blocks.last_mut() {
9369        if b.rffs.is_empty() {
9370            b.malo_refs = malo_refs(&rffs);
9371            b.rffs = rffs;
9372        }
9373    }
9374    Some(blocks)
9375}
9376
9377// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9378// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9379
9380/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9381/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9382/// one; any SG10 of the message otherwise.
9383fn sg10_has(
9384    ctx: &EvaluationContext,
9385    this_sg8: bool,
9386    pred: impl Fn(&[OwnedSegment]) -> bool,
9387) -> ConditionResult {
9388    if this_sg8 {
9389        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9390            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9391        }
9392    }
9393    let Some(blocks) = sg8_blocks(ctx) else {
9394        return ConditionResult::False;
9395    };
9396    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9397        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9398        pred(&segs)
9399    }))
9400}
9401
9402/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9403/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9404/// otherwise.
9405fn this_sg10_has(
9406    ctx: &EvaluationContext,
9407    pred: impl Fn(&[OwnedSegment]) -> bool,
9408) -> ConditionResult {
9409    if let Some(path) = ctx.enclosing_scope("SG10") {
9410        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9411            .iter()
9412            .flat_map(|tag| ctx.find_segments_at(path, tag))
9413            .collect();
9414        return ConditionResult::from(pred(&segs));
9415    }
9416    sg10_has(ctx, false, pred)
9417}
9418
9419/// [114]–[116]: "das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV
9420/// `code` in dieser SG vorhanden".
9421fn spannungsebene_in_this_sg10(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9422    this_sg10_has(ctx, |segs| {
9423        segs.iter()
9424            .any(|s| s.id == "CCI" && component(s, 2, 0) == Some("E03"))
9425            && segs
9426                .iter()
9427                .any(|s| s.id == "CAV" && component(s, 0, 0) == Some(code))
9428    })
9429}
9430
9431/// The reference (C506 DE1154) of the RFF whose field is judged.
9432fn rff_reference<'c>(ctx: &EvaluationContext<'c>) -> Option<&'c str> {
9433    ctx.resolved_segment?
9434        .first()?
9435        .get(1)
9436        .map(String::as_str)
9437        .filter(|v| !v.is_empty())
9438}
9439
9440/// "Wenn in demselben RFF der Code … im DE1153 vorhanden": the qualifier of the
9441/// RFF whose field is judged; message-wide only where no segment is resolved.
9442fn same_rff_qualifier(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9443    match ctx.resolved_segment {
9444        Some(elements) => ConditionResult::from(
9445            elements
9446                .first()
9447                .and_then(|e| e.first())
9448                .is_some_and(|q| q == code),
9449        ),
9450        None => ctx.has_qualifier("RFF", 0, code),
9451    }
9452}
9453
9454/// HAND-EDITED: `[126]`/`[127]`/`[128]` — the value is the 1-based position of
9455/// the resolved RFF among the SG6 RFF of `qualifiers` in its SG4 ("Das erste
9456/// SG6 RFF+Z49/Z53 hat somit die 1, das zweite die 2").
9457fn repetition_ordinal(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9458    let (Some(value), Some(segment)) = (ctx.resolved_value, ctx.resolved_segment) else {
9459        return ConditionResult::Unknown;
9460    };
9461    let siblings: Vec<_> = ctx
9462        .scoped_find_segments_in("SG4", "RFF")
9463        .into_iter()
9464        .filter(|s| qualifiers.contains(&s.get_component(0, 0)))
9465        .collect();
9466    match siblings
9467        .iter()
9468        .position(|s| s.elements.as_slice() == segment)
9469    {
9470        Some(i) => ConditionResult::from(value == (i + 1).to_string()),
9471        None => ConditionResult::Unknown,
9472    }
9473}
9474
9475/// [131]/[132]: the DTM+Z25 of an SG6 whose RFF carries Zeitraum-ID "1"
9476/// (DE1156), against 0:00 German time of the day after DTM+137
9477/// (Nachrichtendatum). `False` when the SG6 has another Zeitraum-ID — [401],
9478/// [134] and [135] judge those.
9479///
9480/// "Dieses DTM+Z25" is the one of the enclosing SG6. Without a scope, every
9481/// SG6 with Zeitraum-ID 1 must pass.
9482// HAND-EDITED
9483fn first_period_start_vs_next_day(
9484    ctx: &EvaluationContext,
9485    holds: impl Fn(chrono::NaiveDateTime, chrono::NaiveDateTime) -> bool,
9486) -> ConditionResult {
9487    use chrono::Duration;
9488    let Some(sent) = ctx
9489        .find_segments_with_qualifier("DTM", 0, "137")
9490        .first()
9491        .and_then(|dtm| german_local_time(dtm.get_component(0, 1)))
9492    else {
9493        return ConditionResult::Unknown;
9494    };
9495    let next_day = (sent.date() + Duration::days(1)).and_hms_opt(0, 0, 0);
9496    let Some(next_day) = next_day else {
9497        return ConditionResult::Unknown;
9498    };
9499    let judge = |path: &[(&str, usize)]| -> Option<ConditionResult> {
9500        let first_period = ctx
9501            .find_segments_at(path, "RFF")
9502            .iter()
9503            .any(|rff| rff.get_component(0, 2) == "1");
9504        if !first_period {
9505            return None;
9506        }
9507        let start = ctx
9508            .find_segments_at(path, "DTM")
9509            .iter()
9510            .find(|dtm| dtm.get_component(0, 0) == "Z25")
9511            .and_then(|dtm| german_local_time(dtm.get_component(0, 1)));
9512        Some(match start {
9513            Some(start) => ConditionResult::from(holds(start, next_day)),
9514            None => ConditionResult::Unknown,
9515        })
9516    };
9517    if let Some(scope) = ctx.enclosing_scope("SG6") {
9518        return judge(scope).unwrap_or(ConditionResult::False);
9519    }
9520    if ctx.navigator().is_none() {
9521        return ConditionResult::Unknown;
9522    }
9523    let mut result = ConditionResult::False;
9524    for sg4 in 0..ctx.group_instance_count(&["SG4"]) {
9525        for sg6 in 0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6") {
9526            match judge(&[("SG4", sg4), ("SG6", sg6)]) {
9527                Some(ConditionResult::False) => return ConditionResult::False,
9528                Some(ConditionResult::Unknown) => result = ConditionResult::Unknown,
9529                Some(ConditionResult::True) if result == ConditionResult::False => {
9530                    result = ConditionResult::True
9531                }
9532                _ => {}
9533            }
9534        }
9535    }
9536    result
9537}
9538
9539/// [134]/[135]/[401]: the DTM+Z25 of an SG6 whose RFF has one of
9540/// `qualifiers` and a Zeitraum-ID (DE1156) other than "1" must equal the
9541/// DTM+Z26 of the SG6 with one of `qualifiers` and the next lower
9542/// Zeitraum-ID. `False` for an SG6 with another qualifier or Zeitraum-ID 1 —
9543/// [131] and the sibling conditions judge those — and when there is no such
9544/// earlier SG6.
9545///
9546/// "Dieses DTM+Z25" is the one of the enclosing SG6, compared with the SG6s
9547/// of its SG4. Without a scope, every such SG6 must pass.
9548// HAND-EDITED
9549fn continues_previous_period(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9550    // (Zeitraum-ID, DTM+Z25, DTM+Z26) of the SG6 at `path`, when its RFF
9551    // has one of `qualifiers`.
9552    let period = |path: &[(&str, usize)]| -> Option<(u32, Option<String>, Option<String>)> {
9553        let id = ctx.find_segments_at(path, "RFF").iter().find_map(|rff| {
9554            qualifiers
9555                .contains(&rff.get_component(0, 0))
9556                .then(|| rff.get_component(0, 2).parse::<u32>().ok())
9557                .flatten()
9558        })?;
9559        let dtms = ctx.find_segments_at(path, "DTM");
9560        let date = |qualifier: &str| {
9561            dtms.iter()
9562                .find(|dtm| dtm.get_component(0, 0) == qualifier)
9563                .map(|dtm| dtm.get_component(0, 1).to_string())
9564                .filter(|v| !v.is_empty())
9565        };
9566        Some((id, date("Z25"), date("Z26")))
9567    };
9568    let same_instant = |a: &str, b: &str| match (german_local_time(a), german_local_time(b)) {
9569        (Some(a), Some(b)) => a == b,
9570        _ => a == b,
9571    };
9572    let judge = |sg4: usize, sg6: usize| -> Option<ConditionResult> {
9573        let (id, start, _) = period(&[("SG4", sg4), ("SG6", sg6)])?;
9574        if id == 1 {
9575            return None;
9576        }
9577        let previous = (0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6"))
9578            .filter_map(|other| period(&[("SG4", sg4), ("SG6", other)]))
9579            .filter(|(other, _, _)| *other < id)
9580            .max_by_key(|(other, _, _)| *other);
9581        Some(match (start, previous) {
9582            (None, _) => ConditionResult::Unknown,
9583            (Some(_), None) | (Some(_), Some((_, _, None))) => ConditionResult::False,
9584            (Some(start), Some((_, _, Some(end)))) => {
9585                ConditionResult::from(same_instant(&start, &end))
9586            }
9587        })
9588    };
9589    if let Some(scope) = ctx.enclosing_scope("SG6") {
9590        let at = |id: &str| scope.iter().find(|(g, _)| *g == id).map(|(_, i)| *i);
9591        return match (at("SG4"), at("SG6")) {
9592            (Some(sg4), Some(sg6)) => judge(sg4, sg6).unwrap_or(ConditionResult::False),
9593            _ => ConditionResult::Unknown,
9594        };
9595    }
9596    if ctx.navigator().is_none() {
9597        return ConditionResult::Unknown;
9598    }
9599    let mut result = ConditionResult::False;
9600    for sg4 in 0..ctx.group_instance_count(&["SG4"]) {
9601        for sg6 in 0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6") {
9602            match judge(sg4, sg6) {
9603                Some(ConditionResult::False) => return ConditionResult::False,
9604                Some(ConditionResult::Unknown) => result = ConditionResult::Unknown,
9605                Some(ConditionResult::True) if result == ConditionResult::False => {
9606                    result = ConditionResult::True
9607                }
9608                _ => {}
9609            }
9610        }
9611    }
9612    result
9613}