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