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        // whatever its SEQ (Z03, ZA3/ZA4, ZF5 all carry [316]; #305).
5883        ctx.this_group_lacks_qualifier("RFF", 0, "Z14", &["SG4", "SG8"])
5884    }
5885
5886    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5887    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5888        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5889        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5890        if found {
5891            ConditionResult::True
5892        } else {
5893            ConditionResult::Unknown
5894        }
5895    }
5896
5897    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5898    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5899        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5900        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5901        if found {
5902            ConditionResult::True
5903        } else {
5904            ConditionResult::Unknown
5905        }
5906    }
5907
5908    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5909    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5910        let com_segs = ctx.find_segments("COM");
5911        let has_em = com_segs.iter().any(|s| {
5912            s.elements
5913                .first()
5914                .and_then(|e| e.get(1))
5915                .is_some_and(|v| v == "EM")
5916        });
5917        ConditionResult::from(has_em)
5918    }
5919
5920    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5921    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5922        let com_segs = ctx.find_segments("COM");
5923        let has_phone = com_segs.iter().any(|s| {
5924            s.elements
5925                .first()
5926                .and_then(|e| e.get(1))
5927                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5928        });
5929        ConditionResult::from(has_phone)
5930    }
5931
5932    /// [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...
5933    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5934        ctx.external.evaluate("code_list_membership_check")
5935    }
5936
5937    /// [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...
5938    // 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)
5939    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5940        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5941        // reaches an SG8; the message as before otherwise.
5942        if let Some(scope) = ctx.enclosing_scope("SG8") {
5943            let Some((_, zeitraum)) =
5944                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5945            else {
5946                return ConditionResult::False;
5947            };
5948            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5949            return ConditionResult::from(sg6s.iter().any(|p| {
5950                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5951                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5952                        && s.get_component(0, 2) == zeitraum
5953                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5954                    s.get_element(0) == "Z25"
5955                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5956                })
5957            }));
5958        }
5959        const THRESHOLD: &str = "202212312300+00";
5960        let seq_zeitraum_ids: Vec<String> = ctx
5961            .find_segments("SEQ")
5962            .iter()
5963            .filter_map(|s| {
5964                s.elements
5965                    .get(1)
5966                    .and_then(|e: &Vec<String>| e.first())
5967                    .cloned()
5968            })
5969            .filter(|v| !v.is_empty())
5970            .collect();
5971        if seq_zeitraum_ids.is_empty() {
5972            return ConditionResult::Unknown;
5973        }
5974        let rff_segs = ctx.find_segments("RFF");
5975        let matching_rff = rff_segs.iter().any(|rff| {
5976            let qual = rff
5977                .elements
5978                .first()
5979                .and_then(|e: &Vec<String>| e.first())
5980                .map(|v| v.as_str())
5981                .unwrap_or("");
5982            if !["Z47", "Z48", "Z49"].contains(&qual) {
5983                return false;
5984            }
5985            let zid = rff
5986                .elements
5987                .first()
5988                .and_then(|e| e.get(2))
5989                .map(|v| v.as_str())
5990                .unwrap_or("");
5991            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5992        });
5993        if !matching_rff {
5994            return ConditionResult::False;
5995        }
5996        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5997            let val = dtm
5998                .elements
5999                .first()
6000                .and_then(|e| e.get(1))
6001                .map(|v| v.replace("?+", "+"))
6002                .unwrap_or_default();
6003            if !val.is_empty() && val.as_str() >= THRESHOLD {
6004                return ConditionResult::True;
6005            }
6006        }
6007        ConditionResult::False
6008    }
6009
6010    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
6011    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
6012        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6013        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
6014        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6015        let has_a03_e0096 = sts_segs.iter().any(|s| {
6016            s.elements
6017                .get(2)
6018                .and_then(|e| e.first())
6019                .is_some_and(|v| v == "A03")
6020                && s.elements
6021                    .get(2)
6022                    .and_then(|e| e.get(1))
6023                    .is_some_and(|v| v == "E_0096")
6024        });
6025        let has_a04_e0097 = sts_segs.iter().any(|s| {
6026            s.elements
6027                .get(2)
6028                .and_then(|e| e.first())
6029                .is_some_and(|v| v == "A04")
6030                && s.elements
6031                    .get(2)
6032                    .and_then(|e| e.get(1))
6033                    .is_some_and(|v| v == "E_0097")
6034        });
6035        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
6036    }
6037
6038    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
6039    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
6040        let seq_segs = ctx.find_segments("SEQ");
6041        let has_seq = seq_segs.iter().any(|s| {
6042            s.elements
6043                .first()
6044                .and_then(|e| e.first())
6045                .is_some_and(|v| v == "Z98")
6046        });
6047        let cci_segs = ctx.find_segments("CCI");
6048        let has_cci = cci_segs.iter().any(|s| {
6049            s.elements
6050                .first()
6051                .and_then(|e| e.first())
6052                .is_some_and(|v| v == "6")
6053                && s.elements
6054                    .get(2)
6055                    .and_then(|e| e.first())
6056                    .is_some_and(|v| v == "ZA9")
6057        });
6058        ConditionResult::from(!(has_seq && has_cci))
6059    }
6060
6061    /// [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
6062    // 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)
6063    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
6064        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
6065        // reaches an SG8; the message as before otherwise.
6066        if let Some(scope) = ctx.enclosing_scope("SG8") {
6067            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
6068            return ConditionResult::from(z01s.iter().any(|p| {
6069                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
6070            }));
6071        }
6072        let nav = match ctx.navigator {
6073            Some(n) => n,
6074            None => {
6075                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
6076                    return ConditionResult::Unknown;
6077                }
6078                let has_cci_za9 = ctx
6079                    .find_segments_with_qualifier("CCI", 0, "6")
6080                    .iter()
6081                    .any(|s| {
6082                        s.elements
6083                            .get(2)
6084                            .and_then(|e: &Vec<String>| e.first())
6085                            .map(|v| v.as_str())
6086                            == Some("ZA9")
6087                    });
6088                return ConditionResult::from(!has_cci_za9);
6089            }
6090        };
6091        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6092        if sg8_count == 0 {
6093            return ConditionResult::Unknown;
6094        }
6095        for sg8_idx in 0..sg8_count {
6096            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
6097            let mut has_cci_za9 = false;
6098            for sg10_idx in 0..sg10_count {
6099                let ccis = nav.find_segments_in_child_group(
6100                    "CCI",
6101                    &["SG4", "SG8"],
6102                    sg8_idx,
6103                    "SG10",
6104                    sg10_idx,
6105                );
6106                if ccis.iter().any(|s| {
6107                    s.elements
6108                        .first()
6109                        .and_then(|e: &Vec<String>| e.first())
6110                        .map(|v| v.as_str())
6111                        == Some("6")
6112                        && s.elements
6113                            .get(2)
6114                            .and_then(|e: &Vec<String>| e.first())
6115                            .map(|v| v.as_str())
6116                            == Some("ZA9")
6117                }) {
6118                    has_cci_za9 = true;
6119                    break;
6120                }
6121            }
6122            if !has_cci_za9 {
6123                return ConditionResult::True;
6124            }
6125        }
6126        ConditionResult::False
6127    }
6128
6129    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
6130    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
6131        let rff_segs = ctx.find_segments("RFF");
6132        let found = rff_segs.iter().any(|s| {
6133            s.elements
6134                .first()
6135                .and_then(|e| e.first())
6136                .is_some_and(|v| v == "Z31")
6137        });
6138        ConditionResult::from(found)
6139    }
6140
6141    /// [334] Nur MP-ID aus Sparte Gas
6142    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
6143        // Check: Marktpartner-ID is from Sparte Gas
6144        ctx.external.evaluate("sector_check")
6145    }
6146
6147    /// [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...
6148    // 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)
6149    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6150        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6151        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6152        let z58_ids: Vec<String> = ctx
6153            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6154            .iter()
6155            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6156            .filter(|v| !v.is_empty())
6157            .collect();
6158        if z58_ids.is_empty() {
6159            return ConditionResult::Unknown;
6160        }
6161        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6162            return ConditionResult::False;
6163        }
6164        let rff_z31_ids: Vec<String> = ctx
6165            .find_segments_with_qualifier("RFF", 0, "Z31")
6166            .iter()
6167            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6168            .filter(|v| !v.is_empty())
6169            .collect();
6170        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6171    }
6172
6173    /// [336] Wenn in Änderungsmeldung gefüllt
6174    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6175        // TODO: implement
6176        ConditionResult::Unknown
6177    }
6178
6179    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6180    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6181        let cci_segs = ctx.find_segments("CCI");
6182        let has_cci = cci_segs.iter().any(|s| {
6183            s.elements
6184                .get(2)
6185                .and_then(|e| e.first())
6186                .is_some_and(|v| v == "ZB4")
6187        });
6188        let cav_segs = ctx.find_segments("CAV");
6189        let has_cav = cav_segs.iter().any(|s| {
6190            s.elements
6191                .first()
6192                .and_then(|e| e.first())
6193                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6194        });
6195        ConditionResult::from(has_cci && has_cav)
6196    }
6197
6198    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6199    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6200        let cci_segs = ctx.find_segments("CCI");
6201        let has_cci = cci_segs.iter().any(|s| {
6202            s.elements
6203                .get(2)
6204                .and_then(|e| e.first())
6205                .is_some_and(|v| v == "ZB4")
6206        });
6207        let cav_segs = ctx.find_segments("CAV");
6208        let has_cav = cav_segs.iter().any(|s| {
6209            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6210                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6211            })
6212        });
6213        ConditionResult::from(has_cci && has_cav)
6214    }
6215
6216    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6217    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6218        let cci_segs = ctx.find_segments("CCI");
6219        let has_cci = cci_segs.iter().any(|s| {
6220            s.elements
6221                .get(2)
6222                .and_then(|e| e.first())
6223                .is_some_and(|v| v == "ZB4")
6224        });
6225        let cav_segs = ctx.find_segments("CAV");
6226        let has_cav = cav_segs.iter().any(|s| {
6227            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6228                [
6229                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6230                ]
6231                .contains(&v.as_str())
6232            })
6233        });
6234        ConditionResult::from(has_cci && has_cav)
6235    }
6236
6237    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6238    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6239        let cci_segs = ctx.find_segments("CCI");
6240        let has_cci = cci_segs.iter().any(|s| {
6241            s.elements
6242                .get(2)
6243                .and_then(|e| e.first())
6244                .is_some_and(|v| v == "ZB4")
6245        });
6246        let cav_segs = ctx.find_segments("CAV");
6247        let has_cav = cav_segs.iter().any(|s| {
6248            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6249                [
6250                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6251                ]
6252                .contains(&v.as_str())
6253            })
6254        });
6255        ConditionResult::from(has_cci && has_cav)
6256    }
6257
6258    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6259    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6260        let cci_segs = ctx.find_segments("CCI");
6261        let has_cci = cci_segs.iter().any(|s| {
6262            s.elements
6263                .get(2)
6264                .and_then(|e| e.first())
6265                .is_some_and(|v| v == "ZB4")
6266        });
6267        let cav_segs = ctx.find_segments("CAV");
6268        let has_cav = cav_segs.iter().any(|s| {
6269            s.elements
6270                .first()
6271                .and_then(|e| e.first())
6272                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6273        });
6274        ConditionResult::from(has_cci && has_cav)
6275    }
6276
6277    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6278    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6279        // TODO: implement
6280        ConditionResult::Unknown
6281    }
6282
6283    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6284    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6285        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6286        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6287        let has_cci = cci_segs.iter().any(|s| {
6288            s.elements
6289                .get(2)
6290                .and_then(|e| e.first())
6291                .is_some_and(|v| v == "ZB4")
6292        });
6293        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6294        let has_cav = cav_segs.iter().any(|s| {
6295            s.elements
6296                .first()
6297                .and_then(|e| e.first())
6298                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6299        });
6300        ConditionResult::from(has_cci && has_cav)
6301    }
6302
6303    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6304    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6305        let loc_segs = ctx.find_segments("LOC");
6306        let found = loc_segs.iter().any(|s| {
6307            s.elements
6308                .first()
6309                .and_then(|e| e.first())
6310                .is_some_and(|v| v == "Z17")
6311        });
6312        ConditionResult::from(found)
6313    }
6314
6315    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6316    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6317        // HAND-EDITED: the CCI and the CAV of one SG10.
6318        sg10_has(ctx, false, |segs| {
6319            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6320        })
6321    }
6322
6323    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6324    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6325        let loc_segs = ctx.find_segments("LOC");
6326        let found = loc_segs.iter().any(|s| {
6327            s.elements
6328                .first()
6329                .and_then(|e| e.first())
6330                .is_some_and(|v| v == "Z16")
6331        });
6332        ConditionResult::from(!found)
6333    }
6334
6335    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6336    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6337        let loc_segs = ctx.find_segments("LOC");
6338        let found = loc_segs.iter().any(|s| {
6339            s.elements
6340                .first()
6341                .and_then(|e| e.first())
6342                .is_some_and(|v| v == "Z21")
6343        });
6344        ConditionResult::from(!found)
6345    }
6346
6347    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6348    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6349        let loc_segs = ctx.find_segments("LOC");
6350        let found = loc_segs.iter().any(|s| {
6351            s.elements
6352                .first()
6353                .and_then(|e| e.first())
6354                .is_some_and(|v| v == "Z17")
6355        });
6356        ConditionResult::from(!found)
6357    }
6358
6359    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6360    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6361        let seq_segs = ctx.find_segments("SEQ");
6362        let has_seq = seq_segs.iter().any(|s| {
6363            s.elements
6364                .first()
6365                .and_then(|e| e.first())
6366                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6367        });
6368        let rff_segs = ctx.find_segments("RFF");
6369        let has_rff = rff_segs.iter().any(|s| {
6370            s.elements
6371                .first()
6372                .and_then(|e| e.first())
6373                .is_some_and(|v| v == "Z31")
6374        });
6375        ConditionResult::from(has_seq && has_rff)
6376    }
6377
6378    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6379    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6380        let sts_segs = ctx.find_segments("STS");
6381        let found = sts_segs.iter().any(|s| {
6382            s.elements
6383                .first()
6384                .and_then(|e| e.first())
6385                .is_some_and(|v| v == "E01")
6386                && s.elements
6387                    .get(2)
6388                    .and_then(|e| e.first())
6389                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6390        });
6391        ConditionResult::from(found)
6392    }
6393
6394    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6395    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6396        let sts_segs = ctx.find_segments("STS");
6397        let found = sts_segs.iter().any(|s| {
6398            s.elements
6399                .get(1)
6400                .and_then(|e| e.first())
6401                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6402        });
6403        ConditionResult::from(found)
6404    }
6405
6406    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6407    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6408        let sts_segs = ctx.find_segments("STS");
6409        let found = sts_segs.iter().any(|s| {
6410            s.elements
6411                .first()
6412                .and_then(|e| e.first())
6413                .is_some_and(|v| v == "E01")
6414                && s.elements
6415                    .get(2)
6416                    .and_then(|e| e.first())
6417                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6418        });
6419        ConditionResult::from(found)
6420    }
6421
6422    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6423    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6424        let sts_segs = ctx.find_segments("STS");
6425        let found = sts_segs.iter().any(|s| {
6426            s.elements
6427                .first()
6428                .and_then(|e| e.first())
6429                .is_some_and(|v| v == "E01")
6430                && s.elements
6431                    .get(2)
6432                    .and_then(|e| e.first())
6433                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6434        });
6435        ConditionResult::from(found)
6436    }
6437
6438    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6439    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6440        let sts_segs = ctx.find_segments("STS");
6441        let found = sts_segs.iter().any(|s| {
6442            s.elements
6443                .first()
6444                .and_then(|e| e.first())
6445                .is_some_and(|v| v == "E01")
6446                && s.elements
6447                    .get(2)
6448                    .and_then(|e| e.first())
6449                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6450        });
6451        ConditionResult::from(found)
6452    }
6453
6454    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6455    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6456        let sts_segs = ctx.find_segments("STS");
6457        let found = sts_segs.iter().any(|s| {
6458            s.elements
6459                .first()
6460                .and_then(|e| e.first())
6461                .is_some_and(|v| v == "E01")
6462                && s.elements
6463                    .get(2)
6464                    .and_then(|e| e.first())
6465                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6466        });
6467        ConditionResult::from(found)
6468    }
6469
6470    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6471    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6472    // Preserve across regeneration until the codegen prompt is updated.
6473    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6474        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6475    }
6476
6477    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6478    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6479    // Preserve across regeneration until the codegen prompt is updated.
6480    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6481        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6482    }
6483
6484    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6485    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6486        let sts_segs = ctx.find_segments("STS");
6487        let found = sts_segs.iter().any(|s| {
6488            s.elements
6489                .first()
6490                .and_then(|e| e.first())
6491                .is_some_and(|v| v == "E01")
6492                && s.elements
6493                    .get(2)
6494                    .and_then(|e| e.first())
6495                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6496        });
6497        ConditionResult::from(found)
6498    }
6499
6500    /// [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
6501    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6502        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6503        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6504        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6505        let has_excluded = sts_segs.iter().any(|s| {
6506            let code = s
6507                .elements
6508                .get(2)
6509                .and_then(|e| e.first())
6510                .map(|v| v.as_str());
6511            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6512            matches!(
6513                (code, ebd),
6514                (Some("A04"), Some("E_0047"))
6515                    | (Some("A04"), Some("E_0014"))
6516                    | (Some("A03"), Some("E_0049"))
6517                    | (Some("A03"), Some("E_0004"))
6518            )
6519        });
6520        ConditionResult::from(!has_excluded)
6521    }
6522
6523    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6524    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6525    // Preserve across regeneration until the codegen prompt is updated.
6526    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6527        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A30"])
6528    }
6529
6530    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6531    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6532        let sts_segs = ctx.find_segments("STS");
6533        let found = sts_segs.iter().any(|s| {
6534            s.elements
6535                .first()
6536                .and_then(|e| e.first())
6537                .is_some_and(|v| v == "E01")
6538                && s.elements
6539                    .get(2)
6540                    .and_then(|e| e.first())
6541                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6542        });
6543        ConditionResult::from(found)
6544    }
6545
6546    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6547    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6548    // Preserve across regeneration until the codegen prompt is updated.
6549    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6550        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A41"])
6551    }
6552
6553    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6554    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6555        let cav_segs = ctx.find_segments("CAV");
6556        let found = cav_segs.iter().any(|s| {
6557            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6558                [
6559                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6560                ]
6561                .contains(&v.as_str())
6562            })
6563        });
6564        ConditionResult::from(found)
6565    }
6566
6567    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6568    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6569        let seq_segs = ctx.find_segments("SEQ");
6570        let found = seq_segs.iter().any(|s| {
6571            s.elements
6572                .first()
6573                .and_then(|e| e.first())
6574                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6575        });
6576        ConditionResult::from(!found)
6577    }
6578
6579    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6580    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6581        let cav_segs = ctx.find_segments("CAV");
6582        let found = cav_segs.iter().any(|s| {
6583            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6584                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6585            })
6586        });
6587        ConditionResult::from(found)
6588    }
6589
6590    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6591    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6592        // TODO: implement
6593        ConditionResult::Unknown
6594    }
6595
6596    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6597    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6598        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6599        ctx.lacks_qualifier("SEQ", 0, "Z24")
6600    }
6601
6602    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6603    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6604        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6605        ctx.lacks_qualifier("SEQ", 0, "Z22")
6606    }
6607
6608    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6609    // 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)
6610    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6611        let nav = match ctx.navigator {
6612            Some(n) => n,
6613            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6614        };
6615        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6616        for i in 0..sg8_count {
6617            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6618            for j in 0..sg10_count {
6619                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6620                let has_cci = ccis.iter().any(|s| {
6621                    s.elements
6622                        .first()
6623                        .and_then(|e: &Vec<String>| e.first())
6624                        .is_some_and(|v: &String| v == "15")
6625                        && s.elements
6626                            .get(2)
6627                            .and_then(|e: &Vec<String>| e.first())
6628                            .is_some_and(|v: &String| v == "Z21")
6629                });
6630                if has_cci {
6631                    let cavs =
6632                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6633                    if cavs.iter().any(|s| {
6634                        s.elements
6635                            .first()
6636                            .and_then(|e: &Vec<String>| e.first())
6637                            .is_some_and(|v: &String| v == "AUS")
6638                    }) {
6639                        return ConditionResult::True;
6640                    }
6641                }
6642            }
6643        }
6644        ConditionResult::False
6645    }
6646
6647    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6648    // 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)
6649    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6650        let nav = match ctx.navigator {
6651            Some(n) => n,
6652            None => {
6653                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6654                return match r {
6655                    ConditionResult::True => ConditionResult::False,
6656                    ConditionResult::False => ConditionResult::True,
6657                    ConditionResult::Unknown => ConditionResult::Unknown,
6658                };
6659            }
6660        };
6661        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6662        for i in 0..sg8_count {
6663            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6664            for j in 0..sg10_count {
6665                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6666                let has_cci = ccis.iter().any(|s| {
6667                    s.elements
6668                        .first()
6669                        .and_then(|e: &Vec<String>| e.first())
6670                        .is_some_and(|v: &String| v == "15")
6671                        && s.elements
6672                            .get(2)
6673                            .and_then(|e: &Vec<String>| e.first())
6674                            .is_some_and(|v: &String| v == "Z21")
6675                });
6676                if has_cci {
6677                    let cavs =
6678                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6679                    if cavs.iter().any(|s| {
6680                        s.elements
6681                            .first()
6682                            .and_then(|e: &Vec<String>| e.first())
6683                            .is_some_and(|v: &String| v == "AUS")
6684                    }) {
6685                        return ConditionResult::False;
6686                    }
6687                }
6688            }
6689        }
6690        ConditionResult::True
6691    }
6692
6693    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6694    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6695        let cci_segs = ctx.find_segments("CCI");
6696        let found = cci_segs.iter().any(|s| {
6697            s.elements
6698                .first()
6699                .and_then(|e| e.first())
6700                .is_some_and(|v| v == "15")
6701                && s.elements
6702                    .get(2)
6703                    .and_then(|e| e.first())
6704                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6705        });
6706        ConditionResult::from(found)
6707    }
6708
6709    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6710    // 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)
6711    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6712        let nav = match ctx.navigator {
6713            Some(n) => n,
6714            None => {
6715                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6716            }
6717        };
6718        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6719        for i in 0..sg8_count {
6720            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6721            for j in 0..sg10_count {
6722                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6723                let has_cci = ccis.iter().any(|s| {
6724                    s.elements
6725                        .first()
6726                        .and_then(|e: &Vec<String>| e.first())
6727                        .is_some_and(|v: &String| v == "15")
6728                        && s.elements
6729                            .get(2)
6730                            .and_then(|e: &Vec<String>| e.first())
6731                            .is_some_and(|v: &String| v == "Z21")
6732                });
6733                if has_cci {
6734                    let cavs =
6735                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6736                    if cavs.iter().any(|s| {
6737                        s.elements
6738                            .first()
6739                            .and_then(|e: &Vec<String>| e.first())
6740                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6741                    }) {
6742                        return ConditionResult::True;
6743                    }
6744                }
6745            }
6746        }
6747        ConditionResult::False
6748    }
6749
6750    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6751    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6752        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6753        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6754        let found = cci_segs.iter().any(|s| {
6755            s.elements
6756                .first()
6757                .and_then(|e| e.first())
6758                .is_some_and(|v| v == "Z37")
6759                && s.elements
6760                    .get(2)
6761                    .and_then(|e| e.first())
6762                    .is_some_and(|v| v == "ZD1")
6763        });
6764        ConditionResult::from(found)
6765    }
6766
6767    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6768    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6769        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6770        let seq_count = ctx
6771            .find_segments("SEQ")
6772            .iter()
6773            .filter(|s| {
6774                s.elements
6775                    .first()
6776                    .and_then(|e| e.first())
6777                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6778            })
6779            .count();
6780        ConditionResult::from(seq_count > 1)
6781    }
6782
6783    /// [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...
6784    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6785        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6786        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6787        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6788        let found = pia_segs.iter().any(|s| {
6789            s.elements
6790                .first()
6791                .and_then(|e| e.first())
6792                .is_some_and(|v| v == "5")
6793                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6794                    v.starts_with("1-")
6795                        && (v.contains(":1.8.")
6796                            || v.contains(":2.8.")
6797                            || v.contains(":1.9.")
6798                            || v.contains(":2.9."))
6799                })
6800        });
6801        ConditionResult::from(found)
6802    }
6803
6804    /// [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...
6805    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6806        // Check: at least one RFF+Z49 (Zeitraum) exists
6807        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6808        ConditionResult::from(found)
6809    }
6810
6811    /// [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...
6812    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6813        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6814        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6815        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6816        let found = pia_segs.iter().any(|s| {
6817            s.elements
6818                .first()
6819                .and_then(|e| e.first())
6820                .is_some_and(|v| v == "5")
6821                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6822                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6823                })
6824        });
6825        ConditionResult::from(found)
6826    }
6827
6828    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6829    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6830        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6831        let pia_segs = ctx.find_segments("PIA");
6832        let found = pia_segs.iter().any(|s| {
6833            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6834                let parts: Vec<&str> = v.split('-').collect();
6835                parts.len() == 3
6836                    && parts[0].len() == 1
6837                    && parts[0].chars().all(|c| c.is_ascii_digit())
6838                    && parts[1].len() == 2
6839                    && parts[1].chars().all(|c| c.is_ascii_digit())
6840                    && parts[2].len() == 1
6841                    && parts[2].chars().all(|c| c.is_ascii_digit())
6842            })
6843        });
6844        ConditionResult::from(found)
6845    }
6846
6847    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6848    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6849        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6850        let pia_segs = ctx.find_segments("PIA");
6851        let found = pia_segs.iter().any(|s| {
6852            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6853                let parts: Vec<&str> = v.split('-').collect();
6854                parts.len() == 4
6855                    && parts[0].len() == 1
6856                    && parts[0].chars().all(|c| c.is_ascii_digit())
6857                    && parts[1].len() == 2
6858                    && parts[1].chars().all(|c| c.is_ascii_digit())
6859                    && parts[2].len() == 1
6860                    && parts[2].chars().all(|c| c.is_ascii_digit())
6861                    && parts[3].len() == 3
6862                    && parts[3].chars().all(|c| c.is_ascii_digit())
6863            })
6864        });
6865        ConditionResult::from(found)
6866    }
6867
6868    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6869    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6870        // Check: PIA code in format n1-n2-n1-n8-n2
6871        let pia_segs = ctx.find_segments("PIA");
6872        let found = pia_segs.iter().any(|s| {
6873            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6874                let parts: Vec<&str> = v.split('-').collect();
6875                parts.len() == 5
6876                    && parts[0].len() == 1
6877                    && parts[0].chars().all(|c| c.is_ascii_digit())
6878                    && parts[1].len() == 2
6879                    && parts[1].chars().all(|c| c.is_ascii_digit())
6880                    && parts[2].len() == 1
6881                    && parts[2].chars().all(|c| c.is_ascii_digit())
6882                    && parts[3].len() == 8
6883                    && parts[3].chars().all(|c| c.is_ascii_digit())
6884                    && parts[4].len() == 2
6885                    && parts[4].chars().all(|c| c.is_ascii_digit())
6886            })
6887        });
6888        ConditionResult::from(found)
6889    }
6890
6891    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6892    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6893        // Check: PIA code in format n1-n2-n1-n8
6894        let pia_segs = ctx.find_segments("PIA");
6895        let found = pia_segs.iter().any(|s| {
6896            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6897                let parts: Vec<&str> = v.split('-').collect();
6898                parts.len() == 4
6899                    && parts[0].len() == 1
6900                    && parts[0].chars().all(|c| c.is_ascii_digit())
6901                    && parts[1].len() == 2
6902                    && parts[1].chars().all(|c| c.is_ascii_digit())
6903                    && parts[2].len() == 1
6904                    && parts[2].chars().all(|c| c.is_ascii_digit())
6905                    && parts[3].len() == 8
6906                    && parts[3].chars().all(|c| c.is_ascii_digit())
6907            })
6908        });
6909        ConditionResult::from(found)
6910    }
6911
6912    /// [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
6913    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6914        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6915        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6916        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6917        let found = pia_segs.iter().any(|s| {
6918            s.elements
6919                .first()
6920                .and_then(|e| e.first())
6921                .is_some_and(|v| v == "5")
6922                && s.elements
6923                    .get(1)
6924                    .and_then(|e| e.first())
6925                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6926        });
6927        ConditionResult::from(found)
6928    }
6929
6930    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6931    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6932        let pia_segs = ctx.find_segments("PIA");
6933        let found = pia_segs.iter().any(|s| {
6934            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6935                let prefixes = ["1-08-1", "1-08-4"];
6936                prefixes.iter().any(|p| v.starts_with(p))
6937            })
6938        });
6939        ConditionResult::from(found)
6940    }
6941
6942    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6943    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6944        let pia_segs = ctx.find_segments("PIA");
6945        let found = pia_segs.iter().any(|s| {
6946            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6947                let prefixes = ["1-08-2", "1-08-5"];
6948                prefixes.iter().any(|p| v.starts_with(p))
6949            })
6950        });
6951        ConditionResult::from(found)
6952    }
6953
6954    /// [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...
6955    // 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)
6956    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6957        let nav = match ctx.navigator() {
6958            Some(n) => n,
6959            None => return ConditionResult::Unknown,
6960        };
6961        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6962        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6963        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6964        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6965        for i in 0..sg5_count {
6966            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6967            for loc in &locs {
6968                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6969                    if !zid.is_empty() {
6970                        loc_zeitraum_ids.push(zid.clone());
6971                    }
6972                }
6973            }
6974        }
6975        if loc_zeitraum_ids.is_empty() {
6976            return ConditionResult::Unknown;
6977        }
6978        let pauschal_id = "9992000000018";
6979        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6980        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6981        // and that has RFF+Z31 with DE1154 != pauschal_id
6982        for i in 0..sg8_count {
6983            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6984            for seq in &seqs {
6985                if !seq
6986                    .elements
6987                    .first()
6988                    .and_then(|e| e.first())
6989                    .is_some_and(|v| v == "Z78")
6990                {
6991                    continue;
6992                }
6993                let zid = seq
6994                    .elements
6995                    .get(1)
6996                    .and_then(|e| e.first())
6997                    .map(|s| s.as_str())
6998                    .unwrap_or("");
6999                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
7000                    continue;
7001                }
7002                // Matching Z78 SG8 found — check RFF+Z31 DE1154
7003                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
7004                for rff in &rffs {
7005                    let qual = rff
7006                        .elements
7007                        .first()
7008                        .and_then(|e| e.first())
7009                        .map(|v| v.as_str())
7010                        .unwrap_or("");
7011                    if qual == "Z31" {
7012                        let id = rff
7013                            .elements
7014                            .first()
7015                            .and_then(|e| e.get(1))
7016                            .map(|v| v.as_str())
7017                            .unwrap_or("");
7018                        let normalized = id.replace(' ', "");
7019                        if !normalized.is_empty() && normalized != pauschal_id {
7020                            return ConditionResult::True;
7021                        }
7022                    }
7023                }
7024            }
7025        }
7026        ConditionResult::False
7027    }
7028
7029    /// [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+...
7030    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
7031        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
7032        // Approximate: check if RFF+Z49 or Z53 exists
7033        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
7034        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
7035        ConditionResult::from(!has_z49 && !has_z53)
7036    }
7037
7038    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
7039    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
7040        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
7041        // Check: OBIS Zählerstand code in PIA+5
7042        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7043        let found = pia_segs.iter().any(|s| {
7044            s.elements
7045                .first()
7046                .and_then(|e| e.first())
7047                .is_some_and(|v| v == "5")
7048                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7049                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
7050                })
7051        });
7052        ConditionResult::from(found)
7053    }
7054
7055    /// [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
7056    // 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)
7057    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
7058        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
7059            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
7060                Some(id) if !id.is_empty() => id.clone(),
7061                _ => return ConditionResult::Unknown,
7062            },
7063            None => return ConditionResult::Unknown,
7064        };
7065        let nav = match ctx.navigator {
7066            Some(n) => n,
7067            None => return ConditionResult::Unknown,
7068        };
7069        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7070        for i in 0..sg8_count {
7071            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7072            for j in 0..sg10_count {
7073                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7074                let has_cci_zb3 = ccis.iter().any(|s| {
7075                    s.elements
7076                        .get(2)
7077                        .and_then(|e: &Vec<String>| e.first())
7078                        .is_some_and(|v: &String| v == "ZB3")
7079                });
7080                if !has_cci_zb3 {
7081                    continue;
7082                }
7083                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7084                for cav in &cavs {
7085                    if cav
7086                        .elements
7087                        .first()
7088                        .and_then(|e: &Vec<String>| e.first())
7089                        .is_some_and(|v: &String| v == "Z91")
7090                    {
7091                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
7092                            if !msb_id.is_empty() {
7093                                return ConditionResult::from(
7094                                    sender_mp_id.as_str() != msb_id.as_str(),
7095                                );
7096                            }
7097                        }
7098                    }
7099                }
7100            }
7101        }
7102        ConditionResult::Unknown
7103    }
7104
7105    /// [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
7106    // 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)
7107    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
7108        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
7109        let mr_mp_id = match mr_nads.first() {
7110            Some(nad) => nad
7111                .elements
7112                .get(1)
7113                .and_then(|e: &Vec<String>| e.first())
7114                .cloned()
7115                .unwrap_or_default(),
7116            None => return ConditionResult::Unknown,
7117        };
7118        if mr_mp_id.is_empty() {
7119            return ConditionResult::Unknown;
7120        }
7121        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
7122        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
7123        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
7124        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
7125        // otherwise, as before.
7126        if let Some(scope) = ctx.enclosing_scope("SG8") {
7127            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
7128            let names_recipient = |s: &[OwnedSegment]| {
7129                sg4_instances::has_cci(s, "", "ZB3")
7130                    && s.iter().any(|c| {
7131                        c.id == "CAV"
7132                            && c.get_element(0) == "Z91"
7133                            && c.get_component(0, 1) == mr_mp_id
7134                    })
7135            };
7136            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
7137                sg4_instances::seq_at(ctx, p)
7138                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
7139                    && (refs.is_empty()
7140                        || sg4_instances::rff_values_at(ctx, p, "Z18")
7141                            .iter()
7142                            .any(|v| refs.contains(v)))
7143                    && sg4_instances::sg10_at(ctx, p, names_recipient)
7144            });
7145            return ConditionResult::from(!same);
7146        }
7147
7148        let nav = match ctx.navigator {
7149            Some(n) => n,
7150            None => {
7151                let cavs = ctx.find_segments("CAV");
7152                let same_id_exists = cavs.iter().any(|s| {
7153                    s.elements
7154                        .first()
7155                        .and_then(|e: &Vec<String>| e.first())
7156                        .is_some_and(|v: &String| v == "Z91")
7157                        && s.elements
7158                            .first()
7159                            .and_then(|e| e.get(1))
7160                            .is_some_and(|v: &String| v == &mr_mp_id)
7161                });
7162                return ConditionResult::from(!same_id_exists);
7163            }
7164        };
7165
7166        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7167        for i in 0..sg8_count {
7168            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7169            for j in 0..sg10_count {
7170                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7171                let has_cci_zb3 = ccis.iter().any(|s| {
7172                    s.elements
7173                        .get(2)
7174                        .and_then(|e: &Vec<String>| e.first())
7175                        .is_some_and(|v: &String| v == "ZB3")
7176                });
7177                if !has_cci_zb3 {
7178                    continue;
7179                }
7180                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7181                for cav in &cavs {
7182                    if cav
7183                        .elements
7184                        .first()
7185                        .and_then(|e: &Vec<String>| e.first())
7186                        .is_some_and(|v: &String| v == "Z91")
7187                    {
7188                        let cav_mp_id = cav
7189                            .elements
7190                            .first()
7191                            .and_then(|e| e.get(1))
7192                            .map(|s| s.as_str())
7193                            .unwrap_or("");
7194                        if cav_mp_id == mr_mp_id.as_str() {
7195                            return ConditionResult::False;
7196                        }
7197                    }
7198                }
7199            }
7200        }
7201
7202        ConditionResult::True
7203    }
7204
7205    /// [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
7206    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7207        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7208        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7209        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7210        let found = pia_segs.iter().any(|s| {
7211            s.elements
7212                .first()
7213                .and_then(|e| e.first())
7214                .is_some_and(|v| v == "5")
7215                && s.elements
7216                    .get(1)
7217                    .and_then(|e| e.first())
7218                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7219        });
7220        ConditionResult::from(found)
7221    }
7222
7223    /// [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
7224    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7225        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7226        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7227        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7228        let found = pia_segs.iter().any(|s| {
7229            s.elements
7230                .first()
7231                .and_then(|e| e.first())
7232                .is_some_and(|v| v == "5")
7233                && s.elements
7234                    .get(1)
7235                    .and_then(|e| e.first())
7236                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7237        });
7238        ConditionResult::from(found)
7239    }
7240
7241    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7242    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7243        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7244        // Check: OBIS Blindarbeit code in PIA+5
7245        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7246        let found = pia_segs.iter().any(|s| {
7247            s.elements
7248                .first()
7249                .and_then(|e| e.first())
7250                .is_some_and(|v| v == "5")
7251                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7252                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7253                })
7254        });
7255        ConditionResult::from(found)
7256    }
7257
7258    /// [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...
7259    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7260        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7261        // reaches an SG8; the message as before otherwise.
7262        if let Some(scope) = ctx.enclosing_scope("SG8") {
7263            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7264                ctx,
7265                scope,
7266                is_obis_wirkarbeit_kumuliert,
7267            ));
7268        }
7269        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7270        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7271        ConditionResult::from(has_seq_z02)
7272    }
7273
7274    /// [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 ...
7275    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7276        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7277        // reaches an SG8; the message as before otherwise.
7278        if let Some(scope) = ctx.enclosing_scope("SG8") {
7279            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7280                ctx,
7281                scope,
7282                is_obis_wirkarbeit_kumuliert,
7283            ));
7284        }
7285        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7286        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7287        ConditionResult::from(!has_seq_z02)
7288    }
7289
7290    /// [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...
7291    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7292        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7293        // reaches an SG8; the message as before otherwise.
7294        if let Some(scope) = ctx.enclosing_scope("SG8") {
7295            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7296                ctx,
7297                scope,
7298                is_obis_wirkarbeit_quarter_hour,
7299            ));
7300        }
7301        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7302        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7303        ConditionResult::from(has_seq_z02)
7304    }
7305
7306    /// [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 ...
7307    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7308        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7309        // reaches an SG8; the message as before otherwise.
7310        if let Some(scope) = ctx.enclosing_scope("SG8") {
7311            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7312                ctx,
7313                scope,
7314                is_obis_wirkarbeit_quarter_hour,
7315            ));
7316        }
7317        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7318        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7319        ConditionResult::from(!has_seq_z02)
7320    }
7321
7322    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7323    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7324        ctx.external.evaluate("code_list_membership_check")
7325    }
7326
7327    /// [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
7328    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7329        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7330        let found = ctx.find_segments("SEQ").iter().any(|s| {
7331            s.elements
7332                .first()
7333                .and_then(|e| e.first())
7334                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7335        });
7336        ConditionResult::from(found)
7337    }
7338
7339    /// [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
7340    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7341        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7342        let found = ctx.find_segments("SEQ").iter().any(|s| {
7343            s.elements
7344                .first()
7345                .and_then(|e| e.first())
7346                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7347        });
7348        ConditionResult::from(!found)
7349    }
7350
7351    /// [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
7352    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7353        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7354        let count = ctx
7355            .find_segments("SEQ")
7356            .iter()
7357            .filter(|s| {
7358                s.elements
7359                    .first()
7360                    .and_then(|e| e.first())
7361                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7362            })
7363            .count();
7364        ConditionResult::from(count > 1)
7365    }
7366
7367    /// [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
7368    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7369        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7370        let count = ctx
7371            .find_segments("SEQ")
7372            .iter()
7373            .filter(|s| {
7374                s.elements
7375                    .first()
7376                    .and_then(|e| e.first())
7377                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7378            })
7379            .count();
7380        ConditionResult::from(count <= 1)
7381    }
7382
7383    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7384    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7385        // TODO: implement
7386        ConditionResult::Unknown
7387    }
7388
7389    /// [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
7390    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7391        // TODO: implement
7392        ConditionResult::Unknown
7393    }
7394
7395    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7396    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7397        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7398        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7399        let found = rff_segs.iter().any(|s| {
7400            s.elements
7401                .first()
7402                .and_then(|e| e.first())
7403                .is_some_and(|v| v == "Z14")
7404        });
7405        ConditionResult::from(found)
7406    }
7407
7408    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7409    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7410        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7411        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7412        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7413    }
7414
7415    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7416    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7417        ctx.external.evaluate("code_list_membership_check")
7418    }
7419
7420    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7421    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7422        ctx.external.evaluate("code_list_membership_check")
7423    }
7424
7425    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7426    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7427        ctx.external.evaluate("code_list_membership_check")
7428    }
7429
7430    /// [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...
7431    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7432        ctx.external.evaluate("code_list_membership_check")
7433    }
7434
7435    /// [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...
7436    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7437        ctx.external.evaluate("code_list_membership_check")
7438    }
7439
7440    /// [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...
7441    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7442        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7443        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7444        ConditionResult::from(seq_z27_count > 1)
7445    }
7446
7447    /// [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+...
7448    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7449        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7450        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7451        ConditionResult::from(seq_z27_count <= 1)
7452    }
7453
7454    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7455    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7456        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7457        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7458        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7459        let found = pia_segs.iter().any(|s| {
7460            s.elements
7461                .first()
7462                .and_then(|e| e.first())
7463                .is_some_and(|v| v == "5")
7464                && s.elements
7465                    .get(1)
7466                    .and_then(|e| e.first())
7467                    .is_some_and(|v| v == "9991000000078")
7468        });
7469        ConditionResult::from(found)
7470    }
7471
7472    /// [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 ...
7473    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7474        ctx.external.evaluate("code_list_membership_check")
7475    }
7476
7477    /// [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...
7478    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7479        ctx.external.evaluate("code_list_membership_check")
7480    }
7481
7482    /// [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
7483    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7484        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7485        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7486        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7487        let found = pia_segs.iter().any(|s| {
7488            s.elements
7489                .first()
7490                .and_then(|e| e.first())
7491                .is_some_and(|v| v == "5")
7492                && s.elements
7493                    .get(1)
7494                    .and_then(|e| e.first())
7495                    .is_some_and(|v| v == "9991000000086")
7496        });
7497        ConditionResult::from(found)
7498    }
7499
7500    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7501    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7502        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7503        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7504        let found = sts_segs.iter().any(|s| {
7505            s.elements
7506                .first()
7507                .and_then(|e| e.first())
7508                .is_some_and(|v| v == "E01")
7509                && s.elements
7510                    .get(2)
7511                    .and_then(|e| e.first())
7512                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7513        });
7514        ConditionResult::from(found)
7515    }
7516
7517    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7518    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7519        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7520        // in tree validation), else the enclosing SG10's CCI.
7521        let is_z39 = |elements: &[Vec<String>]| {
7522            elements
7523                .first()
7524                .and_then(|e| e.first())
7525                .is_some_and(|v| v == "Z39")
7526        };
7527        if let Some(own) = ctx.resolved_segment.filter(|_| {
7528            ctx.scoped_find_segments_in("SG10", "CCI")
7529                .iter()
7530                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7531        }) {
7532            return ConditionResult::from(is_z39(own));
7533        }
7534        ConditionResult::from(
7535            ctx.scoped_find_segments_in("SG10", "CCI")
7536                .iter()
7537                .any(|s| is_z39(&s.elements)),
7538        )
7539    }
7540
7541    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7542    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7543        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7544        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7545        // alternative of the `⊻` held and the XOR failed on each RFF).
7546        same_rff_qualifier(ctx, "Z35")
7547    }
7548
7549    /// [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
7550    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7551        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
7552        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
7553    }
7554
7555    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7556    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7557        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7558        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7559        // alternative of the `⊻` held and the XOR failed on each RFF).
7560        same_rff_qualifier(ctx, "Z34")
7561    }
7562
7563    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7564    // 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)
7565    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7566        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7567        // scope reaches one; the message as before otherwise.
7568        if let Some(scope) = ctx.enclosing_scope("SG8") {
7569            return ConditionResult::from(
7570                ctx.find_segments_at(scope, "SEQ")
7571                    .iter()
7572                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7573                    && !ctx
7574                        .find_segments_at(scope, "PIA")
7575                        .iter()
7576                        .any(|s| s.get_element(0) == "5"),
7577            );
7578        }
7579        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7580            if matches!(
7581                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7582                ConditionResult::True
7583            ) {
7584                return ConditionResult::True;
7585            }
7586        }
7587        ConditionResult::False
7588    }
7589
7590    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7591    // 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)
7592    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7593        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7594        // scope reaches one; the message as before otherwise.
7595        if let Some(scope) = ctx.enclosing_scope("SG8") {
7596            return ConditionResult::from(
7597                ctx.find_segments_at(scope, "SEQ")
7598                    .iter()
7599                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7600                    && !ctx
7601                        .find_segments_at(scope, "CCI")
7602                        .iter()
7603                        .any(|s| s.get_element(0) == "11"),
7604            );
7605        }
7606        let nav = match ctx.navigator {
7607            Some(n) => n,
7608            None => {
7609                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7610                    matches!(
7611                        ctx.filtered_parent_child_has_qualifier(
7612                            &["SG4", "SG8"],
7613                            "SEQ",
7614                            0,
7615                            code,
7616                            "SG10",
7617                            "CCI",
7618                            0,
7619                            "11",
7620                        ),
7621                        ConditionResult::True
7622                    )
7623                });
7624                return ConditionResult::from(!has_cci11);
7625            }
7626        };
7627
7628        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7629        for i in 0..sg8_count {
7630            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7631            let has_cci11 = (0..sg10_count).any(|j| {
7632                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7633                ccis.iter().any(|s| {
7634                    s.elements
7635                        .first()
7636                        .and_then(|e: &Vec<String>| e.first())
7637                        .is_some_and(|v: &String| v == "11")
7638                })
7639            });
7640            if !has_cci11 {
7641                return ConditionResult::True;
7642            }
7643        }
7644        ConditionResult::False
7645    }
7646
7647    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7648    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7649        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7650        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7651        let found = cci_segs.iter().any(|s| {
7652            s.elements
7653                .first()
7654                .and_then(|e| e.first())
7655                .is_some_and(|v| v == "Z17")
7656        });
7657        ConditionResult::from(found)
7658    }
7659
7660    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7661    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7662        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7663        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7664        let found = cci_segs.iter().any(|s| {
7665            s.elements
7666                .first()
7667                .and_then(|e| e.first())
7668                .is_some_and(|v| v == "Z50")
7669        });
7670        ConditionResult::from(found)
7671    }
7672
7673    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7674    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7675        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7676        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7677        let found = cci_segs.iter().any(|s| {
7678            s.elements
7679                .first()
7680                .and_then(|e| e.first())
7681                .is_some_and(|v| v == "Z56")
7682        });
7683        ConditionResult::from(found)
7684    }
7685
7686    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7687    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7688        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7689        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7690        let has_cci = cci_segs.iter().any(|s| {
7691            s.elements
7692                .first()
7693                .and_then(|e| e.first())
7694                .is_some_and(|v| v == "6")
7695                && s.elements
7696                    .get(2)
7697                    .and_then(|e| e.first())
7698                    .is_some_and(|v| v == "ZA9")
7699        });
7700        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7701        let has_cav = cav_segs.iter().any(|s| {
7702            s.elements
7703                .first()
7704                .and_then(|e| e.first())
7705                .is_some_and(|v| v == "ZG3")
7706        });
7707        ConditionResult::from(has_cci && has_cav)
7708    }
7709
7710    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7711    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7712        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7713        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7714        // alternative of the `⊻` held and the XOR failed on each RFF).
7715        same_rff_qualifier(ctx, "Z32")
7716    }
7717
7718    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7719    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7720        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7721        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7722        // alternative of the `⊻` held and the XOR failed on each RFF).
7723        same_rff_qualifier(ctx, "Z18")
7724    }
7725
7726    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7727    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7728        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7729        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7730        // alternative of the `⊻` held and the XOR failed on each RFF).
7731        same_rff_qualifier(ctx, "Z19")
7732    }
7733
7734    /// [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...
7735    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7736        ctx.external.evaluate("code_list_membership_check")
7737    }
7738
7739    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7740    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7741        // Check: begin date < end date (structural — always true for valid messages)
7742        ConditionResult::True
7743    }
7744
7745    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7746    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7747        // Check: end date > begin date (structural — always true for valid messages)
7748        ConditionResult::True
7749    }
7750
7751    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7752    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7753        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7754        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7755        // alternative of the `⊻` held and the XOR failed on each RFF).
7756        same_rff_qualifier(ctx, "Z37")
7757    }
7758
7759    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7760    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7761        // Check: PIA+5 with Messprodukt 9991000001498 exists
7762        let pia_segs = ctx.find_segments("PIA");
7763        let found = pia_segs.iter().any(|s| {
7764            s.elements
7765                .first()
7766                .and_then(|e| e.first())
7767                .is_some_and(|v| v == "5")
7768                && s.elements
7769                    .get(1)
7770                    .and_then(|e| e.first())
7771                    .is_some_and(|v| v == "9991000001498")
7772        });
7773        ConditionResult::from(found)
7774    }
7775
7776    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7777    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7778        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7779        let pia_segs = ctx.find_segments("PIA");
7780        let found = pia_segs.iter().any(|s| {
7781            s.elements
7782                .first()
7783                .and_then(|e| e.first())
7784                .is_some_and(|v| v == "5")
7785                && s.elements
7786                    .get(1)
7787                    .and_then(|e| e.first())
7788                    .is_some_and(|v| v == "9991000000739")
7789        });
7790        ConditionResult::from(found)
7791    }
7792
7793    /// [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
7794    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7795        ctx.external.evaluate("code_list_membership_check")
7796    }
7797
7798    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7799    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7800        ctx.external.evaluate("code_list_membership_check")
7801    }
7802
7803    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7804    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7805        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7806        // SG10, when the scope reaches one; of any one SG10 otherwise.
7807        this_sg10_has(ctx, |segs| {
7808            segs.iter()
7809                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7810                && sg4_instances::has_cav(segs, "ZG8")
7811        })
7812    }
7813
7814    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7815    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7816        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7817        // SG10, when the scope reaches one; of any one SG10 otherwise.
7818        this_sg10_has(ctx, |segs| {
7819            segs.iter()
7820                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7821                && sg4_instances::has_cav(segs, "ZH1")
7822        })
7823    }
7824
7825    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7826    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7827        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7828        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7829        let found = cci_segs.iter().any(|s| {
7830            s.elements
7831                .first()
7832                .and_then(|e| e.first())
7833                .is_some_and(|v| v == "Z61")
7834                && s.elements
7835                    .get(2)
7836                    .and_then(|e| e.first())
7837                    .is_some_and(|v| v == "ZF9")
7838        });
7839        ConditionResult::from(found)
7840    }
7841
7842    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7843    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7844        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7845        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7846        let found = rff_segs.iter().any(|s| {
7847            s.elements
7848                .first()
7849                .and_then(|e| e.first())
7850                .is_some_and(|v| v == "Z38")
7851        });
7852        ConditionResult::from(found)
7853    }
7854
7855    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7856    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7857        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7858        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7859        let found = rff_segs.iter().any(|s| {
7860            s.elements
7861                .first()
7862                .and_then(|e| e.first())
7863                .is_some_and(|v| v == "Z32")
7864        });
7865        ConditionResult::from(found)
7866    }
7867
7868    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7869    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7870        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7871        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7872        let found = segs.iter().any(|s| {
7873            s.elements
7874                .first()
7875                .and_then(|e| e.first())
7876                .is_some_and(|v| v == "ZG8")
7877        });
7878        ConditionResult::from(found)
7879    }
7880
7881    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7882    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7883        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7884        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7885        let found = segs.iter().any(|s| {
7886            s.elements
7887                .first()
7888                .and_then(|e| e.first())
7889                .is_some_and(|v| v == "ZH0")
7890        });
7891        ConditionResult::from(found)
7892    }
7893
7894    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7895    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7896        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7897        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7898        let found = segs.iter().any(|s| {
7899            s.elements
7900                .first()
7901                .and_then(|e| e.first())
7902                .is_some_and(|v| v == "ZH1")
7903        });
7904        ConditionResult::from(found)
7905    }
7906
7907    /// [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
7908    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7909        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7910        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7911        let found = segs.iter().any(|s| {
7912            s.elements
7913                .first()
7914                .and_then(|e| e.first())
7915                .is_some_and(|v| v == "ZH5")
7916        });
7917        ConditionResult::from(found)
7918    }
7919
7920    /// [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/ ...
7921    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7922        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7923        let found = ctx.find_segments("RFF").iter().any(|s| {
7924            let qual = s
7925                .elements
7926                .first()
7927                .and_then(|e| e.first())
7928                .map(|v| v.as_str());
7929            matches!(qual, Some("Z49") | Some("Z53"))
7930                && s.elements
7931                    .first()
7932                    .and_then(|e| e.get(2))
7933                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7934        });
7935        ConditionResult::from(found)
7936    }
7937
7938    /// [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...
7939    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7940        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7941        let found = ctx.find_segments("RFF").iter().any(|s| {
7942            let qual = s
7943                .elements
7944                .first()
7945                .and_then(|e| e.first())
7946                .map(|v| v.as_str());
7947            matches!(qual, Some("Z48") | Some("Z55"))
7948                && s.elements
7949                    .first()
7950                    .and_then(|e| e.get(2))
7951                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7952        });
7953        ConditionResult::from(found)
7954    }
7955
7956    /// [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...
7957    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7958        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7959        let found = ctx.find_segments("RFF").iter().any(|s| {
7960            let qual = s
7961                .elements
7962                .first()
7963                .and_then(|e| e.first())
7964                .map(|v| v.as_str());
7965            matches!(qual, Some("Z47") | Some("Z54"))
7966                && s.elements
7967                    .first()
7968                    .and_then(|e| e.get(2))
7969                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7970        });
7971        ConditionResult::from(found)
7972    }
7973
7974    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7975    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7976        let sts_segs = ctx.find_segments("STS");
7977        let found = sts_segs.iter().any(|s| {
7978            s.elements
7979                .first()
7980                .and_then(|e| e.first())
7981                .is_some_and(|v| v == "7")
7982                && s.elements
7983                    .get(2)
7984                    .and_then(|e| e.first())
7985                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7986        });
7987        ConditionResult::from(found)
7988    }
7989
7990    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7991    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7992        let sts_segs = ctx.find_segments("STS");
7993        let found = sts_segs.iter().any(|s| {
7994            s.elements
7995                .first()
7996                .and_then(|e| e.first())
7997                .is_some_and(|v| v == "7")
7998                && s.elements
7999                    .get(2)
8000                    .and_then(|e| e.first())
8001                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
8002        });
8003        ConditionResult::from(found)
8004    }
8005
8006    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
8007    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
8008        let sts_segs = ctx.find_segments("STS");
8009        let found = sts_segs.iter().any(|s| {
8010            s.elements
8011                .first()
8012                .and_then(|e| e.first())
8013                .is_some_and(|v| v == "7")
8014                && s.elements
8015                    .get(3)
8016                    .and_then(|e| e.first())
8017                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
8018        });
8019        ConditionResult::from(found)
8020    }
8021
8022    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
8023    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
8024        let sts_segs = ctx.find_segments("STS");
8025        let found = sts_segs.iter().any(|s| {
8026            s.elements
8027                .first()
8028                .and_then(|e| e.first())
8029                .is_some_and(|v| v == "7")
8030                && s.elements
8031                    .get(3)
8032                    .and_then(|e| e.first())
8033                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
8034        });
8035        ConditionResult::from(found)
8036    }
8037
8038    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8039    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8040        let sts_segs = ctx.find_segments("STS");
8041        let found = sts_segs.iter().any(|s| {
8042            s.elements
8043                .first()
8044                .and_then(|e| e.first())
8045                .is_some_and(|v| v == "7")
8046                && s.elements
8047                    .get(3)
8048                    .and_then(|e| e.first())
8049                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8050        });
8051        ConditionResult::from(found)
8052    }
8053
8054    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8055    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8056        let sts_segs = ctx.find_segments("STS");
8057        let found = sts_segs.iter().any(|s| {
8058            s.elements
8059                .first()
8060                .and_then(|e| e.first())
8061                .is_some_and(|v| v == "7")
8062                && s.elements
8063                    .get(3)
8064                    .and_then(|e| e.first())
8065                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8066        });
8067        ConditionResult::from(found)
8068    }
8069
8070    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8071    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8072        let sts_segs = ctx.find_segments("STS");
8073        let found = sts_segs.iter().any(|s| {
8074            s.elements
8075                .first()
8076                .and_then(|e| e.first())
8077                .is_some_and(|v| v == "7")
8078                && s.elements
8079                    .get(3)
8080                    .and_then(|e| e.first())
8081                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8082        });
8083        ConditionResult::from(found)
8084    }
8085
8086    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8087    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8088        let sts_segs = ctx.find_segments("STS");
8089        let found = sts_segs.iter().any(|s| {
8090            s.elements
8091                .first()
8092                .and_then(|e| e.first())
8093                .is_some_and(|v| v == "7")
8094                && s.elements
8095                    .get(3)
8096                    .and_then(|e| e.first())
8097                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8098        });
8099        ConditionResult::from(found)
8100    }
8101
8102    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8103    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8104        let sts_segs = ctx.find_segments("STS");
8105        let found = sts_segs.iter().any(|s| {
8106            s.elements
8107                .first()
8108                .and_then(|e| e.first())
8109                .is_some_and(|v| v == "7")
8110                && s.elements
8111                    .get(3)
8112                    .and_then(|e| e.first())
8113                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8114        });
8115        ConditionResult::from(found)
8116    }
8117
8118    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8119    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8120        let sts_segs = ctx.find_segments("STS");
8121        let found = sts_segs.iter().any(|s| {
8122            s.elements
8123                .first()
8124                .and_then(|e| e.first())
8125                .is_some_and(|v| v == "7")
8126                && s.elements
8127                    .get(3)
8128                    .and_then(|e| e.first())
8129                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8130        });
8131        ConditionResult::from(found)
8132    }
8133
8134    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8135    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8136        let sts_segs = ctx.find_segments("STS");
8137        let found = sts_segs.iter().any(|s| {
8138            s.elements
8139                .first()
8140                .and_then(|e| e.first())
8141                .is_some_and(|v| v == "7")
8142                && s.elements
8143                    .get(3)
8144                    .and_then(|e| e.first())
8145                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8146        });
8147        ConditionResult::from(found)
8148    }
8149
8150    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8151    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8152        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8153        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8154        if rff_z49.is_empty() {
8155            return ConditionResult::Unknown;
8156        }
8157        // Approximate: condition applies when RFF+Z49 exists
8158        ConditionResult::True
8159    }
8160
8161    /// [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
8162    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8163        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8164    }
8165
8166    /// [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
8167    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8168        ctx.format_check("DTM", 0, 1, is_mez_utc)
8169    }
8170
8171    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8172    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8173        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8174        // so lexicographic comparison works directly.
8175        let val = ctx.resolved_value.or_else(|| {
8176            ctx.find_segments_with_qualifier("DTM", 0, "137")
8177                .first()
8178                .and_then(|s| s.elements.first())
8179                .and_then(|e| e.get(1))
8180                .map(|v| v.as_str())
8181        });
8182        let Some(dtm_val) = val else {
8183            return ConditionResult::Unknown;
8184        };
8185        if dtm_val.len() < 12 {
8186            return ConditionResult::Unknown;
8187        }
8188        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8189        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8190    }
8191
8192    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8193    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8194        ConditionResult::True
8195    }
8196
8197    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8198    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8199        ConditionResult::True
8200    }
8201
8202    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8203    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8204        ConditionResult::True
8205    }
8206
8207    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8208    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8209        ConditionResult::True
8210    }
8211
8212    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8213    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8214        ConditionResult::True
8215    }
8216
8217    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8218    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8219        ConditionResult::True
8220    }
8221
8222    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8223    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8224        ConditionResult::True
8225    }
8226
8227    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8228    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8229        ConditionResult::True
8230    }
8231
8232    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8233    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8234        ConditionResult::True
8235    }
8236
8237    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8238    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8239        ConditionResult::True
8240    }
8241
8242    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8243    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8244        ConditionResult::True
8245    }
8246
8247    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8248    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8249        ConditionResult::True
8250    }
8251
8252    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8253    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8254        ConditionResult::True
8255    }
8256
8257    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8258    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8259        ConditionResult::True
8260    }
8261
8262    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8263    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8264        ConditionResult::True
8265    }
8266
8267    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8268    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8269        ConditionResult::True
8270    }
8271
8272    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8273    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8274        ConditionResult::True
8275    }
8276
8277    /// [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
8278    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8279        ConditionResult::True
8280    }
8281
8282    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8283    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8284        ConditionResult::True
8285    }
8286
8287    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8288    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8289        ConditionResult::True
8290    }
8291
8292    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8293    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8294        ConditionResult::True
8295    }
8296
8297    /// [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...
8298    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8299        ConditionResult::True
8300    }
8301
8302    /// [522] Hinweis: Es ist der NBN anzugeben
8303    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8304        ConditionResult::True
8305    }
8306
8307    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8308    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8309        ConditionResult::True
8310    }
8311
8312    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8313    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8314        ConditionResult::True
8315    }
8316
8317    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8318    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8319        ConditionResult::True
8320    }
8321
8322    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8323    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8324        ConditionResult::True
8325    }
8326
8327    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8328    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8329        ConditionResult::True
8330    }
8331
8332    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8333    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8334        ConditionResult::True
8335    }
8336
8337    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8338    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8339        ConditionResult::True
8340    }
8341
8342    /// [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, ...
8343    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8344        ConditionResult::True
8345    }
8346
8347    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8348    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8349        ConditionResult::True
8350    }
8351
8352    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8353    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8354        ConditionResult::True
8355    }
8356
8357    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8358    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8359        ConditionResult::True
8360    }
8361
8362    /// [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...
8363    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8364        // TODO: implement
8365        ConditionResult::Unknown
8366    }
8367
8368    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8369    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8370        ConditionResult::True
8371    }
8372
8373    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8374    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8375        ConditionResult::True
8376    }
8377
8378    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8379    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8380        ConditionResult::True
8381    }
8382
8383    /// [563] Hinweis: Für die ID der LieferantensummenZR
8384    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8385        ConditionResult::True
8386    }
8387
8388    /// [566] Hinweis: Altlieferant
8389    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8390        ConditionResult::True
8391    }
8392
8393    /// [567] Hinweis: Neulieferant
8394    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8395        ConditionResult::True
8396    }
8397
8398    /// [568] Hinweis: Lieferant der LieferantensummenZR
8399    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8400        ConditionResult::True
8401    }
8402
8403    /// [569] Hinweis: Dritter Nutzer
8404    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8405        ConditionResult::True
8406    }
8407
8408    /// [570] Hinweis: Netzbetreiber Alt
8409    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8410        ConditionResult::True
8411    }
8412
8413    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8414    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8415        ConditionResult::True
8416    }
8417
8418    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8419    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8420        ConditionResult::True
8421    }
8422
8423    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8424    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8425        ConditionResult::True
8426    }
8427
8428    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8429    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8430        ConditionResult::True
8431    }
8432
8433    /// [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...
8434    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8435        ConditionResult::True
8436    }
8437
8438    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8439    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8440        ConditionResult::True
8441    }
8442
8443    /// [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...
8444    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8445        ConditionResult::True
8446    }
8447
8448    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8449    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8450        ConditionResult::True
8451    }
8452
8453    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8454    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8455        ConditionResult::True
8456    }
8457
8458    /// [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...
8459    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8460        ConditionResult::True
8461    }
8462
8463    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8464    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8465        ConditionResult::True
8466    }
8467
8468    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8469    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8470        ConditionResult::True
8471    }
8472
8473    /// [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
8474    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8475        ConditionResult::True
8476    }
8477
8478    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8479    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8480        ConditionResult::True
8481    }
8482
8483    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8484    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8485        ConditionResult::True
8486    }
8487
8488    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8489    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8490        ConditionResult::True
8491    }
8492
8493    /// [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
8494    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8495        ConditionResult::True
8496    }
8497
8498    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8499    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8500        ConditionResult::True
8501    }
8502
8503    /// [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.
8504    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8505        ConditionResult::True
8506    }
8507
8508    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8509    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8510        ConditionResult::True
8511    }
8512
8513    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8514    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8515        ConditionResult::True
8516    }
8517
8518    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8519    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8520        ConditionResult::True
8521    }
8522
8523    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8524    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8525        ConditionResult::True
8526    }
8527
8528    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8529    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8530        ConditionResult::True
8531    }
8532
8533    /// [637] Hinweis: Bei Verpflichtungsanfrage
8534    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8535        ConditionResult::True
8536    }
8537
8538    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8539    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8540        ConditionResult::True
8541    }
8542
8543    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8544    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8545        ConditionResult::True
8546    }
8547
8548    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8549    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8550        ConditionResult::True
8551    }
8552
8553    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8554    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8555        ConditionResult::True
8556    }
8557
8558    /// [642] Hinweis: Wenn Abo vorliegt
8559    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8560        ConditionResult::True
8561    }
8562
8563    /// [643] Hinweis: Nachfolgender Netzbetreiber
8564    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8565        ConditionResult::True
8566    }
8567
8568    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8569    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8570        ConditionResult::True
8571    }
8572
8573    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8574    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8575        ConditionResult::True
8576    }
8577
8578    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8579    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8580        ConditionResult::True
8581    }
8582
8583    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8584    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8585        ConditionResult::True
8586    }
8587
8588    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8589    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8590        ConditionResult::True
8591    }
8592
8593    /// [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...
8594    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8595        ConditionResult::True
8596    }
8597
8598    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8599    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8600        ConditionResult::True
8601    }
8602
8603    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8604    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8605        ConditionResult::True
8606    }
8607
8608    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8609    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8610        ConditionResult::True
8611    }
8612
8613    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8614    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8615        ConditionResult::True
8616    }
8617
8618    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8619    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8620        ConditionResult::True
8621    }
8622
8623    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8624    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8625        ConditionResult::True
8626    }
8627
8628    /// [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
8629    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8630        ConditionResult::True
8631    }
8632
8633    /// [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...
8634    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8635        ConditionResult::True
8636    }
8637
8638    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8639    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8640        ConditionResult::True
8641    }
8642
8643    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8644    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8645        ConditionResult::True
8646    }
8647
8648    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8649    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8650        ConditionResult::True
8651    }
8652
8653    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8654    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8655        ConditionResult::True
8656    }
8657
8658    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8659    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8660        ConditionResult::True
8661    }
8662
8663    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8664    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8665        ConditionResult::True
8666    }
8667
8668    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8669    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8670        ConditionResult::True
8671    }
8672
8673    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8674    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8675        ConditionResult::True
8676    }
8677
8678    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8679    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8680        ConditionResult::True
8681    }
8682
8683    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8684    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8685        ConditionResult::True
8686    }
8687
8688    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8689    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8690        ConditionResult::True
8691    }
8692
8693    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8694    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8695        ConditionResult::True
8696    }
8697
8698    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8699    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8700        ConditionResult::True
8701    }
8702
8703    /// [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
8704    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8705        ConditionResult::True
8706    }
8707
8708    /// [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.
8709    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8710        ConditionResult::True
8711    }
8712
8713    /// [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
8714    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8715        ConditionResult::True
8716    }
8717
8718    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8719    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8720        ConditionResult::True
8721    }
8722
8723    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8724    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8725        ConditionResult::True
8726    }
8727
8728    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8729    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8730        ConditionResult::True
8731    }
8732
8733    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8734    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8735        ConditionResult::True
8736    }
8737
8738    /// [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...
8739    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8740        ConditionResult::True
8741    }
8742
8743    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8744    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8745        ConditionResult::True
8746    }
8747
8748    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8749    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8750        ConditionResult::True
8751    }
8752
8753    /// [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...
8754    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8755        ConditionResult::True
8756    }
8757
8758    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8759    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8760        ConditionResult::True
8761    }
8762
8763    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8764    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8765        ConditionResult::True
8766    }
8767
8768    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8769    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8770        ConditionResult::True
8771    }
8772
8773    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8774    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8775        ConditionResult::True
8776    }
8777
8778    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8779    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8780        ctx.external.evaluate("data_clearing_required")
8781    }
8782
8783    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8784    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8785        ConditionResult::True
8786    }
8787
8788    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8789    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8790        ConditionResult::True
8791    }
8792
8793    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8794    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8795        ConditionResult::True
8796    }
8797
8798    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8799    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8800        ConditionResult::True
8801    }
8802
8803    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8804    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8805        ConditionResult::True
8806    }
8807
8808    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8809    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8810        ConditionResult::True
8811    }
8812
8813    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8814    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8815        ConditionResult::True
8816    }
8817
8818    /// [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...
8819    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8820        ConditionResult::True
8821    }
8822
8823    /// [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...
8824    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8825        ConditionResult::True
8826    }
8827
8828    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8829    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8830        ConditionResult::True
8831    }
8832
8833    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8834    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8835        ctx.external.evaluate("data_clearing_required")
8836    }
8837
8838    /// [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
8839    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8840        ConditionResult::True
8841    }
8842
8843    /// [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...
8844    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8845        ConditionResult::True
8846    }
8847
8848    /// [902] Format: Möglicher Wert: ≥ 0
8849    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8850        // Format condition — informational, always applies
8851        ConditionResult::True
8852    }
8853
8854    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8855    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8856        // Format condition — informational, always applies
8857        ConditionResult::True
8858    }
8859
8860    /// [914] Format: Möglicher Wert: > 0
8861    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8862        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8863    }
8864
8865    /// [922] Format: TR-ID
8866    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8867        // Format condition — informational, always applies
8868        ConditionResult::True
8869    }
8870
8871    /// [926] Format: Möglicher Wert: 0
8872    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8873        // Format condition — informational, always applies
8874        ConditionResult::True
8875    }
8876
8877    /// [930] Format: max. 2 Nachkommastellen
8878    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8879        // Format condition — informational, always applies
8880        ConditionResult::True
8881    }
8882
8883    /// [931] Format: ZZZ = +00
8884    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8885        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8886    }
8887
8888    /// [932] Format: HHMM = 2200
8889    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8890        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8891    }
8892
8893    /// [933] Format: HHMM = 2300
8894    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8895        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8896    }
8897
8898    /// [937] Format: keine Nachkommastelle
8899    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8900        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8901    }
8902
8903    /// [938] Format: Möglicher Wert: <= 10
8904    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8905        ConditionResult::True
8906    }
8907
8908    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8909    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8910        ctx.format_check("COM", 0, 0, validate_email)
8911    }
8912
8913    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8914    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8915        ctx.format_check("COM", 0, 0, validate_phone)
8916    }
8917
8918    /// [942] Format: n1-n2-n1-n3
8919    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8920        // Format condition — informational, always applies
8921        ConditionResult::True
8922    }
8923
8924    /// [943] Format: n1-n2-n1
8925    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8926        // Format condition — informational, always applies
8927        ConditionResult::True
8928    }
8929
8930    /// [946] Format: max. 11 Nachkommastellen
8931    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8932        // Format condition — informational, always applies
8933        ConditionResult::True
8934    }
8935
8936    /// [948] Format: n1-n2-n1-n8-n2
8937    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8938        // Format condition — informational, always applies
8939        ConditionResult::True
8940    }
8941
8942    /// [950] Format: Marktlokations-ID
8943    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8944        // Format condition — informational, always applies
8945        ConditionResult::True
8946    }
8947
8948    /// [951] Format: Zählpunktbezeichnung
8949    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8950        // Format condition — informational, always applies
8951        ConditionResult::True
8952    }
8953
8954    /// [952] Format: Gerätenummer nach DIN 43863-5
8955    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8956        // Format condition — informational, always applies
8957        ConditionResult::True
8958    }
8959
8960    /// [955] Format: Möglicher Wert: < 100
8961    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8962        // Format condition — informational, always applies
8963        ConditionResult::True
8964    }
8965
8966    /// [957] Format: n1-n2-n1-n8
8967    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8968        // Format condition — informational, always applies
8969        ConditionResult::True
8970    }
8971
8972    /// [960] Format: Netzlokations-ID
8973    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8974        // Format condition — informational, always applies
8975        ConditionResult::True
8976    }
8977
8978    /// [961] Format: SR-ID
8979    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8980        // Format condition — informational, always applies
8981        ConditionResult::True
8982    }
8983
8984    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8985    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8986        // Format condition — informational, always applies
8987        ConditionResult::True
8988    }
8989
8990    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8991    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8992        // Cardinality: at least twice per SG4 IDE+24
8993        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8994        ConditionResult::from(ide_count > 0)
8995    }
8996
8997    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8998    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8999        let seq_segs = ctx.find_segments("SEQ");
9000        let has_z79 = seq_segs.iter().any(|s| {
9001            s.elements
9002                .first()
9003                .and_then(|e| e.first())
9004                .is_some_and(|v| v == "Z79")
9005        });
9006        ConditionResult::from(has_z79)
9007    }
9008
9009    /// [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
9010    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
9011        // TODO: implement
9012        ConditionResult::Unknown
9013    }
9014
9015    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9016    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
9017    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("genau einmal")
9018    // is a count, not a condition on the group: the generated code counted
9019    // the SG8 of every variant naming the ID, so several variants made it
9020    // false and the group was never judged.
9021    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
9022        let named = ctx
9023            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
9024            .iter()
9025            .any(|s| !s.get_component(0, 2).is_empty());
9026        ConditionResult::from(named)
9027    }
9028
9029    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
9030    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
9031    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("mindestens einmal")
9032    // is a count, not a condition on the group: the generated code counted
9033    // the SG8 of every variant naming the ID, so several variants made it
9034    // false and the group was never judged.
9035    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9036        let named = ctx
9037            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
9038            .iter()
9039            .any(|s| !s.get_component(0, 2).is_empty());
9040        ConditionResult::from(named)
9041    }
9042
9043    /// [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 ...
9044    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9045        // TODO: implement
9046        ConditionResult::Unknown
9047    }
9048
9049    /// [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 ...
9050    /// EXTERNAL: Requires context from outside the message.
9051    // 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)
9052    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9053        ctx.external
9054            .evaluate("request_message_zeitraum_ids_present")
9055    }
9056
9057    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9058    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9059        // TODO: implement
9060        ConditionResult::Unknown
9061    }
9062
9063    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9064    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9065        // Check: condition applies when no RFF+Z54 exists
9066        ctx.lacks_qualifier("RFF", 0, "Z54")
9067    }
9068
9069    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9070    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9071        // Check: condition applies when no RFF+Z47 exists
9072        ctx.lacks_qualifier("RFF", 0, "Z47")
9073    }
9074
9075    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9076    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9077        // TODO: implement
9078        ConditionResult::Unknown
9079    }
9080
9081    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9082    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9083        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9084        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9085        ConditionResult::from(loc_count > 0)
9086    }
9087
9088    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9089    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9090        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9091        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9092        ConditionResult::from(loc_count > 0)
9093    }
9094
9095    /// [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...
9096    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9097        // TODO: implement
9098        ConditionResult::Unknown
9099    }
9100
9101    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9102    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9103        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9104        let found = ctx.find_segments("CAV").iter().any(|s| {
9105            s.elements
9106                .first()
9107                .and_then(|e| e.first())
9108                .is_some_and(|v| v == "NZR")
9109        });
9110        ConditionResult::from(found)
9111    }
9112
9113    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9114    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9115        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9116        let ide_count = ctx.find_segments("IDE").len();
9117        ConditionResult::from(ide_count > 0)
9118    }
9119
9120    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9121    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9122        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9123        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9124        ConditionResult::from(ide_count > 0)
9125    }
9126
9127    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9128    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9129        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9130        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9131        ConditionResult::from(ide_count > 0)
9132    }
9133
9134    /// [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)
9135    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9136        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9137        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9138        ConditionResult::from(ide_count > 0)
9139    }
9140
9141    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9142    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9143        // Cardinality: max twice per SG4 IDE+24
9144        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9145        ConditionResult::from(ide_count > 0)
9146    }
9147
9148    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9149    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9150        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9151        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9152        ConditionResult::from(loc_count > 0)
9153    }
9154
9155    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9156    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9157        // Cardinality: exactly twice per SG4 IDE
9158        let ide_count = ctx.find_segments("IDE").len();
9159        ConditionResult::from(ide_count > 0)
9160    }
9161
9162    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9163    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9164        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9165        let found = ctx.find_segments("SEQ").iter().any(|s| {
9166            s.elements
9167                .first()
9168                .and_then(|e| e.first())
9169                .is_some_and(|v| v == "Z13" || v == "ZG0")
9170        });
9171        ConditionResult::from(found)
9172    }
9173
9174    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9175    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9176        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9177        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9178        ConditionResult::from(ide_count > 0)
9179    }
9180
9181    /// [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
9182    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9183        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9184        let found = ctx.find_segments("SEQ").iter().any(|s| {
9185            s.elements
9186                .first()
9187                .and_then(|e| e.first())
9188                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9189        });
9190        ConditionResult::from(found)
9191    }
9192
9193    /// [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
9194    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9195        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
9196        let found = ctx.find_segments("SEQ").iter().any(|s| {
9197            s.elements
9198                .first()
9199                .and_then(|e| e.first())
9200                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9201        });
9202        ConditionResult::from(found)
9203    }
9204
9205    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9206    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9207        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
9208        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9209        ConditionResult::from(ide_count > 0)
9210    }
9211
9212    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9213    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9214        // TODO: implement
9215        ConditionResult::Unknown
9216    }
9217
9218    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9219    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9220        // Cardinality: code once per SG4 IDE+24
9221        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9222        ConditionResult::from(ide_count > 0)
9223    }
9224
9225    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9226    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9227        // TODO: implement
9228        ConditionResult::Unknown
9229    }
9230
9231    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9232    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9233        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9234        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9235        ConditionResult::from(loc_count > 0)
9236    }
9237
9238    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9239    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9240        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9241        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9242        ConditionResult::from(loc_count > 0)
9243    }
9244
9245    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9246    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9247        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9248        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9249        ConditionResult::from(found)
9250    }
9251
9252    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9253    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9254        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9255        let found = ctx.find_segments("SEQ").iter().any(|s| {
9256            s.elements
9257                .first()
9258                .and_then(|e| e.first())
9259                .is_some_and(|v| v == "Z03" || v == "ZF5")
9260        });
9261        ConditionResult::from(found)
9262    }
9263
9264    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9265    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9266        // TODO: implement
9267        ConditionResult::Unknown
9268    }
9269
9270    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9271    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9272        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9273        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9274        ConditionResult::from(loc_count > 0)
9275    }
9276
9277    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9278    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9279        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9280        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9281        ConditionResult::from(loc_count > 0)
9282    }
9283
9284    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9285    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9286        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9287        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9288        ConditionResult::from(loc_count > 0)
9289    }
9290
9291    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9292    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9293        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9294        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9295        ConditionResult::from(loc_count > 0)
9296    }
9297
9298    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9299    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9300        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9301        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9302        ConditionResult::from(loc_count > 0)
9303    }
9304
9305    /// [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...
9306    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9307        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9308        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9309        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9310        let has_netzlokation = rff_z33.iter().any(|s| {
9311            s.elements
9312                .first()
9313                .and_then(|e| e.get(1))
9314                .is_some_and(|v| v == "9992000001256")
9315        });
9316        ConditionResult::from(!has_netzlokation)
9317    }
9318
9319    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9320    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9321        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9322        let found = ctx.find_segments("SEQ").iter().any(|s| {
9323            s.elements
9324                .first()
9325                .and_then(|e| e.first())
9326                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9327        });
9328        ConditionResult::from(found)
9329    }
9330
9331    /// [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
9332    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9333        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9334        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9335        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9336            s.elements
9337                .first()
9338                .and_then(|e| e.first())
9339                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9340        });
9341        ConditionResult::from(has_cav)
9342    }
9343
9344    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9345    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9346        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9347        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9348        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9349            s.elements
9350                .first()
9351                .and_then(|e| e.first())
9352                .is_some_and(|v| v == "MBW")
9353        });
9354        ConditionResult::from(has_cav)
9355    }
9356
9357    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9358    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
9359        // TODO: implement
9360        ConditionResult::Unknown
9361    }
9362
9363    /// [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...
9364    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9365        // TODO: implement
9366        ConditionResult::Unknown
9367    }
9368
9369    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9370    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9371        // TODO: implement
9372        ConditionResult::Unknown
9373    }
9374
9375    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9376    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9377        // TODO: implement
9378        ConditionResult::Unknown
9379    }
9380
9381    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9382    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9383        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9384        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9385        ConditionResult::from(loc_count > 0)
9386    }
9387
9388    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9389    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9390        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9391        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9392        ConditionResult::from(loc_count > 0)
9393    }
9394
9395    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9396    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9397        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9398        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9399        ConditionResult::from(loc_count > 0)
9400    }
9401
9402    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9403    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9404        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9405        let found = ctx.find_segments("LOC").iter().any(|s| {
9406            s.elements
9407                .first()
9408                .and_then(|e| e.first())
9409                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9410        });
9411        ConditionResult::from(found)
9412    }
9413
9414    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9415    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9416        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9417        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9418        ConditionResult::from(loc_count > 0)
9419    }
9420
9421    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9422    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9423        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9424        let found = ctx.find_segments("LOC").iter().any(|s| {
9425            s.elements
9426                .first()
9427                .and_then(|e| e.first())
9428                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9429        });
9430        ConditionResult::from(found)
9431    }
9432}
9433
9434// HAND-EDITED: helpers for [223] / [265].
9435
9436use mig_types::segment::OwnedSegment;
9437
9438/// One SG8 repetition: its SEQ qualifier, the Marktlokation IDs of its
9439/// RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9440struct Sg8Block {
9441    seq: String,
9442    malo_refs: Vec<String>,
9443    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9444}
9445
9446fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9447    seg.elements
9448        .get(element)
9449        .and_then(|e| e.get(comp))
9450        .map(String::as_str)
9451}
9452
9453/// The message's SG8 repetitions, from the group navigator when there is one,
9454/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9455/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9456/// evaluates BO4E input without a navigator.) `None` without either.
9457fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9458    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9459        rffs.iter()
9460            .filter(|s| component(s, 0, 0) == Some("Z18"))
9461            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9462            .collect()
9463    };
9464    if let Some(nav) = ctx.navigator() {
9465        let sg8 = ["SG4", "SG8"];
9466        return Some(
9467            (0..nav.group_instance_count(&sg8))
9468                .map(|i| Sg8Block {
9469                    seq: nav
9470                        .find_segments_in_group("SEQ", &sg8, i)
9471                        .first()
9472                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9473                        .unwrap_or_default(),
9474                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9475                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9476                        .map(|j| {
9477                            (
9478                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9479                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9480                            )
9481                        })
9482                        .collect(),
9483                })
9484                .collect(),
9485        );
9486    }
9487    if ctx.segments.is_empty() {
9488        return None;
9489    }
9490    let mut blocks: Vec<Sg8Block> = Vec::new();
9491    let mut rffs: Vec<OwnedSegment> = Vec::new();
9492    for seg in ctx.segments {
9493        match seg.id.as_str() {
9494            "SEQ" => {
9495                if let Some(b) = blocks.last_mut() {
9496                    b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9497                }
9498                rffs.clear();
9499                blocks.push(Sg8Block {
9500                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9501                    malo_refs: Vec::new(),
9502                    sg10: Vec::new(),
9503                });
9504            }
9505            // Segments after the SG8s end the last one.
9506            "NAD" | "UNT" | "IDE" => {
9507                if let Some(b) = blocks.last_mut() {
9508                    if b.malo_refs.is_empty() {
9509                        b.malo_refs = malo_refs(&std::mem::take(&mut rffs));
9510                    }
9511                }
9512                rffs.clear();
9513                if seg.id == "IDE" {
9514                    continue;
9515                }
9516                break;
9517            }
9518            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9519            "CCI" => {
9520                if let Some(b) = blocks.last_mut() {
9521                    b.sg10.push((vec![seg.clone()], Vec::new()));
9522                }
9523            }
9524            "CAV" => {
9525                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9526                    cavs.push(seg.clone());
9527                }
9528            }
9529            _ => {}
9530        }
9531    }
9532    if let Some(b) = blocks.last_mut() {
9533        if b.malo_refs.is_empty() {
9534            b.malo_refs = malo_refs(&rffs);
9535        }
9536    }
9537    Some(blocks)
9538}
9539
9540// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9541// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9542
9543/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9544/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9545/// one; any SG10 of the message otherwise.
9546fn sg10_has(
9547    ctx: &EvaluationContext,
9548    this_sg8: bool,
9549    pred: impl Fn(&[OwnedSegment]) -> bool,
9550) -> ConditionResult {
9551    if this_sg8 {
9552        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9553            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9554        }
9555    }
9556    let Some(blocks) = sg8_blocks(ctx) else {
9557        return ConditionResult::False;
9558    };
9559    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9560        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9561        pred(&segs)
9562    }))
9563}
9564
9565/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9566/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9567/// otherwise.
9568fn this_sg10_has(
9569    ctx: &EvaluationContext,
9570    pred: impl Fn(&[OwnedSegment]) -> bool,
9571) -> ConditionResult {
9572    if let Some(path) = ctx.enclosing_scope("SG10") {
9573        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9574            .iter()
9575            .flat_map(|tag| ctx.find_segments_at(path, tag))
9576            .collect();
9577        return ConditionResult::from(pred(&segs));
9578    }
9579    sg10_has(ctx, false, pred)
9580}
9581
9582/// "Wenn in demselben RFF der Code … im DE1153 vorhanden": the qualifier of the
9583/// RFF whose field is judged; message-wide only where no segment is resolved.
9584fn same_rff_qualifier(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9585    match ctx.resolved_segment {
9586        Some(elements) => ConditionResult::from(
9587            elements
9588                .first()
9589                .and_then(|e| e.first())
9590                .is_some_and(|q| q == code),
9591        ),
9592        None => ctx.has_qualifier("RFF", 0, code),
9593    }
9594}
9595
9596/// HAND-EDITED: `[126]`/`[127]`/`[128]` — the value is the 1-based position of
9597/// the resolved RFF among the SG6 RFF of `qualifiers` in its SG4 ("Das erste
9598/// SG6 RFF+Z49/Z53 hat somit die 1, das zweite die 2").
9599fn repetition_ordinal(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9600    let (Some(value), Some(segment)) = (ctx.resolved_value, ctx.resolved_segment) else {
9601        return ConditionResult::Unknown;
9602    };
9603    let siblings: Vec<_> = ctx
9604        .scoped_find_segments_in("SG4", "RFF")
9605        .into_iter()
9606        .filter(|s| qualifiers.contains(&s.get_component(0, 0)))
9607        .collect();
9608    match siblings.iter().position(|s| s.elements.as_slice() == segment) {
9609        Some(i) => ConditionResult::from(value == (i + 1).to_string()),
9610        None => ConditionResult::Unknown,
9611    }
9612}