Skip to main content

automapper_validation/generated/fv2504/
utilmd_strom_conditions_fv2504.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2504/UTILMD_AHB_Strom_2_1_Fehlerkorrektur_20250623.xml
4// Generated: 2026-03-12T10:15:42Z
5// </auto-generated>
6
7// LLM-produced bodies aren't idiomatic Rust — silence stylistic lints at
8// file scope so `cargo clippy -D warnings` stays green. Correctness,
9// suspicious, and perf categories stay on so real bugs still surface.
10#![allow(clippy::style, clippy::complexity)]
11
12#[allow(unused_imports)]
13use crate::eval::format_validators::*;
14// HAND-EDITED: instances of one SG4 compared with each other.
15use crate::eval::sg4_instances;
16use crate::eval::{Cluster, ConditionEvaluator, ConditionResult, EvaluationContext};
17// HAND-EDITED: shared cluster helpers for conditions [359]/[360]/[366]/[368].
18// Preserve across regeneration — see CLAUDE.md "Codelist Providers".
19use crate::eval::ebd_cluster::{all_sts_e01_in_cluster, ebd_ablehnung_except};
20
21/// Generated condition evaluator for UTILMD_Strom FV2504.
22pub struct UtilmdStromConditionEvaluatorFV2504 {
23    // External condition IDs that require runtime context.
24    external_conditions: std::collections::HashSet<u32>,
25}
26
27impl Default for UtilmdStromConditionEvaluatorFV2504 {
28    fn default() -> Self {
29        let mut external_conditions = std::collections::HashSet::new();
30        external_conditions.insert(1);
31        external_conditions.insert(4);
32        external_conditions.insert(5);
33        external_conditions.insert(6);
34        external_conditions.insert(8);
35        external_conditions.insert(9);
36        external_conditions.insert(14);
37        external_conditions.insert(31);
38        external_conditions.insert(33);
39        external_conditions.insert(36);
40        external_conditions.insert(38);
41        external_conditions.insert(39);
42        external_conditions.insert(40);
43        external_conditions.insert(75);
44        external_conditions.insert(98);
45        external_conditions.insert(99);
46        external_conditions.insert(151);
47        external_conditions.insert(233);
48        external_conditions.insert(241);
49        external_conditions.insert(251);
50        external_conditions.insert(292);
51        external_conditions.insert(314);
52        external_conditions.insert(315);
53        external_conditions.insert(323);
54        external_conditions.insert(412);
55        external_conditions.insert(425);
56        external_conditions.insert(426);
57        external_conditions.insert(427);
58        external_conditions.insert(428);
59        external_conditions.insert(429);
60        external_conditions.insert(433);
61        external_conditions.insert(435);
62        external_conditions.insert(453);
63        external_conditions.insert(459);
64        external_conditions.insert(460);
65        external_conditions.insert(706);
66        external_conditions.insert(717);
67        external_conditions.insert(2011);
68        Self {
69            external_conditions,
70        }
71    }
72}
73
74impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2504 {
75    fn message_type(&self) -> &str {
76        "UTILMD_Strom"
77    }
78
79    fn format_version(&self) -> &str {
80        "FV2504"
81    }
82
83    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
84        match condition {
85            1 => self.evaluate_1(ctx),
86            4 => self.evaluate_4(ctx),
87            5 => self.evaluate_5(ctx),
88            6 => self.evaluate_6(ctx),
89            7 => self.evaluate_7(ctx),
90            8 => self.evaluate_8(ctx),
91            9 => self.evaluate_9(ctx),
92            10 => self.evaluate_10(ctx),
93            11 => self.evaluate_11(ctx),
94            12 => self.evaluate_12(ctx),
95            13 => self.evaluate_13(ctx),
96            14 => self.evaluate_14(ctx),
97            15 => self.evaluate_15(ctx),
98            16 => self.evaluate_16(ctx),
99            17 => self.evaluate_17(ctx),
100            18 => self.evaluate_18(ctx),
101            19 => self.evaluate_19(ctx),
102            20 => self.evaluate_20(ctx),
103            21 => self.evaluate_21(ctx),
104            22 => self.evaluate_22(ctx),
105            23 => self.evaluate_23(ctx),
106            24 => self.evaluate_24(ctx),
107            25 => self.evaluate_25(ctx),
108            27 => self.evaluate_27(ctx),
109            30 => self.evaluate_30(ctx),
110            31 => self.evaluate_31(ctx),
111            32 => self.evaluate_32(ctx),
112            33 => self.evaluate_33(ctx),
113            34 => self.evaluate_34(ctx),
114            35 => self.evaluate_35(ctx),
115            36 => self.evaluate_36(ctx),
116            37 => self.evaluate_37(ctx),
117            38 => self.evaluate_38(ctx),
118            39 => self.evaluate_39(ctx),
119            40 => self.evaluate_40(ctx),
120            41 => self.evaluate_41(ctx),
121            42 => self.evaluate_42(ctx),
122            43 => self.evaluate_43(ctx),
123            44 => self.evaluate_44(ctx),
124            45 => self.evaluate_45(ctx),
125            46 => self.evaluate_46(ctx),
126            47 => self.evaluate_47(ctx),
127            48 => self.evaluate_48(ctx),
128            49 => self.evaluate_49(ctx),
129            50 => self.evaluate_50(ctx),
130            51 => self.evaluate_51(ctx),
131            52 => self.evaluate_52(ctx),
132            53 => self.evaluate_53(ctx),
133            54 => self.evaluate_54(ctx),
134            55 => self.evaluate_55(ctx),
135            56 => self.evaluate_56(ctx),
136            57 => self.evaluate_57(ctx),
137            58 => self.evaluate_58(ctx),
138            60 => self.evaluate_60(ctx),
139            61 => self.evaluate_61(ctx),
140            62 => self.evaluate_62(ctx),
141            63 => self.evaluate_63(ctx),
142            64 => self.evaluate_64(ctx),
143            65 => self.evaluate_65(ctx),
144            66 => self.evaluate_66(ctx),
145            67 => self.evaluate_67(ctx),
146            68 => self.evaluate_68(ctx),
147            69 => self.evaluate_69(ctx),
148            70 => self.evaluate_70(ctx),
149            71 => self.evaluate_71(ctx),
150            72 => self.evaluate_72(ctx),
151            73 => self.evaluate_73(ctx),
152            74 => self.evaluate_74(ctx),
153            75 => self.evaluate_75(ctx),
154            76 => self.evaluate_76(ctx),
155            77 => self.evaluate_77(ctx),
156            78 => self.evaluate_78(ctx),
157            83 => self.evaluate_83(ctx),
158            84 => self.evaluate_84(ctx),
159            85 => self.evaluate_85(ctx),
160            86 => self.evaluate_86(ctx),
161            87 => self.evaluate_87(ctx),
162            88 => self.evaluate_88(ctx),
163            89 => self.evaluate_89(ctx),
164            90 => self.evaluate_90(ctx),
165            91 => self.evaluate_91(ctx),
166            92 => self.evaluate_92(ctx),
167            93 => self.evaluate_93(ctx),
168            94 => self.evaluate_94(ctx),
169            95 => self.evaluate_95(ctx),
170            96 => self.evaluate_96(ctx),
171            97 => self.evaluate_97(ctx),
172            98 => self.evaluate_98(ctx),
173            99 => self.evaluate_99(ctx),
174            100 => self.evaluate_100(ctx),
175            101 => self.evaluate_101(ctx),
176            102 => self.evaluate_102(ctx),
177            103 => self.evaluate_103(ctx),
178            104 => self.evaluate_104(ctx),
179            105 => self.evaluate_105(ctx),
180            106 => self.evaluate_106(ctx),
181            107 => self.evaluate_107(ctx),
182            108 => self.evaluate_108(ctx),
183            110 => self.evaluate_110(ctx),
184            111 => self.evaluate_111(ctx),
185            112 => self.evaluate_112(ctx),
186            113 => self.evaluate_113(ctx),
187            114 => self.evaluate_114(ctx),
188            115 => self.evaluate_115(ctx),
189            116 => self.evaluate_116(ctx),
190            117 => self.evaluate_117(ctx),
191            118 => self.evaluate_118(ctx),
192            119 => self.evaluate_119(ctx),
193            120 => self.evaluate_120(ctx),
194            121 => self.evaluate_121(ctx),
195            122 => self.evaluate_122(ctx),
196            123 => self.evaluate_123(ctx),
197            124 => self.evaluate_124(ctx),
198            125 => self.evaluate_125(ctx),
199            126 => self.evaluate_126(ctx),
200            127 => self.evaluate_127(ctx),
201            128 => self.evaluate_128(ctx),
202            129 => self.evaluate_129(ctx),
203            130 => self.evaluate_130(ctx),
204            131 => self.evaluate_131(ctx),
205            132 => self.evaluate_132(ctx),
206            133 => self.evaluate_133(ctx),
207            134 => self.evaluate_134(ctx),
208            135 => self.evaluate_135(ctx),
209            136 => self.evaluate_136(ctx),
210            137 => self.evaluate_137(ctx),
211            138 => self.evaluate_138(ctx),
212            139 => self.evaluate_139(ctx),
213            140 => self.evaluate_140(ctx),
214            141 => self.evaluate_141(ctx),
215            142 => self.evaluate_142(ctx),
216            143 => self.evaluate_143(ctx),
217            144 => self.evaluate_144(ctx),
218            145 => self.evaluate_145(ctx),
219            146 => self.evaluate_146(ctx),
220            147 => self.evaluate_147(ctx),
221            148 => self.evaluate_148(ctx),
222            149 => self.evaluate_149(ctx),
223            150 => self.evaluate_150(ctx),
224            151 => self.evaluate_151(ctx),
225            152 => self.evaluate_152(ctx),
226            153 => self.evaluate_153(ctx),
227            156 => self.evaluate_156(ctx),
228            161 => self.evaluate_161(ctx),
229            162 => self.evaluate_162(ctx),
230            163 => self.evaluate_163(ctx),
231            164 => self.evaluate_164(ctx),
232            165 => self.evaluate_165(ctx),
233            166 => self.evaluate_166(ctx),
234            167 => self.evaluate_167(ctx),
235            170 => self.evaluate_170(ctx),
236            172 => self.evaluate_172(ctx),
237            173 => self.evaluate_173(ctx),
238            174 => self.evaluate_174(ctx),
239            175 => self.evaluate_175(ctx),
240            176 => self.evaluate_176(ctx),
241            177 => self.evaluate_177(ctx),
242            178 => self.evaluate_178(ctx),
243            179 => self.evaluate_179(ctx),
244            180 => self.evaluate_180(ctx),
245            184 => self.evaluate_184(ctx),
246            190 => self.evaluate_190(ctx),
247            191 => self.evaluate_191(ctx),
248            192 => self.evaluate_192(ctx),
249            193 => self.evaluate_193(ctx),
250            194 => self.evaluate_194(ctx),
251            195 => self.evaluate_195(ctx),
252            196 => self.evaluate_196(ctx),
253            197 => self.evaluate_197(ctx),
254            198 => self.evaluate_198(ctx),
255            199 => self.evaluate_199(ctx),
256            201 => self.evaluate_201(ctx),
257            202 => self.evaluate_202(ctx),
258            203 => self.evaluate_203(ctx),
259            204 => self.evaluate_204(ctx),
260            205 => self.evaluate_205(ctx),
261            206 => self.evaluate_206(ctx),
262            209 => self.evaluate_209(ctx),
263            210 => self.evaluate_210(ctx),
264            212 => self.evaluate_212(ctx),
265            213 => self.evaluate_213(ctx),
266            215 => self.evaluate_215(ctx),
267            216 => self.evaluate_216(ctx),
268            217 => self.evaluate_217(ctx),
269            219 => self.evaluate_219(ctx),
270            220 => self.evaluate_220(ctx),
271            221 => self.evaluate_221(ctx),
272            223 => self.evaluate_223(ctx),
273            224 => self.evaluate_224(ctx),
274            227 => self.evaluate_227(ctx),
275            229 => self.evaluate_229(ctx),
276            232 => self.evaluate_232(ctx),
277            233 => self.evaluate_233(ctx),
278            234 => self.evaluate_234(ctx),
279            237 => self.evaluate_237(ctx),
280            238 => self.evaluate_238(ctx),
281            239 => self.evaluate_239(ctx),
282            240 => self.evaluate_240(ctx),
283            241 => self.evaluate_241(ctx),
284            243 => self.evaluate_243(ctx),
285            244 => self.evaluate_244(ctx),
286            248 => self.evaluate_248(ctx),
287            249 => self.evaluate_249(ctx),
288            251 => self.evaluate_251(ctx),
289            252 => self.evaluate_252(ctx),
290            254 => self.evaluate_254(ctx),
291            255 => self.evaluate_255(ctx),
292            256 => self.evaluate_256(ctx),
293            257 => self.evaluate_257(ctx),
294            259 => self.evaluate_259(ctx),
295            261 => self.evaluate_261(ctx),
296            262 => self.evaluate_262(ctx),
297            265 => self.evaluate_265(ctx),
298            266 => self.evaluate_266(ctx),
299            267 => self.evaluate_267(ctx),
300            268 => self.evaluate_268(ctx),
301            269 => self.evaluate_269(ctx),
302            270 => self.evaluate_270(ctx),
303            273 => self.evaluate_273(ctx),
304            279 => self.evaluate_279(ctx),
305            280 => self.evaluate_280(ctx),
306            282 => self.evaluate_282(ctx),
307            284 => self.evaluate_284(ctx),
308            285 => self.evaluate_285(ctx),
309            286 => self.evaluate_286(ctx),
310            287 => self.evaluate_287(ctx),
311            288 => self.evaluate_288(ctx),
312            291 => self.evaluate_291(ctx),
313            292 => self.evaluate_292(ctx),
314            293 => self.evaluate_293(ctx),
315            294 => self.evaluate_294(ctx),
316            295 => self.evaluate_295(ctx),
317            296 => self.evaluate_296(ctx),
318            297 => self.evaluate_297(ctx),
319            298 => self.evaluate_298(ctx),
320            299 => self.evaluate_299(ctx),
321            300 => self.evaluate_300(ctx),
322            301 => self.evaluate_301(ctx),
323            302 => self.evaluate_302(ctx),
324            303 => self.evaluate_303(ctx),
325            304 => self.evaluate_304(ctx),
326            305 => self.evaluate_305(ctx),
327            306 => self.evaluate_306(ctx),
328            307 => self.evaluate_307(ctx),
329            309 => self.evaluate_309(ctx),
330            314 => self.evaluate_314(ctx),
331            315 => self.evaluate_315(ctx),
332            316 => self.evaluate_316(ctx),
333            317 => self.evaluate_317(ctx),
334            318 => self.evaluate_318(ctx),
335            321 => self.evaluate_321(ctx),
336            322 => self.evaluate_322(ctx),
337            323 => self.evaluate_323(ctx),
338            327 => self.evaluate_327(ctx),
339            329 => self.evaluate_329(ctx),
340            331 => self.evaluate_331(ctx),
341            332 => self.evaluate_332(ctx),
342            333 => self.evaluate_333(ctx),
343            334 => self.evaluate_334(ctx),
344            335 => self.evaluate_335(ctx),
345            336 => self.evaluate_336(ctx),
346            337 => self.evaluate_337(ctx),
347            338 => self.evaluate_338(ctx),
348            339 => self.evaluate_339(ctx),
349            340 => self.evaluate_340(ctx),
350            341 => self.evaluate_341(ctx),
351            342 => self.evaluate_342(ctx),
352            344 => self.evaluate_344(ctx),
353            345 => self.evaluate_345(ctx),
354            346 => self.evaluate_346(ctx),
355            347 => self.evaluate_347(ctx),
356            348 => self.evaluate_348(ctx),
357            349 => self.evaluate_349(ctx),
358            350 => self.evaluate_350(ctx),
359            351 => self.evaluate_351(ctx),
360            352 => self.evaluate_352(ctx),
361            355 => self.evaluate_355(ctx),
362            356 => self.evaluate_356(ctx),
363            357 => self.evaluate_357(ctx),
364            358 => self.evaluate_358(ctx),
365            359 => self.evaluate_359(ctx),
366            360 => self.evaluate_360(ctx),
367            363 => self.evaluate_363(ctx),
368            365 => self.evaluate_365(ctx),
369            366 => self.evaluate_366(ctx),
370            367 => self.evaluate_367(ctx),
371            368 => self.evaluate_368(ctx),
372            370 => self.evaluate_370(ctx),
373            371 => self.evaluate_371(ctx),
374            372 => self.evaluate_372(ctx),
375            373 => self.evaluate_373(ctx),
376            375 => self.evaluate_375(ctx),
377            376 => self.evaluate_376(ctx),
378            377 => self.evaluate_377(ctx),
379            378 => self.evaluate_378(ctx),
380            379 => self.evaluate_379(ctx),
381            380 => self.evaluate_380(ctx),
382            384 => self.evaluate_384(ctx),
383            386 => self.evaluate_386(ctx),
384            387 => self.evaluate_387(ctx),
385            388 => self.evaluate_388(ctx),
386            391 => self.evaluate_391(ctx),
387            392 => self.evaluate_392(ctx),
388            393 => self.evaluate_393(ctx),
389            394 => self.evaluate_394(ctx),
390            395 => self.evaluate_395(ctx),
391            396 => self.evaluate_396(ctx),
392            397 => self.evaluate_397(ctx),
393            398 => self.evaluate_398(ctx),
394            399 => self.evaluate_399(ctx),
395            401 => self.evaluate_401(ctx),
396            402 => self.evaluate_402(ctx),
397            403 => self.evaluate_403(ctx),
398            404 => self.evaluate_404(ctx),
399            405 => self.evaluate_405(ctx),
400            406 => self.evaluate_406(ctx),
401            407 => self.evaluate_407(ctx),
402            408 => self.evaluate_408(ctx),
403            409 => self.evaluate_409(ctx),
404            410 => self.evaluate_410(ctx),
405            411 => self.evaluate_411(ctx),
406            412 => self.evaluate_412(ctx),
407            413 => self.evaluate_413(ctx),
408            414 => self.evaluate_414(ctx),
409            415 => self.evaluate_415(ctx),
410            416 => self.evaluate_416(ctx),
411            417 => self.evaluate_417(ctx),
412            419 => self.evaluate_419(ctx),
413            420 => self.evaluate_420(ctx),
414            421 => self.evaluate_421(ctx),
415            425 => self.evaluate_425(ctx),
416            426 => self.evaluate_426(ctx),
417            427 => self.evaluate_427(ctx),
418            428 => self.evaluate_428(ctx),
419            429 => self.evaluate_429(ctx),
420            430 => self.evaluate_430(ctx),
421            431 => self.evaluate_431(ctx),
422            432 => self.evaluate_432(ctx),
423            433 => self.evaluate_433(ctx),
424            435 => self.evaluate_435(ctx),
425            436 => self.evaluate_436(ctx),
426            437 => self.evaluate_437(ctx),
427            438 => self.evaluate_438(ctx),
428            440 => self.evaluate_440(ctx),
429            441 => self.evaluate_441(ctx),
430            442 => self.evaluate_442(ctx),
431            444 => self.evaluate_444(ctx),
432            445 => self.evaluate_445(ctx),
433            446 => self.evaluate_446(ctx),
434            447 => self.evaluate_447(ctx),
435            448 => self.evaluate_448(ctx),
436            449 => self.evaluate_449(ctx),
437            450 => self.evaluate_450(ctx),
438            451 => self.evaluate_451(ctx),
439            452 => self.evaluate_452(ctx),
440            453 => self.evaluate_453(ctx),
441            454 => self.evaluate_454(ctx),
442            455 => self.evaluate_455(ctx),
443            456 => self.evaluate_456(ctx),
444            457 => self.evaluate_457(ctx),
445            458 => self.evaluate_458(ctx),
446            459 => self.evaluate_459(ctx),
447            460 => self.evaluate_460(ctx),
448            461 => self.evaluate_461(ctx),
449            462 => self.evaluate_462(ctx),
450            463 => self.evaluate_463(ctx),
451            465 => self.evaluate_465(ctx),
452            466 => self.evaluate_466(ctx),
453            467 => self.evaluate_467(ctx),
454            468 => self.evaluate_468(ctx),
455            469 => self.evaluate_469(ctx),
456            470 => self.evaluate_470(ctx),
457            471 => self.evaluate_471(ctx),
458            472 => self.evaluate_472(ctx),
459            473 => self.evaluate_473(ctx),
460            474 => self.evaluate_474(ctx),
461            475 => self.evaluate_475(ctx),
462            476 => self.evaluate_476(ctx),
463            477 => self.evaluate_477(ctx),
464            478 => self.evaluate_478(ctx),
465            479 => self.evaluate_479(ctx),
466            480 => self.evaluate_480(ctx),
467            481 => self.evaluate_481(ctx),
468            483 => self.evaluate_483(ctx),
469            484 => self.evaluate_484(ctx),
470            487 => self.evaluate_487(ctx),
471            489 => self.evaluate_489(ctx),
472            490 => self.evaluate_490(ctx),
473            491 => self.evaluate_491(ctx),
474            494 => self.evaluate_494(ctx),
475            500 => self.evaluate_500(ctx),
476            501 => self.evaluate_501(ctx),
477            502 => self.evaluate_502(ctx),
478            503 => self.evaluate_503(ctx),
479            504 => self.evaluate_504(ctx),
480            505 => self.evaluate_505(ctx),
481            506 => self.evaluate_506(ctx),
482            507 => self.evaluate_507(ctx),
483            508 => self.evaluate_508(ctx),
484            509 => self.evaluate_509(ctx),
485            510 => self.evaluate_510(ctx),
486            511 => self.evaluate_511(ctx),
487            512 => self.evaluate_512(ctx),
488            513 => self.evaluate_513(ctx),
489            514 => self.evaluate_514(ctx),
490            515 => self.evaluate_515(ctx),
491            516 => self.evaluate_516(ctx),
492            517 => self.evaluate_517(ctx),
493            518 => self.evaluate_518(ctx),
494            519 => self.evaluate_519(ctx),
495            520 => self.evaluate_520(ctx),
496            521 => self.evaluate_521(ctx),
497            522 => self.evaluate_522(ctx),
498            523 => self.evaluate_523(ctx),
499            524 => self.evaluate_524(ctx),
500            525 => self.evaluate_525(ctx),
501            526 => self.evaluate_526(ctx),
502            527 => self.evaluate_527(ctx),
503            528 => self.evaluate_528(ctx),
504            529 => self.evaluate_529(ctx),
505            530 => self.evaluate_530(ctx),
506            531 => self.evaluate_531(ctx),
507            532 => self.evaluate_532(ctx),
508            533 => self.evaluate_533(ctx),
509            555 => self.evaluate_555(ctx),
510            556 => self.evaluate_556(ctx),
511            558 => self.evaluate_558(ctx),
512            559 => self.evaluate_559(ctx),
513            563 => self.evaluate_563(ctx),
514            566 => self.evaluate_566(ctx),
515            567 => self.evaluate_567(ctx),
516            568 => self.evaluate_568(ctx),
517            569 => self.evaluate_569(ctx),
518            570 => self.evaluate_570(ctx),
519            572 => self.evaluate_572(ctx),
520            576 => self.evaluate_576(ctx),
521            577 => self.evaluate_577(ctx),
522            579 => self.evaluate_579(ctx),
523            580 => self.evaluate_580(ctx),
524            581 => self.evaluate_581(ctx),
525            586 => self.evaluate_586(ctx),
526            590 => self.evaluate_590(ctx),
527            594 => self.evaluate_594(ctx),
528            599 => self.evaluate_599(ctx),
529            601 => self.evaluate_601(ctx),
530            606 => self.evaluate_606(ctx),
531            609 => self.evaluate_609(ctx),
532            611 => self.evaluate_611(ctx),
533            614 => self.evaluate_614(ctx),
534            617 => self.evaluate_617(ctx),
535            618 => self.evaluate_618(ctx),
536            619 => self.evaluate_619(ctx),
537            621 => self.evaluate_621(ctx),
538            622 => self.evaluate_622(ctx),
539            623 => self.evaluate_623(ctx),
540            630 => self.evaluate_630(ctx),
541            631 => self.evaluate_631(ctx),
542            632 => self.evaluate_632(ctx),
543            637 => self.evaluate_637(ctx),
544            638 => self.evaluate_638(ctx),
545            639 => self.evaluate_639(ctx),
546            640 => self.evaluate_640(ctx),
547            641 => self.evaluate_641(ctx),
548            642 => self.evaluate_642(ctx),
549            643 => self.evaluate_643(ctx),
550            645 => self.evaluate_645(ctx),
551            646 => self.evaluate_646(ctx),
552            647 => self.evaluate_647(ctx),
553            648 => self.evaluate_648(ctx),
554            651 => self.evaluate_651(ctx),
555            653 => self.evaluate_653(ctx),
556            655 => self.evaluate_655(ctx),
557            659 => self.evaluate_659(ctx),
558            660 => self.evaluate_660(ctx),
559            662 => self.evaluate_662(ctx),
560            663 => self.evaluate_663(ctx),
561            664 => self.evaluate_664(ctx),
562            665 => self.evaluate_665(ctx),
563            667 => self.evaluate_667(ctx),
564            668 => self.evaluate_668(ctx),
565            670 => self.evaluate_670(ctx),
566            671 => self.evaluate_671(ctx),
567            672 => self.evaluate_672(ctx),
568            673 => self.evaluate_673(ctx),
569            674 => self.evaluate_674(ctx),
570            675 => self.evaluate_675(ctx),
571            677 => self.evaluate_677(ctx),
572            678 => self.evaluate_678(ctx),
573            679 => self.evaluate_679(ctx),
574            680 => self.evaluate_680(ctx),
575            682 => self.evaluate_682(ctx),
576            683 => self.evaluate_683(ctx),
577            684 => self.evaluate_684(ctx),
578            685 => self.evaluate_685(ctx),
579            687 => self.evaluate_687(ctx),
580            688 => self.evaluate_688(ctx),
581            689 => self.evaluate_689(ctx),
582            690 => self.evaluate_690(ctx),
583            693 => self.evaluate_693(ctx),
584            694 => self.evaluate_694(ctx),
585            695 => self.evaluate_695(ctx),
586            696 => self.evaluate_696(ctx),
587            698 => self.evaluate_698(ctx),
588            699 => self.evaluate_699(ctx),
589            700 => self.evaluate_700(ctx),
590            704 => self.evaluate_704(ctx),
591            705 => self.evaluate_705(ctx),
592            706 => self.evaluate_706(ctx),
593            707 => self.evaluate_707(ctx),
594            708 => self.evaluate_708(ctx),
595            709 => self.evaluate_709(ctx),
596            710 => self.evaluate_710(ctx),
597            711 => self.evaluate_711(ctx),
598            712 => self.evaluate_712(ctx),
599            713 => self.evaluate_713(ctx),
600            714 => self.evaluate_714(ctx),
601            715 => self.evaluate_715(ctx),
602            716 => self.evaluate_716(ctx),
603            717 => self.evaluate_717(ctx),
604            718 => self.evaluate_718(ctx),
605            719 => self.evaluate_719(ctx),
606            902 => self.evaluate_902(ctx),
607            910 => self.evaluate_910(ctx),
608            914 => self.evaluate_914(ctx),
609            922 => self.evaluate_922(ctx),
610            926 => self.evaluate_926(ctx),
611            930 => self.evaluate_930(ctx),
612            931 => self.evaluate_931(ctx),
613            932 => self.evaluate_932(ctx),
614            933 => self.evaluate_933(ctx),
615            937 => self.evaluate_937(ctx),
616            938 => self.evaluate_938(ctx),
617            939 => self.evaluate_939(ctx),
618            940 => self.evaluate_940(ctx),
619            942 => self.evaluate_942(ctx),
620            943 => self.evaluate_943(ctx),
621            946 => self.evaluate_946(ctx),
622            948 => self.evaluate_948(ctx),
623            950 => self.evaluate_950(ctx),
624            951 => self.evaluate_951(ctx),
625            952 => self.evaluate_952(ctx),
626            955 => self.evaluate_955(ctx),
627            957 => self.evaluate_957(ctx),
628            960 => self.evaluate_960(ctx),
629            961 => self.evaluate_961(ctx),
630            967 => self.evaluate_967(ctx),
631            2001 => self.evaluate_2001(ctx),
632            2002 => self.evaluate_2002(ctx),
633            2003 => self.evaluate_2003(ctx),
634            2004 => self.evaluate_2004(ctx),
635            2005 => self.evaluate_2005(ctx),
636            2006 => self.evaluate_2006(ctx),
637            2007 => self.evaluate_2007(ctx),
638            2008 => self.evaluate_2008(ctx),
639            2009 => self.evaluate_2009(ctx),
640            2010 => self.evaluate_2010(ctx),
641            2011 => self.evaluate_2011(ctx),
642            2012 => self.evaluate_2012(ctx),
643            2013 => self.evaluate_2013(ctx),
644            2014 => self.evaluate_2014(ctx),
645            2015 => self.evaluate_2015(ctx),
646            2016 => self.evaluate_2016(ctx),
647            2017 => self.evaluate_2017(ctx),
648            2018 => self.evaluate_2018(ctx),
649            2060 => self.evaluate_2060(ctx),
650            2061 => self.evaluate_2061(ctx),
651            2071 => self.evaluate_2071(ctx),
652            2073 => self.evaluate_2073(ctx),
653            2075 => self.evaluate_2075(ctx),
654            2080 => self.evaluate_2080(ctx),
655            2095 => self.evaluate_2095(ctx),
656            2096 => self.evaluate_2096(ctx),
657            2119 => self.evaluate_2119(ctx),
658            2140 => self.evaluate_2140(ctx),
659            2182 => self.evaluate_2182(ctx),
660            2183 => self.evaluate_2183(ctx),
661            2207 => self.evaluate_2207(ctx),
662            2225 => self.evaluate_2225(ctx),
663            2236 => self.evaluate_2236(ctx),
664            2252 => self.evaluate_2252(ctx),
665            2261 => self.evaluate_2261(ctx),
666            2284 => self.evaluate_2284(ctx),
667            2286 => self.evaluate_2286(ctx),
668            2287 => self.evaluate_2287(ctx),
669            2288 => self.evaluate_2288(ctx),
670            2307 => self.evaluate_2307(ctx),
671            2308 => self.evaluate_2308(ctx),
672            2309 => self.evaluate_2309(ctx),
673            2310 => self.evaluate_2310(ctx),
674            2311 => self.evaluate_2311(ctx),
675            2312 => self.evaluate_2312(ctx),
676            2313 => self.evaluate_2313(ctx),
677            2317 => self.evaluate_2317(ctx),
678            2318 => self.evaluate_2318(ctx),
679            2344 => self.evaluate_2344(ctx),
680            2350 => self.evaluate_2350(ctx),
681            2351 => self.evaluate_2351(ctx),
682            2352 => self.evaluate_2352(ctx),
683            2356 => self.evaluate_2356(ctx),
684            2357 => self.evaluate_2357(ctx),
685            2358 => self.evaluate_2358(ctx),
686            2359 => self.evaluate_2359(ctx),
687            2360 => self.evaluate_2360(ctx),
688            2361 => self.evaluate_2361(ctx),
689            _ => ConditionResult::Unknown,
690        }
691    }
692
693    fn is_external(&self, condition: u32) -> bool {
694        self.external_conditions.contains(&condition)
695    }
696    fn is_known(&self, condition: u32) -> bool {
697        matches!(
698            condition,
699            1 | 4
700                | 5
701                | 6
702                | 7
703                | 8
704                | 9
705                | 10
706                | 11
707                | 12
708                | 13
709                | 14
710                | 15
711                | 16
712                | 17
713                | 18
714                | 19
715                | 20
716                | 21
717                | 22
718                | 23
719                | 24
720                | 25
721                | 27
722                | 30
723                | 31
724                | 32
725                | 33
726                | 34
727                | 35
728                | 36
729                | 37
730                | 38
731                | 39
732                | 40
733                | 41
734                | 42
735                | 43
736                | 44
737                | 45
738                | 46
739                | 47
740                | 48
741                | 49
742                | 50
743                | 51
744                | 52
745                | 53
746                | 54
747                | 55
748                | 56
749                | 57
750                | 58
751                | 60
752                | 61
753                | 62
754                | 63
755                | 64
756                | 65
757                | 66
758                | 67
759                | 68
760                | 69
761                | 70
762                | 71
763                | 72
764                | 73
765                | 74
766                | 75
767                | 76
768                | 77
769                | 78
770                | 83
771                | 84
772                | 85
773                | 86
774                | 87
775                | 88
776                | 89
777                | 90
778                | 91
779                | 92
780                | 93
781                | 94
782                | 95
783                | 96
784                | 97
785                | 98
786                | 99
787                | 100
788                | 101
789                | 102
790                | 103
791                | 104
792                | 105
793                | 106
794                | 107
795                | 108
796                | 110
797                | 111
798                | 112
799                | 113
800                | 114
801                | 115
802                | 116
803                | 117
804                | 118
805                | 119
806                | 120
807                | 121
808                | 122
809                | 123
810                | 124
811                | 125
812                | 126
813                | 127
814                | 128
815                | 129
816                | 130
817                | 131
818                | 132
819                | 133
820                | 134
821                | 135
822                | 136
823                | 137
824                | 138
825                | 139
826                | 140
827                | 141
828                | 142
829                | 143
830                | 144
831                | 145
832                | 146
833                | 147
834                | 148
835                | 149
836                | 150
837                | 151
838                | 152
839                | 153
840                | 156
841                | 161
842                | 162
843                | 163
844                | 164
845                | 165
846                | 166
847                | 167
848                | 170
849                | 172
850                | 173
851                | 174
852                | 175
853                | 176
854                | 177
855                | 178
856                | 179
857                | 180
858                | 184
859                | 190
860                | 191
861                | 192
862                | 193
863                | 194
864                | 195
865                | 196
866                | 197
867                | 198
868                | 199
869                | 201
870                | 202
871                | 203
872                | 204
873                | 205
874                | 206
875                | 209
876                | 210
877                | 212
878                | 213
879                | 215
880                | 216
881                | 217
882                | 219
883                | 220
884                | 221
885                | 223
886                | 224
887                | 227
888                | 229
889                | 232
890                | 233
891                | 234
892                | 237
893                | 238
894                | 239
895                | 240
896                | 241
897                | 243
898                | 244
899                | 248
900                | 249
901                | 251
902                | 252
903                | 254
904                | 255
905                | 256
906                | 257
907                | 259
908                | 261
909                | 262
910                | 265
911                | 266
912                | 267
913                | 268
914                | 269
915                | 270
916                | 273
917                | 279
918                | 280
919                | 282
920                | 284
921                | 285
922                | 286
923                | 287
924                | 288
925                | 291
926                | 292
927                | 293
928                | 294
929                | 295
930                | 296
931                | 297
932                | 298
933                | 299
934                | 300
935                | 301
936                | 302
937                | 303
938                | 304
939                | 305
940                | 306
941                | 307
942                | 309
943                | 314
944                | 315
945                | 316
946                | 317
947                | 318
948                | 321
949                | 322
950                | 323
951                | 327
952                | 329
953                | 331
954                | 332
955                | 333
956                | 334
957                | 335
958                | 336
959                | 337
960                | 338
961                | 339
962                | 340
963                | 341
964                | 342
965                | 344
966                | 345
967                | 346
968                | 347
969                | 348
970                | 349
971                | 350
972                | 351
973                | 352
974                | 355
975                | 356
976                | 357
977                | 358
978                | 359
979                | 360
980                | 363
981                | 365
982                | 366
983                | 367
984                | 368
985                | 370
986                | 371
987                | 372
988                | 373
989                | 375
990                | 376
991                | 377
992                | 378
993                | 379
994                | 380
995                | 384
996                | 386
997                | 387
998                | 388
999                | 391
1000                | 392
1001                | 393
1002                | 394
1003                | 395
1004                | 396
1005                | 397
1006                | 398
1007                | 399
1008                | 401
1009                | 402
1010                | 403
1011                | 404
1012                | 405
1013                | 406
1014                | 407
1015                | 408
1016                | 409
1017                | 410
1018                | 411
1019                | 412
1020                | 413
1021                | 414
1022                | 415
1023                | 416
1024                | 417
1025                | 419
1026                | 420
1027                | 421
1028                | 425
1029                | 426
1030                | 427
1031                | 428
1032                | 429
1033                | 430
1034                | 431
1035                | 432
1036                | 433
1037                | 435
1038                | 436
1039                | 437
1040                | 438
1041                | 440
1042                | 441
1043                | 442
1044                | 444
1045                | 445
1046                | 446
1047                | 447
1048                | 448
1049                | 449
1050                | 450
1051                | 451
1052                | 452
1053                | 453
1054                | 454
1055                | 455
1056                | 456
1057                | 457
1058                | 458
1059                | 459
1060                | 460
1061                | 461
1062                | 462
1063                | 463
1064                | 465
1065                | 466
1066                | 467
1067                | 468
1068                | 469
1069                | 470
1070                | 471
1071                | 472
1072                | 473
1073                | 474
1074                | 475
1075                | 476
1076                | 477
1077                | 478
1078                | 479
1079                | 480
1080                | 481
1081                | 483
1082                | 484
1083                | 487
1084                | 489
1085                | 490
1086                | 491
1087                | 494
1088                | 500
1089                | 501
1090                | 502
1091                | 503
1092                | 504
1093                | 505
1094                | 506
1095                | 507
1096                | 508
1097                | 509
1098                | 510
1099                | 511
1100                | 512
1101                | 513
1102                | 514
1103                | 515
1104                | 516
1105                | 517
1106                | 518
1107                | 519
1108                | 520
1109                | 521
1110                | 522
1111                | 523
1112                | 524
1113                | 525
1114                | 526
1115                | 527
1116                | 528
1117                | 529
1118                | 530
1119                | 531
1120                | 532
1121                | 533
1122                | 555
1123                | 556
1124                | 558
1125                | 559
1126                | 563
1127                | 566
1128                | 567
1129                | 568
1130                | 569
1131                | 570
1132                | 572
1133                | 576
1134                | 577
1135                | 579
1136                | 580
1137                | 581
1138                | 586
1139                | 590
1140                | 594
1141                | 599
1142                | 601
1143                | 606
1144                | 609
1145                | 611
1146                | 614
1147                | 617
1148                | 618
1149                | 619
1150                | 621
1151                | 622
1152                | 623
1153                | 630
1154                | 631
1155                | 632
1156                | 637
1157                | 638
1158                | 639
1159                | 640
1160                | 641
1161                | 642
1162                | 643
1163                | 645
1164                | 646
1165                | 647
1166                | 648
1167                | 651
1168                | 653
1169                | 655
1170                | 659
1171                | 660
1172                | 662
1173                | 663
1174                | 664
1175                | 665
1176                | 667
1177                | 668
1178                | 670
1179                | 671
1180                | 672
1181                | 673
1182                | 674
1183                | 675
1184                | 677
1185                | 678
1186                | 679
1187                | 680
1188                | 682
1189                | 683
1190                | 684
1191                | 685
1192                | 687
1193                | 688
1194                | 689
1195                | 690
1196                | 693
1197                | 694
1198                | 695
1199                | 696
1200                | 698
1201                | 699
1202                | 700
1203                | 704
1204                | 705
1205                | 706
1206                | 707
1207                | 708
1208                | 709
1209                | 710
1210                | 711
1211                | 712
1212                | 713
1213                | 714
1214                | 715
1215                | 716
1216                | 717
1217                | 718
1218                | 719
1219                | 902
1220                | 910
1221                | 914
1222                | 922
1223                | 926
1224                | 930
1225                | 931
1226                | 932
1227                | 933
1228                | 937
1229                | 938
1230                | 939
1231                | 940
1232                | 942
1233                | 943
1234                | 946
1235                | 948
1236                | 950
1237                | 951
1238                | 952
1239                | 955
1240                | 957
1241                | 960
1242                | 961
1243                | 967
1244                | 2001
1245                | 2002
1246                | 2003
1247                | 2004
1248                | 2005
1249                | 2006
1250                | 2007
1251                | 2008
1252                | 2009
1253                | 2010
1254                | 2011
1255                | 2012
1256                | 2013
1257                | 2014
1258                | 2015
1259                | 2016
1260                | 2017
1261                | 2018
1262                | 2060
1263                | 2061
1264                | 2071
1265                | 2073
1266                | 2075
1267                | 2080
1268                | 2095
1269                | 2096
1270                | 2119
1271                | 2140
1272                | 2182
1273                | 2183
1274                | 2207
1275                | 2225
1276                | 2236
1277                | 2252
1278                | 2261
1279                | 2284
1280                | 2286
1281                | 2287
1282                | 2288
1283                | 2307
1284                | 2308
1285                | 2309
1286                | 2310
1287                | 2311
1288                | 2312
1289                | 2313
1290                | 2317
1291                | 2318
1292                | 2344
1293                | 2350
1294                | 2351
1295                | 2352
1296                | 2356
1297                | 2357
1298                | 2358
1299                | 2359
1300                | 2360
1301                | 2361
1302        )
1303    }
1304}
1305
1306impl UtilmdStromConditionEvaluatorFV2504 {
1307    /// [2006] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolge...
1308    // REVIEW: Counts SG8 instances with SEQ+Z01 where condition [199] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. Returns True if at least one qualifying parent exists (cardinality enforcement is partial — full 'exactly once' check requires knowing the current SG type). The recursive call self.evaluate(199, ctx) operates at message scope, which is the best available approximation. (medium confidence)
1309    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
1310        let nav = match ctx.navigator() {
1311            Some(n) => n,
1312            None => return ConditionResult::Unknown,
1313        };
1314        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1315        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1316        for i in 0..sg8_count {
1317            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1318            let is_z01 = seq_segs.iter().any(|s| {
1319                s.elements
1320                    .first()
1321                    .and_then(|e| e.first())
1322                    .is_some_and(|v| v == "Z01")
1323            });
1324            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1325                for seq in &seq_segs {
1326                    if seq
1327                        .elements
1328                        .first()
1329                        .and_then(|e| e.first())
1330                        .is_some_and(|v| v == "Z01")
1331                    {
1332                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1333                            if !zid.is_empty() {
1334                                qualifying_zeitraum_ids.push(zid.clone());
1335                            }
1336                        }
1337                    }
1338                }
1339            }
1340        }
1341        if qualifying_zeitraum_ids.is_empty() {
1342            return ConditionResult::Unknown;
1343        }
1344        ConditionResult::True
1345    }
1346
1347    /// [2007] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
1348    // REVIEW: Semantically identical to [2006] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation with no functional difference. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [199] is fulfilled. Recursive call self.evaluate(199, ctx) operates at message scope. (medium confidence)
1349    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
1350        let nav = match ctx.navigator() {
1351            Some(n) => n,
1352            None => return ConditionResult::Unknown,
1353        };
1354        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1355        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1356        for i in 0..sg8_count {
1357            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1358            let is_z01 = seq_segs.iter().any(|s| {
1359                s.elements
1360                    .first()
1361                    .and_then(|e| e.first())
1362                    .is_some_and(|v| v == "Z01")
1363            });
1364            if is_z01 && matches!(self.evaluate(199, ctx), ConditionResult::True) {
1365                for seq in &seq_segs {
1366                    if seq
1367                        .elements
1368                        .first()
1369                        .and_then(|e| e.first())
1370                        .is_some_and(|v| v == "Z01")
1371                    {
1372                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1373                            if !zid.is_empty() {
1374                                qualifying_zeitraum_ids.push(zid.clone());
1375                            }
1376                        }
1377                    }
1378                }
1379            }
1380        }
1381        if qualifying_zeitraum_ids.is_empty() {
1382            return ConditionResult::Unknown;
1383        }
1384        ConditionResult::True
1385    }
1386
1387    /// [2008] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolgen...
1388    // REVIEW: Same structure as [2006/2007] but references condition [89] instead of [199]. Counts qualifying SG8 SEQ+Z01 instances where condition [89] is fulfilled and collects their Zeitraum-IDs from SEQ elements[1][0]. The recursive self.evaluate(89, ctx) call operates at message scope. (medium confidence)
1389    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
1390        let nav = match ctx.navigator() {
1391            Some(n) => n,
1392            None => return ConditionResult::Unknown,
1393        };
1394        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1395        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1396        for i in 0..sg8_count {
1397            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1398            let is_z01 = seq_segs.iter().any(|s| {
1399                s.elements
1400                    .first()
1401                    .and_then(|e| e.first())
1402                    .is_some_and(|v| v == "Z01")
1403            });
1404            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1405                for seq in &seq_segs {
1406                    if seq
1407                        .elements
1408                        .first()
1409                        .and_then(|e| e.first())
1410                        .is_some_and(|v| v == "Z01")
1411                    {
1412                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1413                            if !zid.is_empty() {
1414                                qualifying_zeitraum_ids.push(zid.clone());
1415                            }
1416                        }
1417                    }
1418                }
1419            }
1420        }
1421        if qualifying_zeitraum_ids.is_empty() {
1422            return ConditionResult::Unknown;
1423        }
1424        ConditionResult::True
1425    }
1426
1427    /// [2009] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolg...
1428    // REVIEW: Semantically identical to [2008] — 'Bedingungen' (plural) vs 'Bedingung' (singular) is a grammatical variation. Same logic: collects qualifying SG8 SEQ+Z01 Zeitraum-IDs where condition [89] is fulfilled. Recursive call self.evaluate(89, ctx) operates at message scope. (medium confidence)
1429    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
1430        let nav = match ctx.navigator() {
1431            Some(n) => n,
1432            None => return ConditionResult::Unknown,
1433        };
1434        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1435        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
1436        for i in 0..sg8_count {
1437            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1438            let is_z01 = seq_segs.iter().any(|s| {
1439                s.elements
1440                    .first()
1441                    .and_then(|e| e.first())
1442                    .is_some_and(|v| v == "Z01")
1443            });
1444            if is_z01 && matches!(self.evaluate(89, ctx), ConditionResult::True) {
1445                for seq in &seq_segs {
1446                    if seq
1447                        .elements
1448                        .first()
1449                        .and_then(|e| e.first())
1450                        .is_some_and(|v| v == "Z01")
1451                    {
1452                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
1453                            if !zid.is_empty() {
1454                                qualifying_zeitraum_ids.push(zid.clone());
1455                            }
1456                        }
1457                    }
1458                }
1459            }
1460        }
1461        if qualifying_zeitraum_ids.is_empty() {
1462            return ConditionResult::Unknown;
1463        }
1464        ConditionResult::True
1465    }
1466
1467    /// [1] Wenn Aufteilung vorhanden
1468    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
1469        ctx.external.evaluate("message_splitting")
1470    }
1471
1472    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
1473    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
1474        ctx.external.evaluate("recipient_is_lf")
1475    }
1476
1477    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
1478    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
1479        ctx.external.evaluate("sender_is_lf")
1480    }
1481
1482    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
1483    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
1484        ctx.external.evaluate("recipient_role_check")
1485    }
1486
1487    /// [7] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) vorhanden
1488    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
1489        let sts_segs = ctx.find_segments("STS");
1490        let found = sts_segs.iter().any(|s| {
1491            s.elements
1492                .first()
1493                .and_then(|e| e.first())
1494                .is_some_and(|v| v == "7")
1495                && s.elements
1496                    .get(2)
1497                    .and_then(|e| e.first())
1498                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1499        });
1500        ConditionResult::from(found)
1501    }
1502
1503    /// [8] Wenn für Datenclearing erforderlich
1504    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
1505        ctx.external.evaluate("data_clearing_required")
1506    }
1507
1508    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
1509    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
1510        ctx.external.evaluate("sender_role_check")
1511    }
1512
1513    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
1514    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
1515        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
1516        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1517    }
1518
1519    /// [11] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) nicht vorhanden
1520    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        let sts_segs = ctx.find_segments("STS");
1522        let found = sts_segs.iter().any(|s| {
1523            s.elements
1524                .first()
1525                .and_then(|e| e.first())
1526                .is_some_and(|v| v == "7")
1527                && s.elements
1528                    .get(2)
1529                    .and_then(|e| e.first())
1530                    .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
1531        });
1532        ConditionResult::from(!found)
1533    }
1534
1535    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
1536    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
1537        let dtm_segs = ctx.find_segments("DTM");
1538        let found = dtm_segs.iter().any(|s| {
1539            s.elements
1540                .first()
1541                .and_then(|e| e.first())
1542                .is_some_and(|v| v == "471")
1543        });
1544        ConditionResult::from(!found)
1545    }
1546
1547    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
1548    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
1549        let sts_segs = ctx.find_segments("STS");
1550        let found = sts_segs.iter().any(|s| {
1551            s.elements
1552                .first()
1553                .and_then(|e| e.first())
1554                .is_some_and(|v| v == "E01")
1555                && s.elements
1556                    .get(2)
1557                    .and_then(|e| e.first())
1558                    .is_some_and(|v| ["Z01"].contains(&v.as_str()))
1559        });
1560        ConditionResult::from(!found)
1561    }
1562
1563    /// [14] Wenn Datum bekannt
1564    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
1565        ctx.external.evaluate("date_known")
1566    }
1567
1568    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
1569    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
1570        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1571        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1572        let found = seq_segs.iter().any(|s| {
1573            s.elements
1574                .first()
1575                .and_then(|e| e.first())
1576                .is_some_and(|v| ["Z98"].contains(&v.as_str()))
1577        });
1578        ConditionResult::from(found)
1579    }
1580
1581    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
1582    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
1583        let sts_segs = ctx.find_segments("STS");
1584        let found = sts_segs.iter().any(|s| {
1585            s.elements
1586                .first()
1587                .and_then(|e| e.first())
1588                .is_some_and(|v| v == "E01")
1589                && s.elements
1590                    .get(2)
1591                    .and_then(|e| e.first())
1592                    .is_some_and(|v| ["Z12"].contains(&v.as_str()))
1593        });
1594        ConditionResult::from(found)
1595    }
1596
1597    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
1598    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
1599        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1600        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
1601        let found = seq_segs.iter().any(|s| {
1602            s.elements
1603                .first()
1604                .and_then(|e| e.first())
1605                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
1606        });
1607        ConditionResult::from(found)
1608    }
1609
1610    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
1611    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
1612        let dtm_segs = ctx.find_segments("DTM");
1613        let found = dtm_segs.iter().any(|s| {
1614            s.elements
1615                .first()
1616                .and_then(|e| e.first())
1617                .is_some_and(|v| v == "93")
1618        });
1619        ConditionResult::from(!found)
1620    }
1621
1622    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
1623    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
1624        // HAND-EDITED (#336): the SEQ alone says nothing about a Prognose auf Basis von Werten; the CCI of one of its
1625        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1626        if ctx.navigator().is_none() {
1627            let seq = ctx
1628                .find_segments("SEQ")
1629                .iter()
1630                .any(|s| ["Z01", "Z98"].contains(&s.get_element(0)));
1631            return ConditionResult::from(
1632                seq && sg4_instances::has_cci(
1633                    &ctx.find_segments("CCI")
1634                        .into_iter()
1635                        .cloned()
1636                        .collect::<Vec<_>>(),
1637                    "",
1638                    "ZC0",
1639                ),
1640            );
1641        }
1642        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01", "Z98"], "", "ZC0"))
1643    }
1644
1645    /// [20] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1646    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1647        // HAND-EDITED (#336): the SEQ alone is every MaLo, consumption or not; the CCI of one of its
1648        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
1649        if ctx.navigator().is_none() {
1650            let seq = ctx
1651                .find_segments("SEQ")
1652                .iter()
1653                .any(|s| ["Z01"].contains(&s.get_element(0)));
1654            return ConditionResult::from(
1655                seq && sg4_instances::has_cci(
1656                    &ctx.find_segments("CCI")
1657                        .into_iter()
1658                        .cloned()
1659                        .collect::<Vec<_>>(),
1660                    "Z30",
1661                    "Z07",
1662                ),
1663            );
1664        }
1665        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z07"))
1666    }
1667
1668    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
1669    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
1670        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1671        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1672        let found = cci_segs.iter().any(|s| {
1673            s.elements
1674                .get(2)
1675                .and_then(|e| e.first())
1676                .is_some_and(|v| v == "ZA6")
1677        });
1678        ConditionResult::from(found)
1679    }
1680
1681    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
1682    // HAND-EDITED: the value is one of the Zeitraum-IDs (DE1156) of the SG6
1683    // RFF+Z47/Z48/Z49 of its SG4. The generated code compared those IDs with
1684    // the SG8 SEQ DE1050 and never looked at the value.
1685    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
1686        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
1687            return ConditionResult::Unknown;
1688        };
1689        let found = ["Z47", "Z48", "Z49"].iter().any(|q| {
1690            ctx.scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, q)
1691                .iter()
1692                .any(|s| s.get_component(0, 2) == value)
1693        });
1694        ConditionResult::from(found)
1695    }
1696
1697    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
1698    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
1699        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1700        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1701        let found = sts_segs.iter().any(|s| {
1702            s.elements
1703                .first()
1704                .and_then(|e| e.first())
1705                .is_some_and(|v| v == "E01")
1706                && s.elements
1707                    .get(2)
1708                    .and_then(|e| e.first())
1709                    .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
1710        });
1711        ConditionResult::from(found)
1712    }
1713
1714    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
1715    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
1716        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1717        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1718        let found = sts_segs.iter().any(|s| {
1719            s.elements
1720                .first()
1721                .and_then(|e| e.first())
1722                .is_some_and(|v| v == "E01")
1723                && s.elements
1724                    .get(2)
1725                    .and_then(|e| e.first())
1726                    .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
1727        });
1728        ConditionResult::from(found)
1729    }
1730
1731    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
1732    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
1733        // TODO: implement
1734        ConditionResult::Unknown
1735    }
1736
1737    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
1738    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
1739        let dtm_segs = ctx.find_segments("DTM");
1740        let found = dtm_segs.iter().any(|s| {
1741            s.elements
1742                .first()
1743                .and_then(|e| e.first())
1744                .is_some_and(|v| v == "Z01")
1745                && s.elements
1746                    .first()
1747                    .and_then(|e| e.get(1))
1748                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| ['T', 'R'].contains(&c)))
1749        });
1750        ConditionResult::from(found)
1751    }
1752
1753    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
1754    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
1755        // Antwort auf Aktivierung — depends on PID context
1756        ctx.external.evaluate("activation_response")
1757    }
1758
1759    /// [31] Wenn eine Korrektur erfolgt
1760    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
1761        ctx.external.evaluate("correction_in_progress")
1762    }
1763
1764    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
1765    // HAND-EDITED: counts the SG8 SEQ+Z78 (Referenz auf die
1766    // Lokationsbündelstruktur) of this SG4. The generated code counted every
1767    // SEQ of the message, so any 55043 held it (reported by mako-twin).
1768    // HAND-EDITED (#392): 55177/55180 give the reference as SEQ+ZC7
1769    // (erwartet), others as ZC8 (im System vorhanden) or ZD5 (informativ);
1770    // counting Z78 alone made [32] false there and refused the numbers of
1771    // several bundles. One bundle may stand once as erwartet and once as im
1772    // System, so the count is per variant.
1773    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
1774        let several = ["Z78", "ZC7", "ZC8", "ZD5"].iter().any(|code| {
1775            ctx.scoped_find_segments_in_with_qualifier("SG4", "SEQ", 0, code)
1776                .len()
1777                > 1
1778        });
1779        ConditionResult::from(several)
1780    }
1781
1782    /// [33] Wenn in einer SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6 \"Produkte zur Bestellung / Änderung von Date...
1783    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
1784        ctx.external.evaluate("code_list_membership_check")
1785    }
1786
1787    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
1788    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
1789        // Antwort auf Deaktivierung — depends on PID context
1790        ctx.external.evaluate("deactivation_response")
1791    }
1792
1793    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
1794    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
1795        let dtm_segs = ctx.find_segments("DTM");
1796        let found = dtm_segs.iter().any(|s| {
1797            s.elements
1798                .first()
1799                .and_then(|e| e.first())
1800                .is_some_and(|v| v == "Z01")
1801                && s.elements
1802                    .first()
1803                    .and_then(|e| e.get(1))
1804                    .is_some_and(|v| v.chars().nth(3).is_some_and(|c| c == 'T'))
1805        });
1806        ConditionResult::from(found)
1807    }
1808
1809    /// [36] Wenn in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTI...
1810    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
1811        ctx.external.evaluate("code_list_membership_check")
1812    }
1813
1814    /// [37] Wenn Anmeldung/ Änderung befristet
1815    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
1816        // Same check as [10]: STS element[4] is befristete Anmeldung code
1817        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
1818    }
1819
1820    /// [38] Es sind nur die Code der Produkteigenschaft zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel  6.1 \"Anmeldung ei...
1821    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
1822        ctx.external.evaluate("code_list_membership_check")
1823    }
1824
1825    /// [39] Wenn in derselben SG8 SEQ+Z79 m PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTIL...
1826    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
1827        ctx.external.evaluate("code_list_membership_check")
1828    }
1829
1830    /// [40] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeld...
1831    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
1832        ctx.external.evaluate("code_list_membership_check")
1833    }
1834
1835    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
1836    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
1837        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
1838        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
1839        if seq_z79.is_empty() {
1840            return ConditionResult::Unknown;
1841        }
1842        // Check if SEQ has a value in C286.D1050 (elements[1][0])
1843        let found = seq_z79.iter().any(|s| {
1844            s.elements
1845                .get(1)
1846                .and_then(|e| e.first())
1847                .is_some_and(|v| !v.is_empty())
1848        });
1849        ConditionResult::from(found)
1850    }
1851
1852    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
1853    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
1854        let seq_segs = ctx.find_segments("SEQ");
1855        let zh0_count = seq_segs
1856            .iter()
1857            .filter(|s| {
1858                s.elements
1859                    .first()
1860                    .and_then(|e| e.first())
1861                    .is_some_and(|v| v == "ZH0")
1862            })
1863            .count();
1864        ConditionResult::from(zh0_count > 1)
1865    }
1866
1867    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
1868    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
1869        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1870        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1871        let found = sts_segs.iter().any(|s| {
1872            s.elements
1873                .first()
1874                .and_then(|e| e.first())
1875                .is_some_and(|v| v == "E01")
1876                && s.elements
1877                    .get(2)
1878                    .and_then(|e| e.first())
1879                    .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
1880        });
1881        ConditionResult::from(found)
1882    }
1883
1884    /// [44] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: "Gültige Daten", "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN DE1154 ((Referenz Vo...
1885    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1886        // Hinweis: Zeitraum-ID aus passendem SG6 RFF+Z49/Z53 der Anfragenachricht eintragen — informational data population rule
1887        ConditionResult::True
1888    }
1889
1890    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
1891    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
1892        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
1893        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
1894        let found = cci_segs.iter().any(|s| {
1895            s.elements
1896                .first()
1897                .and_then(|e| e.first())
1898                .is_some_and(|v| v == "Z01")
1899                && s.elements
1900                    .get(2)
1901                    .and_then(|e| e.first())
1902                    .is_some_and(|v| v == "Z82")
1903        });
1904        ConditionResult::from(found)
1905    }
1906
1907    /// [46] Wenn ID für Objekt vergeben wurde
1908    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
1909        // TODO: implement
1910        ConditionResult::Unknown
1911    }
1912
1913    /// [47] Wenn SG10 CCI+15++Z21 (Summenzeitreihentyp) CAV+DBA/ VZR/ EGS/ LGS/ SLS/ SES/ TLS/ TES/ BIL/ BIP/ BIT/ GAL/ GAP/ GAT/ GEL/ GEP/ GET/ SOL/ SOP/ SOT/ WFL/ WFP/ WFT/ WNL/ WNP/ WNT vorhanden
1914    // REVIEW: Iterates all SG8 instances and their SG10 children looking for CCI with elements[0][0]=="15" and elements[2][0]=="Z21" (Zeitreihentyp), then checks if the same SG10 has a CAV with elements[0][0] in the Summenzeitreihentyp code list. Uses navigator for parent-child group traversal. (medium confidence)
1915    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
1916        const CODES: &[&str] = &[
1917            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
1918            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
1919            "WNP", "WNT",
1920        ];
1921        let nav = match ctx.navigator {
1922            Some(n) => n,
1923            None => return ConditionResult::Unknown,
1924        };
1925        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1926        for i in 0..sg8_count {
1927            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1928            for j in 0..sg10_count {
1929                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1930                let has_cci_15_z21 = ccis.iter().any(|s| {
1931                    s.elements
1932                        .first()
1933                        .and_then(|e: &Vec<String>| e.first())
1934                        .is_some_and(|v: &String| v == "15")
1935                        && s.elements
1936                            .get(2)
1937                            .and_then(|e: &Vec<String>| e.first())
1938                            .is_some_and(|v: &String| v == "Z21")
1939                });
1940                if has_cci_15_z21 {
1941                    let cavs =
1942                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1943                    if cavs.iter().any(|s| {
1944                        s.elements
1945                            .first()
1946                            .and_then(|e: &Vec<String>| e.first())
1947                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
1948                    }) {
1949                        return ConditionResult::True;
1950                    }
1951                }
1952            }
1953        }
1954        ConditionResult::False
1955    }
1956
1957    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
1958    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
1959        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1960        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1961        let found = sts_segs.iter().any(|s| {
1962            s.elements
1963                .first()
1964                .and_then(|e| e.first())
1965                .is_some_and(|v| v == "E01")
1966                && s.elements
1967                    .get(2)
1968                    .and_then(|e| e.first())
1969                    .is_some_and(|v| ["A99"].contains(&v.as_str()))
1970        });
1971        ConditionResult::from(found)
1972    }
1973
1974    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
1975    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
1976        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
1977        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
1978        let found = sts_segs.iter().any(|s| {
1979            s.elements
1980                .first()
1981                .and_then(|e| e.first())
1982                .is_some_and(|v| v == "E01")
1983                && s.elements
1984                    .get(2)
1985                    .and_then(|e| e.first())
1986                    .is_some_and(|v| ["E14"].contains(&v.as_str()))
1987        });
1988        ConditionResult::from(found)
1989    }
1990
1991    /// [50] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) mit derselben Zeitraum-ID im DE1050 auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Infor...
1992    // REVIEW: Collects (Zeitraum-ID from SEQ.elements[1][0], Marktlok-ID from sibling RFF+Z18.elements[0][1]) for all SG8 instances with SEQ+Z98 and SEQ+ZE7. Returns True if any Z98 group has no ZE7 group matching on both values. Uses collect_group_values with instance-index correlation via HashMap, assuming each SG8 has at most one SEQ (guaranteed as entry segment) and the zip ordering is stable. (medium confidence)
1993    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
1994        use std::collections::HashMap;
1995
1996        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
1997        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
1998        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
1999        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
2000
2001        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
2002        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
2003        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
2004            rff_by_instance
2005                .entry(*idx)
2006                .or_default()
2007                .push((q.clone(), id.clone()));
2008        }
2009
2010        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
2011        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
2012        for (idx, z) in &seq_zeitraum {
2013            seq_zeitraum_map.insert(*idx, z.clone());
2014        }
2015
2016        let z98_data: Vec<(String, String)> = seq_quals
2017            .iter()
2018            .filter(|(_, q)| q == "Z98")
2019            .map(|(idx, _)| {
2020                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2021                let marktlok = rff_by_instance
2022                    .get(idx)
2023                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2024                    .map(|(_, id)| id.clone())
2025                    .unwrap_or_default();
2026                (zeitraum, marktlok)
2027            })
2028            .collect();
2029
2030        if z98_data.is_empty() {
2031            return ConditionResult::Unknown;
2032        }
2033
2034        let ze7_data: Vec<(String, String)> = seq_quals
2035            .iter()
2036            .filter(|(_, q)| q == "ZE7")
2037            .map(|(idx, _)| {
2038                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
2039                let marktlok = rff_by_instance
2040                    .get(idx)
2041                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
2042                    .map(|(_, id)| id.clone())
2043                    .unwrap_or_default();
2044                (zeitraum, marktlok)
2045            })
2046            .collect();
2047
2048        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
2049            !ze7_data
2050                .iter()
2051                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
2052        }))
2053    }
2054
2055    /// [51] Wenn die Marktlokations-ID im SG8 RFF+Z18 derselben SG8 SEQ+Z98 (Informative Daten der Marktlokation) mit derselben Zeitraum-ID im DE1050 nicht auf ein SG8 SEQ+Z78 (Referenz auf die Lokationsbünde...
2056    // REVIEW: Condition is True when no Z98 SG8 (Informative Daten der Marktlokation) references — via matching Zeitraum-ID in SEQ.C286.DE1050 — a Z78 SG8 (Referenz auf Lokationsbündelstruktur) that carries the pauschal bundle code 9992000000018 in RFF+Z31.DE1154. Requires navigator for cross-SG8 Zeitraum-ID correlation. (medium confidence)
2057    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
2058        // HAND-EDITED: "derselben SG8 SEQ+Z98 mit derselben Zeitraum-ID … nicht auf ein SG8 SEQ+Z78" — compared within the enclosing SG4 when the scope
2059        // reaches an SG8; the message as before otherwise.
2060        if let Some(scope) = ctx.enclosing_scope("SG8") {
2061            let Some((_, zeitraum)) =
2062                sg4_instances::seq_at(ctx, scope).filter(|(code, _)| code == "Z98")
2063            else {
2064                return ConditionResult::False;
2065            };
2066            let pauschal = sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
2067                sg4_instances::seq_at(ctx, p)
2068                    .is_some_and(|(c, z)| c == "Z78" && !z.is_empty() && z == zeitraum)
2069                    && sg4_instances::rff_values_at(ctx, p, "Z31")
2070                        .iter()
2071                        .any(|v| v.replace(' ', "") == "9992000000018")
2072            });
2073            return ConditionResult::from(!pauschal);
2074        }
2075        let nav = match ctx.navigator() {
2076            Some(n) => n,
2077            None => return ConditionResult::Unknown,
2078        };
2079        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2080        let pauschal_id = "9992000000018";
2081        // Collect Zeitraum-IDs from Z78 SG8s that carry the pauschal RFF+Z31 ID
2082        let mut pauschal_zeitraum_ids: Vec<String> = Vec::new();
2083        for i in 0..sg8_count {
2084            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2085            let has_z78 = seqs.iter().any(|s| {
2086                s.elements
2087                    .first()
2088                    .and_then(|e| e.first())
2089                    .is_some_and(|v| v == "Z78")
2090            });
2091            if !has_z78 {
2092                continue;
2093            }
2094            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
2095            let has_pauschal = rffs.iter().any(|s| {
2096                s.elements
2097                    .first()
2098                    .and_then(|e| e.first())
2099                    .is_some_and(|v| v == "Z31")
2100                    && s.elements
2101                        .first()
2102                        .and_then(|e| e.get(1))
2103                        .is_some_and(|v| v.replace(' ', "") == pauschal_id)
2104            });
2105            if has_pauschal {
2106                for seq in &seqs {
2107                    if seq
2108                        .elements
2109                        .first()
2110                        .and_then(|e| e.first())
2111                        .is_some_and(|v| v == "Z78")
2112                    {
2113                        if let Some(zid) = seq.elements.get(1).and_then(|e| e.first()) {
2114                            if !zid.is_empty() {
2115                                pauschal_zeitraum_ids.push(zid.clone());
2116                            }
2117                        }
2118                    }
2119                }
2120            }
2121        }
2122        // For each Z98 SG8, check whether its Zeitraum-ID (SEQ.C286.DE1050) maps to a pauschal Z78
2123        for i in 0..sg8_count {
2124            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2125            for seq in &seqs {
2126                if seq
2127                    .elements
2128                    .first()
2129                    .and_then(|e| e.first())
2130                    .is_some_and(|v| v == "Z98")
2131                {
2132                    let zid = seq
2133                        .elements
2134                        .get(1)
2135                        .and_then(|e| e.first())
2136                        .map(|s| s.as_str())
2137                        .unwrap_or("");
2138                    if !zid.is_empty() && pauschal_zeitraum_ids.iter().any(|z| z == zid) {
2139                        // Z98 SG8 references a pauschal Z78 → condition is False
2140                        return ConditionResult::False;
2141                    }
2142                }
2143            }
2144        }
2145        // No Z98 SG8 references the pauschal bundle → condition is True
2146        ConditionResult::True
2147    }
2148
2149    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
2150    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
2151        let cci_segs = ctx.find_segments("CCI");
2152        let found = cci_segs.iter().any(|s| {
2153            s.elements
2154                .first()
2155                .and_then(|e| e.first())
2156                .is_some_and(|v| v == "Z07")
2157                && s.elements
2158                    .get(2)
2159                    .and_then(|e| e.first())
2160                    .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
2161        });
2162        ConditionResult::from(found)
2163    }
2164
2165    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
2166    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
2167        // TODO: implement
2168        ConditionResult::Unknown
2169    }
2170
2171    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
2172    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
2173        // HAND-EDITED (#336): the SEQ alone is every MaLo, generation or not; the CCI of one of its
2174        // SG10s decides. Within the enclosing SG4 when the scope reaches one.
2175        if ctx.navigator().is_none() {
2176            let seq = ctx
2177                .find_segments("SEQ")
2178                .iter()
2179                .any(|s| ["Z01"].contains(&s.get_element(0)));
2180            return ConditionResult::from(
2181                seq && sg4_instances::has_cci(
2182                    &ctx.find_segments("CCI")
2183                        .into_iter()
2184                        .cloned()
2185                        .collect::<Vec<_>>(),
2186                    "Z30",
2187                    "Z06",
2188                ),
2189            );
2190        }
2191        ConditionResult::from(sg4_instances::seq_with_cci(ctx, &["Z01"], "Z30", "Z06"))
2192    }
2193
2194    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
2195    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
2196        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2197        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2198        let found = sts_segs.iter().any(|s| {
2199            s.elements
2200                .first()
2201                .and_then(|e| e.first())
2202                .is_some_and(|v| v == "E01")
2203                && s.elements
2204                    .get(2)
2205                    .and_then(|e| e.first())
2206                    .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
2207        });
2208        ConditionResult::from(found)
2209    }
2210
2211    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
2212    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
2213        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
2214        // Zustimmung codes start with A0x (A01-A07 are approval codes)
2215        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2216        let found = sts_segs.iter().any(|s| {
2217            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
2218                [
2219                    "A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10", "A11",
2220                    "A12", "A13", "A14", "A15", "A16", "A17", "A25",
2221                ]
2222                .contains(&v.as_str())
2223            })
2224        });
2225        ConditionResult::from(found)
2226    }
2227
2228    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
2229    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
2230        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2231        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
2232        let found = cci_segs.iter().any(|s| {
2233            s.elements
2234                .first()
2235                .and_then(|e| e.first())
2236                .is_some_and(|v| v == "Z45")
2237                && s.elements
2238                    .get(2)
2239                    .and_then(|e| e.first())
2240                    .is_some_and(|v| v == "ZD9")
2241        });
2242        ConditionResult::from(found)
2243    }
2244
2245    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
2246    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
2247        // HAND-EDITED: "in diesem CCI" — the CCI the field is in (its own segment
2248        // in tree validation), else the enclosing SG10's CCI.
2249        let has_293 =
2250            |elements: &[Vec<String>]| elements.iter().any(|e| e.iter().any(|v| v == "293"));
2251        if let Some(own) = ctx.resolved_segment.filter(|_| {
2252            ctx.scoped_find_segments_in("SG10", "CCI")
2253                .iter()
2254                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
2255        }) {
2256            return ConditionResult::from(has_293(own));
2257        }
2258        ConditionResult::from(
2259            ctx.scoped_find_segments_in("SG10", "CCI")
2260                .iter()
2261                .any(|s| has_293(&s.elements)),
2262        )
2263    }
2264
2265    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
2266    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
2267        let bgm_segs = ctx.find_segments("BGM");
2268        let found = bgm_segs.iter().any(|s| {
2269            s.elements
2270                .first()
2271                .and_then(|e| e.first())
2272                .is_some_and(|v| v == "Z90")
2273        });
2274        ConditionResult::from(found)
2275    }
2276
2277    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
2278    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
2279        let bgm_segs = ctx.find_segments("BGM");
2280        let found = bgm_segs.iter().any(|s| {
2281            s.elements
2282                .first()
2283                .and_then(|e| e.first())
2284                .is_some_and(|v| v == "Z89")
2285        });
2286        ConditionResult::from(found)
2287    }
2288
2289    /// [62] Wenn mehr als eine SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit einer Gruppen-/Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), ...
2290    // REVIEW: Iterates SG8 instances within each SG4, finds those with SEQ+Z45 and a non-empty Zeitraum-ID in elements[1][0]. Checks if PIA+Z02 in the same SG8 has a product code (elements[1][0]) beginning with 1-08-1 through 1-08-5. Groups by Zeitraum-ID and checks if any ID appears more than once. Requires navigator for per-instance group segment access; returns Unknown without one. (medium confidence)
2291    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
2292        let nav = match ctx.navigator {
2293            Some(n) => n,
2294            None => return ConditionResult::Unknown,
2295        };
2296        use std::collections::HashMap;
2297        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
2298        let sg4_count = nav.group_instance_count(&["SG4"]);
2299        for sg4_idx in 0..sg4_count {
2300            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
2301            for sg8_idx in 0..sg8_count {
2302                let seqs =
2303                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
2304                let zeitraum_opt = seqs
2305                    .iter()
2306                    .filter(|s| {
2307                        s.elements
2308                            .first()
2309                            .and_then(|e: &Vec<String>| e.first())
2310                            .is_some_and(|v: &String| v == "Z45")
2311                    })
2312                    .filter_map(|s| {
2313                        s.elements
2314                            .get(1)
2315                            .and_then(|e: &Vec<String>| e.first())
2316                            .filter(|v| !v.is_empty())
2317                            .cloned()
2318                    })
2319                    .next();
2320                let Some(zid) = zeitraum_opt else { continue };
2321                let pias =
2322                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
2323                let has_qualifying_pia = pias.iter().any(|s| {
2324                    s.elements
2325                        .first()
2326                        .and_then(|e: &Vec<String>| e.first())
2327                        .is_some_and(|v: &String| v == "Z02")
2328                        && s.elements
2329                            .get(1)
2330                            .and_then(|e: &Vec<String>| e.first())
2331                            .map(|code| {
2332                                code.starts_with("1-08-1")
2333                                    || code.starts_with("1-08-2")
2334                                    || code.starts_with("1-08-3")
2335                                    || code.starts_with("1-08-4")
2336                                    || code.starts_with("1-08-5")
2337                            })
2338                            .unwrap_or(false)
2339                });
2340                if has_qualifying_pia {
2341                    *count_by_zid.entry(zid).or_insert(0) += 1;
2342                }
2343            }
2344        }
2345        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
2346    }
2347
2348    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
2349    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
2350        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2351        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2352        let found = sts_segs.iter().any(|s| {
2353            s.elements
2354                .first()
2355                .and_then(|e| e.first())
2356                .is_some_and(|v| v == "E01")
2357                && s.elements
2358                    .get(2)
2359                    .and_then(|e| e.first())
2360                    .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
2361        });
2362        ConditionResult::from(found)
2363    }
2364
2365    /// [64] Wenn mehr als eine SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) mit einer Gruppen-/ Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5...
2366    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
2367        // Check: more than one SG8 SEQ+ZE1 has PIA+Z02 with code starting with 1-08-1/2/3/4/5
2368        let pia_segs = ctx.find_segments("PIA");
2369        let count = pia_segs
2370            .iter()
2371            .filter(|s| {
2372                s.elements
2373                    .first()
2374                    .and_then(|e| e.first())
2375                    .is_some_and(|v| v == "Z02")
2376                    && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2377                        ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2378                            .iter()
2379                            .any(|p| v.starts_with(p))
2380                    })
2381            })
2382            .count();
2383        ConditionResult::from(count > 1)
2384    }
2385
2386    /// [65] Wenn in derselben SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, v...
2387    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
2388        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2389        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
2390        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
2391        let found = pia_segs.iter().any(|s| {
2392            s.elements
2393                .first()
2394                .and_then(|e| e.first())
2395                .is_some_and(|v| v == "Z02")
2396                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
2397                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"]
2398                        .iter()
2399                        .any(|p| v.starts_with(p))
2400                })
2401        });
2402        ConditionResult::from(found)
2403    }
2404
2405    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
2406    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
2407        let seq_segs = ctx.find_segments("SEQ");
2408        let count = seq_segs
2409            .iter()
2410            .filter(|s| {
2411                s.elements
2412                    .first()
2413                    .and_then(|e| e.first())
2414                    .is_some_and(|v| v == "ZH0")
2415            })
2416            .count();
2417        ConditionResult::from(count > 1)
2418    }
2419
2420    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpaketes CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
2421    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
2422        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
2423        let cav_segs = ctx.find_segments("CAV");
2424        let found = cav_segs.iter().any(|s| {
2425            s.elements
2426                .first()
2427                .and_then(|e| e.first())
2428                .is_some_and(|v| v == "ZH9")
2429                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2430                && s.elements
2431                    .first()
2432                    .and_then(|e| e.get(3))
2433                    .is_some_and(|v| v == "9991000002933")
2434        });
2435        ConditionResult::from(!found)
2436    }
2437
2438    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
2439    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
2440        let seq_segs = ctx.find_segments("SEQ");
2441        let count = seq_segs
2442            .iter()
2443            .filter(|s| {
2444                s.elements
2445                    .first()
2446                    .and_then(|e| e.first())
2447                    .is_some_and(|v| v == "ZH0")
2448            })
2449            .count();
2450        ConditionResult::from(count > 2)
2451    }
2452
2453    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
2454    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
2455        let seq_segs = ctx.find_segments("SEQ");
2456        let count = seq_segs
2457            .iter()
2458            .filter(|s| {
2459                s.elements
2460                    .first()
2461                    .and_then(|e| e.first())
2462                    .is_some_and(|v| v == "ZH0")
2463            })
2464            .count();
2465        ConditionResult::from(count > 3)
2466    }
2467
2468    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
2469    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
2470        let seq_segs = ctx.find_segments("SEQ");
2471        let count = seq_segs
2472            .iter()
2473            .filter(|s| {
2474                s.elements
2475                    .first()
2476                    .and_then(|e| e.first())
2477                    .is_some_and(|v| v == "ZH0")
2478            })
2479            .count();
2480        ConditionResult::from(count == 5)
2481    }
2482
2483    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
2484    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
2485        // Antwort auf Zuordnung — depends on PID context
2486        ctx.external.evaluate("assignment_response")
2487    }
2488
2489    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
2490    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
2491        // Antwort auf Beendigung der Zuordnung — depends on PID context
2492        ctx.external.evaluate("assignment_end_response")
2493    }
2494
2495    /// [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist, der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist
2496    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
2497    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
2498    // Regenerating this function should preserve the shape, but re-verify
2499    // against the committed body if generate-conditions is run.
2500    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
2501        // [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der
2502        // Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist,
2503        // der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist.
2504        //
2505        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
2506        // narrows to the enclosing SG8 instance; otherwise falls back to
2507        // message-wide find_segments. Matches the AHB wording
2508        // "in derselben SG8 im PIA DE7140 ein Code...".
2509        use crate::eval::CodelistColumn;
2510        let any_match = ctx.scoped_find_segments_in("SG8", "PIA").iter().any(|s| {
2511            let is_z02 = s
2512                .elements
2513                .first()
2514                .and_then(|e| e.first())
2515                .is_some_and(|v| v == "Z02");
2516            if !is_z02 {
2517                return false;
2518            }
2519            let code = match s.elements.get(1).and_then(|e| e.first()) {
2520                Some(c) if !c.is_empty() => c,
2521                _ => return false,
2522            };
2523            ctx.codelist
2524                .flag(code, CodelistColumn::UtilmdPreisangabe)
2525                .unwrap_or(false)
2526        });
2527        ConditionResult::from(any_match)
2528    }
2529
2530    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
2531    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
2532        let seq_segs = ctx.find_segments("SEQ");
2533        let has_seq = seq_segs.iter().any(|s| {
2534            s.elements
2535                .first()
2536                .and_then(|e| e.first())
2537                .is_some_and(|v| v == "Z59")
2538        });
2539        let pia_segs = ctx.find_segments("PIA");
2540        let has_pia = pia_segs.iter().any(|s| {
2541            s.elements
2542                .first()
2543                .and_then(|e| e.first())
2544                .is_some_and(|v| v == "5")
2545        });
2546        ConditionResult::from(has_seq && has_pia)
2547    }
2548
2549    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
2550    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
2551        ctx.external.evaluate("code_list_membership_check")
2552    }
2553
2554    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
2555    // REVIEW: SEQ+Z49 is 'Abgerechnete Daten der Bilanzierungsgebietssummenzeitreihe' — the only SEQ code in the reference that explicitly names a Bilanzierungsgebiet-level Summenzeitreihe. Checking its presence is the most direct mapping of the condition text. A broader interpretation involving CAV codes (Bezeichnung der Summenzeitreihe) is possible but those codes are not fully enumerated in the provided reference. (medium confidence)
2556    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
2557        ctx.has_qualifier("SEQ", 0, "Z49")
2558    }
2559
2560    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
2561    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
2562        let seq_segs = ctx.find_segments("SEQ");
2563        let found = seq_segs.iter().any(|s| {
2564            s.elements
2565                .first()
2566                .and_then(|e| e.first())
2567                .is_some_and(|v| ["Z03"].contains(&v.as_str()))
2568        });
2569        ConditionResult::from(!found)
2570    }
2571
2572    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
2573    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
2574        let sts_segs = ctx.find_segments("STS");
2575        let found = sts_segs.iter().any(|s| {
2576            s.elements
2577                .first()
2578                .and_then(|e| e.first())
2579                .is_some_and(|v| v == "7")
2580                && s.elements
2581                    .get(2)
2582                    .and_then(|e| e.first())
2583                    .is_some_and(|v| ["E02"].contains(&v.as_str()))
2584        });
2585        ConditionResult::from(!found)
2586    }
2587
2588    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
2589    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
2590        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
2591        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
2592        let found = sts_segs.iter().any(|s| {
2593            s.elements
2594                .first()
2595                .and_then(|e| e.first())
2596                .is_some_and(|v| v == "E01")
2597                && s.elements
2598                    .get(2)
2599                    .and_then(|e| e.first())
2600                    .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
2601        });
2602        ConditionResult::from(found)
2603    }
2604
2605    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
2606    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
2607        let sts_segs = ctx.find_segments("STS");
2608        let found = sts_segs.iter().any(|s| {
2609            s.elements
2610                .first()
2611                .and_then(|e| e.first())
2612                .is_some_and(|v| v == "E01")
2613                && s.elements
2614                    .get(2)
2615                    .and_then(|e| e.first())
2616                    .is_some_and(|v| ["A57"].contains(&v.as_str()))
2617        });
2618        ConditionResult::from(found)
2619    }
2620
2621    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
2622    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
2623        let dtm_segs = ctx.find_segments("DTM");
2624        let found = dtm_segs.iter().any(|s| {
2625            s.elements
2626                .first()
2627                .and_then(|e| e.first())
2628                .is_some_and(|v| v == "Z01")
2629                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
2630                    v.chars()
2631                        .nth(3)
2632                        .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
2633                })
2634        });
2635        ConditionResult::from(found)
2636    }
2637
2638    /// [86] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der...
2639    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
2640        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" for this SG6's RFF+Z50 — compared within the enclosing SG4 when the scope
2641        // reaches an SG6; the message as before otherwise.
2642        if let Some(scope) = ctx.enclosing_scope("SG6") {
2643            let refs = sg4_instances::rff_values_at(ctx, scope, "Z50");
2644            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
2645                sg4_instances::has_cci(s, "", "ZA6")
2646            }));
2647        }
2648        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
2649        ctx.groups_share_qualified_value(
2650            "RFF",
2651            0,
2652            "Z50",
2653            0,
2654            2,
2655            &["SG4", "SG6"],
2656            "RFF",
2657            0,
2658            2,
2659            &["SG4", "SG8"],
2660        )
2661    }
2662
2663    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
2664    // HAND-EDITED: judges the DTM+157 value in German local time. The generated
2665    // code looked for raw `DD=01`/`HHMM=0000` in any DTM, which a correct UTC
2666    // value (`202610312300+00`) never has (mako-twin, 55018).
2667    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
2668        use chrono::{Datelike, Timelike};
2669        let value = match ctx.resolved_value.filter(|v| !v.is_empty()) {
2670            Some(v) => v.to_string(),
2671            None => {
2672                let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2673                match dtm157.first() {
2674                    Some(dtm) => dtm.get_component(0, 1).to_string(),
2675                    None => return ConditionResult::Unknown,
2676                }
2677            }
2678        };
2679        match german_local_time(&value) {
2680            Some(t) => ConditionResult::from(t.day() == 1 && t.hour() == 0 && t.minute() == 0),
2681            None => ConditionResult::False,
2682        }
2683    }
2684
2685    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
2686    // HAND-EDITED: compares the value; the generated code only checked that a
2687    // DTM+157 exists.
2688    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
2689        let Some(value) = ctx.resolved_value.filter(|v| !v.is_empty()) else {
2690            return ConditionResult::Unknown;
2691        };
2692        let dtm157 = ctx.scoped_find_segments_in_with_qualifier("SG4", "DTM", 0, "157");
2693        match dtm157.first() {
2694            Some(dtm) => ConditionResult::from(dtm.get_component(0, 1) == value),
2695            None => ConditionResult::Unknown,
2696        }
2697    }
2698
2699    /// [89] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in diesem SG8, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+...
2700    // REVIEW: Checks whether any SG8 instance contains an SG10 child with CCI at elements[2][0]=ZA6 and CAV at elements[0][0]=E14. The condition text requires the Zeitraum-ID in DE1050 to match 'this SG8', but a per-group evaluation context is not available in the current API — the Zeitraum-ID cross-reference is therefore not enforced and is instead approximated as a message-wide check. The CCI qualifier position (elements[2][0]) is confirmed by the MIG reference for CCI segments in SG10. (medium confidence)
2701    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
2702        let nav = match ctx.navigator {
2703            Some(n) => n,
2704            None => {
2705                return ctx.filtered_parent_child_has_qualifier(
2706                    &["SG4", "SG8"],
2707                    "SEQ",
2708                    0,
2709                    "Z01",
2710                    "SG10",
2711                    "CCI",
2712                    2,
2713                    "ZA6",
2714                )
2715            }
2716        };
2717        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2718        for i in 0..sg8_count {
2719            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2720            for j in 0..sg10_count {
2721                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2722                let has_cci_za6 = ccis.iter().any(|s| {
2723                    s.elements
2724                        .get(2)
2725                        .and_then(|e: &Vec<String>| e.first())
2726                        .is_some_and(|v: &String| v == "ZA6")
2727                });
2728                if !has_cci_za6 {
2729                    continue;
2730                }
2731                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2732                if cavs.iter().any(|s| {
2733                    s.elements
2734                        .first()
2735                        .and_then(|e: &Vec<String>| e.first())
2736                        .is_some_and(|v: &String| v == "E14")
2737                }) {
2738                    return ConditionResult::True;
2739                }
2740            }
2741        }
2742        ConditionResult::False
2743    }
2744
2745    /// [90] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2746    // REVIEW: Within each SG8, iterates all SG10 child instances. For each SG10 that has a CCI with elements[2][0]=ZA6, checks the CAV elements[0][0] value. Tracks whether E14 and E02 have both been seen within the same SG8. Returns True when both are found in a single SG8, satisfying the requirement that both Prognosegrundlage options appear 'in dieser SG8'. (medium confidence)
2747    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
2748        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
2749        // scope reaches one; the message as before otherwise.
2750        if let Some(_scope) = ctx.enclosing_scope("SG8") {
2751            return ConditionResult::from(
2752                ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2753                    .unwrap_or_default()
2754                    .iter()
2755                    .any(|segs| {
2756                        segs.iter()
2757                            .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2758                            && segs
2759                                .iter()
2760                                .any(|s| s.id == "CAV" && s.get_element(0) == "E14")
2761                    })
2762                    && ctx
2763                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
2764                        .unwrap_or_default()
2765                        .iter()
2766                        .any(|segs| {
2767                            segs.iter()
2768                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
2769                                && segs
2770                                    .iter()
2771                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
2772                        }),
2773            );
2774        }
2775        let nav = match ctx.navigator {
2776            Some(n) => n,
2777            None => return ConditionResult::Unknown,
2778        };
2779        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2780        for i in 0..sg8_count {
2781            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2782            let mut has_za6_e14 = false;
2783            let mut has_za6_e02 = false;
2784            for j in 0..sg10_count {
2785                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2786                let has_cci_za6 = ccis.iter().any(|s| {
2787                    s.elements
2788                        .get(2)
2789                        .and_then(|e: &Vec<String>| e.first())
2790                        .is_some_and(|v: &String| v == "ZA6")
2791                });
2792                if !has_cci_za6 {
2793                    continue;
2794                }
2795                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2796                for cav in &cavs {
2797                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
2798                        if code == "E14" {
2799                            has_za6_e14 = true;
2800                        }
2801                        if code == "E02" {
2802                            has_za6_e02 = true;
2803                        }
2804                    }
2805                }
2806            }
2807            if has_za6_e14 && has_za6_e02 {
2808                return ConditionResult::True;
2809            }
2810        }
2811        ConditionResult::False
2812    }
2813
2814    /// [91] Wenn nicht SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/ TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
2815    // REVIEW: Logical negation of condition 90. Condition 91 is explicitly defined as 'Wenn NICHT (CCI+++ZA6 einmal mit CAV+E14 und einmal mit CAV+E02 in dieser SG8 vorhanden)', which is the exact complement of condition 90. Preserving Unknown propagation handles the no-navigator fallback case correctly. (medium confidence)
2816    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
2817        match self.evaluate_90(ctx) {
2818            ConditionResult::True => ConditionResult::False,
2819            ConditionResult::False => ConditionResult::True,
2820            ConditionResult::Unknown => ConditionResult::Unknown,
2821        }
2822    }
2823
2824    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
2825    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
2826        // TODO: implement
2827        ConditionResult::Unknown
2828    }
2829
2830    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
2831    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
2832        // TODO: implement
2833        ConditionResult::Unknown
2834    }
2835
2836    /// [94] Wenn ein Segment innerhalb der SG vorhanden
2837    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
2838        // Structural: segment exists within SG (always true for valid messages)
2839        ConditionResult::True
2840    }
2841
2842    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
2843    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
2844        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
2845        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
2846        let has_cci = cci_segs.iter().any(|s| {
2847            s.elements
2848                .first()
2849                .and_then(|e| e.first())
2850                .is_some_and(|v| v == "Z17")
2851        });
2852        let cav_segs = ctx.scoped_find_segments_in("SG10", "CAV");
2853        let has_cav = cav_segs.iter().any(|s| {
2854            s.elements
2855                .first()
2856                .and_then(|e| e.first())
2857                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
2858        });
2859        ConditionResult::from(has_cci && has_cav)
2860    }
2861
2862    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
2863    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
2864        let sts_segs = ctx.find_segments("STS");
2865        let found = sts_segs.iter().any(|s| {
2866            s.elements
2867                .first()
2868                .and_then(|e| e.first())
2869                .is_some_and(|v| v == "7")
2870                && s.elements
2871                    .get(3)
2872                    .and_then(|e| e.first())
2873                    .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
2874        });
2875        ConditionResult::from(found)
2876    }
2877
2878    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
2879    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
2880        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
2881        let cav_segs = ctx.find_segments("CAV");
2882        let found = cav_segs.iter().any(|s| {
2883            s.elements
2884                .first()
2885                .and_then(|e| e.first())
2886                .is_some_and(|v| v == "ZH9")
2887                // HAND-EDITED: the code stands in DE7110, C889 component 3.
2888                && s.elements
2889                    .first()
2890                    .and_then(|e| e.get(3))
2891                    .is_some_and(|v| v == "9991000002420")
2892        });
2893        ConditionResult::from(found)
2894    }
2895
2896    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
2897    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
2898        ctx.external.evaluate("sender_role_check")
2899    }
2900
2901    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
2902    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
2903        ctx.external.evaluate("sender_role_check")
2904    }
2905
2906    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
2907    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
2908        let cav_segs = ctx.find_segments("CAV");
2909        let found = cav_segs.iter().any(|s| {
2910            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2911                [
2912                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
2913                ]
2914                .contains(&v.as_str())
2915            })
2916        });
2917        ConditionResult::from(found)
2918    }
2919
2920    /// [101] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14/ Z36 (TLP/ TEP/ TEP mit Ref.messung) in dieser SG8 vorhanden
2921    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
2922        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2923        sg10_has(ctx, true, |segs| {
2924            sg4_instances::has_cci(segs, "", "ZA6")
2925                && (sg4_instances::has_cav(segs, "E14") || sg4_instances::has_cav(segs, "Z36"))
2926        })
2927    }
2928
2929    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
2930    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
2931        // HAND-EDITED: the CCI and the CAV of one SG10 of this SG8.
2932        sg10_has(ctx, true, |segs| {
2933            sg4_instances::has_cci(segs, "", "ZA6") && sg4_instances::has_cav(segs, "Z36")
2934        })
2935    }
2936
2937    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
2938    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
2939        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2940        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2941        let found = qty_segs.iter().any(|s| {
2942            s.elements
2943                .first()
2944                .and_then(|e| e.first())
2945                .is_some_and(|v| v == "265")
2946        });
2947        ConditionResult::from(found)
2948    }
2949
2950    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
2951    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
2952        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2953        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2954        let found = qty_segs.iter().any(|s| {
2955            s.elements
2956                .first()
2957                .and_then(|e| e.first())
2958                .is_some_and(|v| v == "Z08")
2959        });
2960        ConditionResult::from(found)
2961    }
2962
2963    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
2964    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
2965        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2966        let qty_segs = ctx.scoped_find_segments_in("SG8", "QTY");
2967        let found = qty_segs.iter().any(|s| {
2968            s.elements
2969                .first()
2970                .and_then(|e| e.first())
2971                .is_some_and(|v| v == "Z10")
2972        });
2973        ConditionResult::from(found)
2974    }
2975
2976    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
2977    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
2978        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2979        // Check: in SG8 with SEQ+Z01, SG10 has CCI+++ZA6 and CAV+E02
2980        ctx.this_group_parent_child_has_qualifier(
2981            &["SG4", "SG8"],
2982            "SEQ",
2983            0,
2984            "Z01",
2985            "SG10",
2986            "CAV",
2987            0,
2988            "E02",
2989        )
2990    }
2991
2992    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
2993    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
2994        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
2995        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
2996        let found = cav_segs.iter().any(|s| {
2997            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
2998                [
2999                    "SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP",
3000                ]
3001                .contains(&v.as_str())
3002            })
3003        });
3004        ConditionResult::from(found)
3005    }
3006
3007    /// [108] Wenn in derselben SG8 (Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, vorhanden
3008    // HAND-EDITED — do not let generate-conditions regenerate this function
3009    // without first confirming prompt.rs B6 SCOPE RULES section is in the
3010    // system prompt. Regeneration without that guidance would emit
3011    // ctx.has_qualifier (message-wide) and silently reintroduce the
3012    // per-instance bug this function fixes.
3013    // See: docs/plans/2026-04-23-ahb-codelist-and-per-instance-plan.md B7.
3014    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
3015        // [108] Wenn in derselben SG8 ein PIA+Z02 mit einem Code aus
3016        // 1-08-1/2/3/4/5 vorhanden. Scope-aware: narrows to the enclosing
3017        // SG8 instance when GroupScope is set; falls back to message-wide.
3018        const PREFIXES: &[&str] = &["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"];
3019        let found = ctx
3020            .scoped_find_segments_in("SG8", "PIA")
3021            .iter()
3022            .filter(|s| {
3023                s.elements
3024                    .first()
3025                    .and_then(|e| e.first())
3026                    .is_some_and(|q| q == "Z02")
3027            })
3028            .any(|s| {
3029                s.elements
3030                    .get(1)
3031                    .and_then(|e| e.first())
3032                    .is_some_and(|v| PREFIXES.iter().any(|p| v.starts_with(p)))
3033            });
3034        ConditionResult::from(found)
3035    }
3036
3037    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
3038    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
3039        let cav_segs = ctx.find_segments("CAV");
3040        let found = cav_segs.iter().any(|s| {
3041            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
3042                [
3043                    "LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL",
3044                ]
3045                .contains(&v.as_str())
3046            })
3047        });
3048        ConditionResult::from(found)
3049    }
3050
3051    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3052    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3053        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3054        // scope reaches one; the message as before otherwise.
3055        if let Some(scope) = ctx.enclosing_scope("SG8") {
3056            return ConditionResult::from(
3057                ctx.find_segments_at(scope, "SEQ")
3058                    .iter()
3059                    .any(|s| matches!(s.get_element(0), "Z59"))
3060                    && !ctx
3061                        .find_segments_at(scope, "PIA")
3062                        .iter()
3063                        .any(|s| s.get_element(0) == "5"),
3064            );
3065        }
3066        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3067    }
3068
3069    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3070    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3071        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3072        // scope reaches one; the message as before otherwise.
3073        if let Some(scope) = ctx.enclosing_scope("SG8") {
3074            return ConditionResult::from(
3075                ctx.find_segments_at(scope, "SEQ")
3076                    .iter()
3077                    .any(|s| matches!(s.get_element(0), "Z59"))
3078                    && !ctx
3079                        .find_segments_at(scope, "CCI")
3080                        .iter()
3081                        .any(|s| s.get_element(0) == "11"),
3082            );
3083        }
3084        let result = ctx.filtered_parent_child_has_qualifier(
3085            &["SG4", "SG8"],
3086            "SEQ",
3087            0,
3088            "Z59",
3089            "SG10",
3090            "CCI",
3091            0,
3092            "11",
3093        );
3094        match result {
3095            ConditionResult::True => ConditionResult::False,
3096            ConditionResult::False => ConditionResult::True,
3097            ConditionResult::Unknown => ConditionResult::Unknown,
3098        }
3099    }
3100
3101    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3102    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3103        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3104        ctx.external.evaluate("counting_time_order_response")
3105    }
3106
3107    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3108    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3109        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3110        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3111        spannungsebene_in_this_sg10(ctx, "E06")
3112    }
3113
3114    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3115    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3116        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3117        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3118        spannungsebene_in_this_sg10(ctx, "E05")
3119    }
3120
3121    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3122    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3123        // HAND-EDITED (#379): the CCI is `CCI+++E03` (C240 DE7037), and "in
3124        // dieser SG" is the SG10 judged — not a CCI with E03 in DE7059 anywhere.
3125        spannungsebene_in_this_sg10(ctx, "E04")
3126    }
3127
3128    /// [117] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
3129    // REVIEW: Full condition requires: (1) current SG8 has RFF+Z18=X, (2) another SG8 with SEQ+Z01 also has RFF+Z18=X, (3) that SG8 has SG10 with CCI elements[2][0]='ZA6' AND CAV elements[0][0]='E14'. Implementation simplifies to checking whether any SG8 has SG10 with both CCI+++ZA6 and CAV+E14 co-occurring in the same SG10 instance. The cross-SG RFF+Z18 value correlation and SEQ+Z01 parent check are omitted because the navigator API only exposes child-group segments (SG10 within SG8), not SG8's own entry segments. Medium confidence due to this structural limitation. (medium confidence)
3130    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3131        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (TLP/TEP) — compared within the enclosing SG4 when the scope
3132        // reaches an SG8; the message as before otherwise.
3133        if let Some(scope) = ctx.enclosing_scope("SG8") {
3134            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
3135            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
3136                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E14")
3137            }));
3138        }
3139        let nav = match ctx.navigator {
3140            Some(n) => n,
3141            None => return ConditionResult::Unknown,
3142        };
3143        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3144        for i in 0..sg8_count {
3145            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3146            for j in 0..sg10_count {
3147                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3148                let has_cci_za6 = ccis.iter().any(|s| {
3149                    s.elements
3150                        .get(2)
3151                        .and_then(|e: &Vec<String>| e.first())
3152                        .is_some_and(|v: &String| v == "ZA6")
3153                });
3154                if has_cci_za6 {
3155                    let cavs =
3156                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3157                    if cavs.iter().any(|s| {
3158                        s.elements
3159                            .first()
3160                            .and_then(|e: &Vec<String>| e.first())
3161                            .is_some_and(|v: &String| v == "E14")
3162                    }) {
3163                        return ConditionResult::True;
3164                    }
3165                }
3166            }
3167        }
3168        ConditionResult::False
3169    }
3170
3171    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3172    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3173        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3174        // scope reaches one; the message as before otherwise.
3175        if let Some(scope) = ctx.enclosing_scope("SG8") {
3176            return ConditionResult::from(
3177                ctx.find_segments_at(scope, "SEQ")
3178                    .iter()
3179                    .any(|s| matches!(s.get_element(0), "Z98"))
3180                    && ctx
3181                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3182                        .unwrap_or_default()
3183                        .iter()
3184                        .any(|segs| {
3185                            segs.iter()
3186                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "ZA6")
3187                                && segs
3188                                    .iter()
3189                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E02")
3190                        }),
3191            );
3192        }
3193        let has_cci = ctx.filtered_parent_child_has_qualifier(
3194            &["SG4", "SG8"],
3195            "SEQ",
3196            0,
3197            "Z98",
3198            "SG10",
3199            "CCI",
3200            2,
3201            "ZA6",
3202        );
3203        if has_cci != ConditionResult::True {
3204            return has_cci;
3205        }
3206        ctx.filtered_parent_child_has_qualifier(
3207            &["SG4", "SG8"],
3208            "SEQ",
3209            0,
3210            "Z98",
3211            "SG10",
3212            "CAV",
3213            0,
3214            "E02",
3215        )
3216    }
3217
3218    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3219    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3220        // HAND-EDITED (#337): the CAV+E02 of the SG10 CCI+++ZA6, as [265] asks
3221        // for its CAV+Z36 — not any CAV+E02 (CCI+++ZC0 CAV+E02 is no profile).
3222        let Some(blocks) = sg8_blocks(ctx) else {
3223            return ConditionResult::Unknown;
3224        };
3225        let found = blocks
3226            .iter()
3227            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
3228            .any(|b| {
3229                b.sg10.iter().any(|(ccis, cavs)| {
3230                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
3231                        && cavs.iter().any(|c| component(c, 0, 0) == Some("E02"))
3232                })
3233            });
3234        ConditionResult::from(found)
3235    }
3236
3237    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3238    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3239        // TODO: implement
3240        ConditionResult::Unknown
3241    }
3242
3243    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3244    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3245        let z03 =
3246            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3247        if z03 == ConditionResult::True {
3248            return ConditionResult::True;
3249        }
3250        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3251    }
3252
3253    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3254    // REVIEW: Uses filtered_parent_child_has_qualifier to find SG8 instances where SEQ+Z98 has an SG10 child with CCI at elements[2]='E03' (Spannungsebene der Marktlokation). Then confirms CAV+E06 (Niederspannung) exists in an SG8 group. The CAV check is group-scoped rather than strictly same-SG10-instance because the navigator API does not directly expose parent-group (SG8) segment access alongside child-group (SG10) checks in one call. (medium confidence)
3255    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3256        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
3257        // scope reaches one; the message as before otherwise.
3258        if let Some(scope) = ctx.enclosing_scope("SG8") {
3259            return ConditionResult::from(
3260                ctx.find_segments_at(scope, "SEQ")
3261                    .iter()
3262                    .any(|s| matches!(s.get_element(0), "Z98"))
3263                    && ctx
3264                        .enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"])
3265                        .unwrap_or_default()
3266                        .iter()
3267                        .any(|segs| {
3268                            segs.iter()
3269                                .any(|s| s.id == "CCI" && s.get_component(2, 0) == "E03")
3270                                && segs
3271                                    .iter()
3272                                    .any(|s| s.id == "CAV" && s.get_element(0) == "E06")
3273                        }),
3274            );
3275        }
3276        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3277        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3278            &["SG4", "SG8"],
3279            "SEQ",
3280            0,
3281            "Z98",
3282            "SG10",
3283            "CCI",
3284            2,
3285            "E03",
3286        );
3287        if has_cci_e03 != ConditionResult::True {
3288            return has_cci_e03;
3289        }
3290        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3291        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3292    }
3293
3294    /// [123] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit dem SG8 RFF+MG (Gerätenummer des Zählers) auf die gleiche Identifikation/Nummer des Gerätes...
3295    // REVIEW: Scans segments linearly to collect RFF+MG device IDs from each SG8 group with SEQ+Z20 (OBIS-Daten der Zähleinrichtung/Smartmeter-Gateway). A new SEQ starts a new SG8 context; RFF+MG within that context records the device number. Returns True if any device ID appears in 2+ different SG8 groups (indicating multiple OBIS datasets reference the same device). Linear scan is used because the navigator API does not expose a direct method to retrieve segments within a parent SG8 instance by absolute index. (medium confidence)
3296    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3297        struct Sg8Z20Entry {
3298            mg_id: Option<String>,
3299        }
3300        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3301        let mut current: Option<Sg8Z20Entry> = None;
3302        for seg in ctx.segments {
3303            match seg.id.as_str() {
3304                "SEQ" => {
3305                    if let Some(entry) = current.take() {
3306                        entries.push(entry);
3307                    }
3308                    if seg
3309                        .elements
3310                        .first()
3311                        .and_then(|e: &Vec<String>| e.first())
3312                        .is_some_and(|v: &String| v == "Z20")
3313                    {
3314                        current = Some(Sg8Z20Entry { mg_id: None });
3315                    }
3316                }
3317                "RFF" if current.is_some() => {
3318                    if seg
3319                        .elements
3320                        .first()
3321                        .and_then(|e: &Vec<String>| e.first())
3322                        .is_some_and(|v: &String| v == "MG")
3323                    {
3324                        if let Some(ref mut entry) = current {
3325                            if entry.mg_id.is_none() {
3326                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3327                            }
3328                        }
3329                    }
3330                }
3331                _ => {}
3332            }
3333        }
3334        if let Some(entry) = current {
3335            entries.push(entry);
3336        }
3337        let ids: Vec<&str> = entries
3338            .iter()
3339            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3340            .collect();
3341        for i in 0..ids.len() {
3342            for j in (i + 1)..ids.len() {
3343                if ids[i] == ids[j] {
3344                    return ConditionResult::True;
3345                }
3346            }
3347        }
3348        ConditionResult::False
3349    }
3350
3351    /// [124] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit derselben Zeitraum-ID aus dem DE1050 dieser SG8 mit dem SG8 RFF+MG (Gerätenummer des Zählers...
3352    // REVIEW: Extends condition 123 by also requiring that the matching SG8 groups share the same Zeitraum-ID (from SEQ.elements[1][0] = DE1050 / C286.d1050). Both the Zeitraum-ID and the RFF+MG device number must be identical across at least two different SG8 instances with SEQ+Z20 for the condition to be True. (medium confidence)
3353    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3354        // HAND-EDITED: "noch mindestens eine weitere SG8 SEQ+Z20 mit derselben Zeitraum-ID … RFF+MG" — compared within the enclosing SG4 when the scope
3355        // reaches an SG8; the message as before otherwise.
3356        if let Some(scope) = ctx.enclosing_scope("SG8") {
3357            let Some((_, zeitraum)) = sg4_instances::seq_at(ctx, scope)
3358                .filter(|(code, zeitraum)| code == "Z20" && !zeitraum.is_empty())
3359            else {
3360                return ConditionResult::False;
3361            };
3362            let meters = sg4_instances::rff_values_at(ctx, scope, "MG");
3363            return ConditionResult::from(sg4_instances::other_sg8s(ctx, scope).iter().any(|p| {
3364                sg4_instances::seq_at(ctx, p).is_some_and(|(c, z)| c == "Z20" && z == zeitraum)
3365                    && sg4_instances::rff_values_at(ctx, p, "MG")
3366                        .iter()
3367                        .any(|m| meters.contains(m))
3368            }));
3369        }
3370        struct Sg8Z20Entry {
3371            zeitraum_id: String,
3372            mg_id: Option<String>,
3373        }
3374        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3375        let mut current: Option<Sg8Z20Entry> = None;
3376        for seg in ctx.segments {
3377            match seg.id.as_str() {
3378                "SEQ" => {
3379                    if let Some(entry) = current.take() {
3380                        entries.push(entry);
3381                    }
3382                    if seg
3383                        .elements
3384                        .first()
3385                        .and_then(|e: &Vec<String>| e.first())
3386                        .is_some_and(|v: &String| v == "Z20")
3387                    {
3388                        let zeitraum_id = seg
3389                            .elements
3390                            .get(1)
3391                            .and_then(|e: &Vec<String>| e.first())
3392                            .cloned()
3393                            .unwrap_or_default();
3394                        current = Some(Sg8Z20Entry {
3395                            zeitraum_id,
3396                            mg_id: None,
3397                        });
3398                    }
3399                }
3400                "RFF" if current.is_some() => {
3401                    if seg
3402                        .elements
3403                        .first()
3404                        .and_then(|e: &Vec<String>| e.first())
3405                        .is_some_and(|v: &String| v == "MG")
3406                    {
3407                        if let Some(ref mut entry) = current {
3408                            if entry.mg_id.is_none() {
3409                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3410                            }
3411                        }
3412                    }
3413                }
3414                _ => {}
3415            }
3416        }
3417        if let Some(entry) = current {
3418            entries.push(entry);
3419        }
3420        for i in 0..entries.len() {
3421            for j in (i + 1)..entries.len() {
3422                let ei = &entries[i];
3423                let ej = &entries[j];
3424                if !ei.zeitraum_id.is_empty()
3425                    && ei.zeitraum_id == ej.zeitraum_id
3426                    && ei.mg_id.is_some()
3427                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
3428                    && ei.mg_id == ej.mg_id
3429                {
3430                    return ConditionResult::True;
3431                }
3432            }
3433        }
3434        ConditionResult::False
3435    }
3436
3437    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
3438    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
3439        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3440        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
3441        let found = seq_segs.iter().any(|s| {
3442            s.elements
3443                .first()
3444                .and_then(|e| e.first())
3445                .is_some_and(|v| ["Z76", "ZC5", "ZC6"].contains(&v.as_str()))
3446        });
3447        ConditionResult::from(found)
3448    }
3449
3450    /// [126] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) ergibt. Bedeutet: Das erste SG6 RFF+Z49/ Z53...
3451    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z49/Z53
3452    // of its SG4, counting from 1. The generated code only checked that one
3453    // existed, so any value passed.
3454    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
3455        repetition_ordinal(ctx, &["Z49", "Z53"])
3456    }
3457
3458    /// [127] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) ergibt. Bedeutet: Das erste SG6  R...
3459    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z48/Z55
3460    // of its SG4, counting from 1. The generated code only checked that one
3461    // existed, so any value passed.
3462    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
3463        repetition_ordinal(ctx, &["Z48", "Z55"])
3464    }
3465
3466    /// [128] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) ergibt. Bede...
3467    // HAND-EDITED: the value is the position of this RFF among the SG6 RFF+Z47/Z54
3468    // of its SG4, counting from 1. The generated code only checked that one
3469    // existed, so any value passed.
3470    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
3471        repetition_ordinal(ctx, &["Z47", "Z54"])
3472    }
3473
3474    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
3475    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
3476        // Check: all LOC+Z16 DE3225 values are identical
3477        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
3478        if loc_segs.len() <= 1 {
3479            return ConditionResult::True;
3480        }
3481        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
3482        let all_same = loc_segs
3483            .iter()
3484            .all(|s| s.elements.get(1).and_then(|e| e.first()) == first_val);
3485        ConditionResult::from(all_same)
3486    }
3487
3488    /// [130] Wenn an Messlokation vorhanden
3489    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
3490        // TODO: implement
3491        ConditionResult::Unknown
3492    }
3493
3494    /// [131] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID \"1\" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+1...
3495    // HAND-EDITED: compares the dates. The generated code only checked that
3496    // some RFF carries Zeitraum-ID 1 (mako-twin, 55643).
3497    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
3498        first_period_start_vs_next_day(ctx, |start, next_day| start <= next_day)
3499    }
3500
3501    /// [132] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID "1" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+137...
3502    // HAND-EDITED: judges the dates in German local time, in this SG6. The
3503    // generated code compared raw digits with `…0000`, which a correct UTC
3504    // value (`202610222200+00`) never has.
3505    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
3506        first_period_start_vs_next_day(ctx, |start, next_day| start == next_day)
3507    }
3508
3509    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
3510    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
3511        // TODO: implement
3512        ConditionResult::Unknown
3513    }
3514
3515    /// [134] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) mit der Zeitraum ID "1" im DE1156 ist, muss das Datum ...
3516    // 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)
3517    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
3518        let nav = match ctx.navigator() {
3519            Some(n) => n,
3520            None => return ConditionResult::Unknown,
3521        };
3522        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3523        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z48/Z55 SG6
3524        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3525        for i in 0..sg6_count {
3526            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3527            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3528            let maybe_zid = rffs.iter().find_map(|s| {
3529                let qual = s
3530                    .elements
3531                    .first()
3532                    .and_then(|e| e.first())
3533                    .map(|v| v.as_str())?;
3534                if qual == "Z48" || qual == "Z55" {
3535                    s.elements
3536                        .first()
3537                        .and_then(|e| e.get(2))
3538                        .and_then(|v| v.parse::<u32>().ok())
3539                } else {
3540                    None
3541                }
3542            });
3543            if let Some(zid) = maybe_zid {
3544                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3545                let z25 = dtms.iter().find_map(|s| {
3546                    if s.elements
3547                        .first()
3548                        .and_then(|e| e.first())
3549                        .map(|v| v.as_str())
3550                        == Some("Z25")
3551                    {
3552                        s.elements.first().and_then(|e| e.get(1)).cloned()
3553                    } else {
3554                        None
3555                    }
3556                });
3557                let z26 = dtms.iter().find_map(|s| {
3558                    if s.elements
3559                        .first()
3560                        .and_then(|e| e.first())
3561                        .map(|v| v.as_str())
3562                        == Some("Z26")
3563                    {
3564                        s.elements.first().and_then(|e| e.get(1)).cloned()
3565                    } else {
3566                        None
3567                    }
3568                });
3569                slices.push((zid, z25, z26));
3570            }
3571        }
3572        if slices.is_empty() {
3573            return ConditionResult::Unknown;
3574        }
3575        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3576        for i in 0..slices.len() {
3577            let (zid, ref z25, _) = slices[i];
3578            if zid <= 1 {
3579                continue;
3580            }
3581            let prev_id = zid - 1;
3582            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3583                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3584                    (Some(curr), Some(prev_end)) => {
3585                        if curr != prev_end {
3586                            return ConditionResult::False;
3587                        }
3588                    }
3589                    _ => return ConditionResult::Unknown,
3590                },
3591                None => return ConditionResult::Unknown,
3592            }
3593        }
3594        ConditionResult::True
3595    }
3596
3597    /// [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...
3598    // 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)
3599    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
3600        let nav = match ctx.navigator() {
3601            Some(n) => n,
3602            None => return ConditionResult::Unknown,
3603        };
3604        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
3605        // Collect (zeitraum_id, dtm_z25_value, dtm_z26_value) for every Z47/Z54 SG6
3606        let mut slices: Vec<(u32, Option<String>, Option<String>)> = Vec::new();
3607        for i in 0..sg6_count {
3608            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
3609            // RFF C506: [0][0]=qualifier, [0][1]=ref-id, [0][2]=Zeitraum-ID
3610            let maybe_zid = rffs.iter().find_map(|s| {
3611                let qual = s
3612                    .elements
3613                    .first()
3614                    .and_then(|e| e.first())
3615                    .map(|v| v.as_str())?;
3616                if qual == "Z47" || qual == "Z54" {
3617                    s.elements
3618                        .first()
3619                        .and_then(|e| e.get(2))
3620                        .and_then(|v| v.parse::<u32>().ok())
3621                } else {
3622                    None
3623                }
3624            });
3625            if let Some(zid) = maybe_zid {
3626                let dtms = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
3627                let z25 = dtms.iter().find_map(|s| {
3628                    if s.elements
3629                        .first()
3630                        .and_then(|e| e.first())
3631                        .map(|v| v.as_str())
3632                        == Some("Z25")
3633                    {
3634                        s.elements.first().and_then(|e| e.get(1)).cloned()
3635                    } else {
3636                        None
3637                    }
3638                });
3639                let z26 = dtms.iter().find_map(|s| {
3640                    if s.elements
3641                        .first()
3642                        .and_then(|e| e.first())
3643                        .map(|v| v.as_str())
3644                        == Some("Z26")
3645                    {
3646                        s.elements.first().and_then(|e| e.get(1)).cloned()
3647                    } else {
3648                        None
3649                    }
3650                });
3651                slices.push((zid, z25, z26));
3652            }
3653        }
3654        if slices.is_empty() {
3655            return ConditionResult::Unknown;
3656        }
3657        // For each slice with Zeitraum-ID > 1: its DTM+Z25 must equal DTM+Z26 of the previous slice
3658        for i in 0..slices.len() {
3659            let (zid, ref z25, _) = slices[i];
3660            if zid <= 1 {
3661                continue;
3662            }
3663            let prev_id = zid - 1;
3664            match slices.iter().find(|(id, _, _)| *id == prev_id) {
3665                Some((_, _, prev_z26)) => match (z25, prev_z26) {
3666                    (Some(curr), Some(prev_end)) => {
3667                        if curr != prev_end {
3668                            return ConditionResult::False;
3669                        }
3670                    }
3671                    _ => return ConditionResult::Unknown,
3672                },
3673                None => return ConditionResult::Unknown,
3674            }
3675        }
3676        ConditionResult::True
3677    }
3678
3679    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) vorhanden
3680    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
3681        // HAND-EDITED: the CCI and the CAV of one SG10.
3682        sg10_has(ctx, false, |segs| {
3683            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "EHZ")
3684        })
3685    }
3686
3687    /// [137] Nicht bei Neuanlage
3688    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
3689        // Check: not a Neuanlage (new registration) process
3690        // STS+7 with transaktionsgrund containing Neuanlage indicators
3691        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3692        let is_neuanlage = sts_segs.iter().any(|s| {
3693            s.elements
3694                .get(2)
3695                .and_then(|e| e.first())
3696                .is_some_and(|v| v == "E01" || v == "E03")
3697        });
3698        ConditionResult::from(!is_neuanlage)
3699    }
3700
3701    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
3702    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
3703        let qty_segs = ctx.find_segments("QTY");
3704        let found = qty_segs.iter().any(|s| {
3705            s.elements
3706                .first()
3707                .and_then(|e| e.first())
3708                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
3709        });
3710        ConditionResult::from(found)
3711    }
3712
3713    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) nicht vorhanden
3714    // 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)
3715    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
3716        let nav = match ctx.navigator {
3717            Some(n) => n,
3718            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
3719        };
3720        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3721        for i in 0..sg8_count {
3722            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3723            for j in 0..sg10_count {
3724                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3725                if cavs.iter().any(|s| {
3726                    s.elements
3727                        .first()
3728                        .and_then(|e: &Vec<String>| e.first())
3729                        .is_some_and(|v: &String| v == "IVA")
3730                }) {
3731                    return ConditionResult::False;
3732                }
3733            }
3734        }
3735        ConditionResult::True
3736    }
3737
3738    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
3739    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
3740        let qty_segs = ctx.find_segments("QTY");
3741        let found = qty_segs.iter().any(|s| {
3742            s.elements
3743                .first()
3744                .and_then(|e| e.first())
3745                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
3746        });
3747        ConditionResult::from(found)
3748    }
3749
3750    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
3751    // HAND-EDITED (#392, reported by mako-twin): the CAVs of this SG8 (the
3752    // Wandler whose Faktor is asked for), not of the message: an MUW beside
3753    // an MIW in another SG8 was asked for a Faktor.
3754    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
3755        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
3756        let found = cav_segs.iter().any(|s| {
3757            s.elements
3758                .first()
3759                .and_then(|e| e.first())
3760                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
3761        });
3762        ConditionResult::from(found)
3763    }
3764
3765    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
3766    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
3767        let pia_segs = ctx.find_segments("PIA");
3768        let found = pia_segs.iter().any(|s| {
3769            s.elements
3770                .first()
3771                .and_then(|e| e.first())
3772                .is_some_and(|v| v == "Z01")
3773                && s.elements
3774                    .get(1)
3775                    .and_then(|e| e.get(1))
3776                    .is_some_and(|v| ["Z04", "Z05"].contains(&v.as_str()))
3777        });
3778        ConditionResult::from(found)
3779    }
3780
3781    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3782    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
3783        let sts_segs = ctx.find_segments("STS");
3784        let found = sts_segs.iter().any(|s| {
3785            s.elements
3786                .first()
3787                .and_then(|e| e.first())
3788                .is_some_and(|v| v == "7")
3789                && s.elements
3790                    .get(2)
3791                    .and_then(|e| e.first())
3792                    .is_some_and(|v| v == "ZX3")
3793        });
3794        ConditionResult::from(found)
3795    }
3796
3797    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
3798    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
3799        let sts_segs = ctx.find_segments("STS");
3800        let found = sts_segs.iter().any(|s| {
3801            s.elements
3802                .first()
3803                .and_then(|e| e.first())
3804                .is_some_and(|v| v == "7")
3805                && s.elements
3806                    .get(2)
3807                    .and_then(|e| e.first())
3808                    .is_some_and(|v| v == "ZAN")
3809        });
3810        ConditionResult::from(found)
3811    }
3812
3813    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3814    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
3815        let sts_segs = ctx.find_segments("STS");
3816        let found = sts_segs.iter().any(|s| {
3817            s.elements
3818                .first()
3819                .and_then(|e| e.first())
3820                .is_some_and(|v| v == "7")
3821                && s.elements
3822                    .get(2)
3823                    .and_then(|e| e.first())
3824                    .is_some_and(|v| v == "ZX2")
3825        });
3826        ConditionResult::from(found)
3827    }
3828
3829    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
3830    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
3831        let sts_segs = ctx.find_segments("STS");
3832        let found = sts_segs.iter().any(|s| {
3833            s.elements
3834                .first()
3835                .and_then(|e| e.first())
3836                .is_some_and(|v| v == "7")
3837                && s.elements
3838                    .get(2)
3839                    .and_then(|e| e.first())
3840                    .is_some_and(|v| v == "ZA0")
3841        });
3842        ConditionResult::from(found)
3843    }
3844
3845    /// [147] Wenn in Anfrage vorhanden
3846    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
3847        // TODO: implement
3848        ConditionResult::Unknown
3849    }
3850
3851    /// [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
3852    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
3853        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3854        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3855        let found = pia_segs.iter().any(|s| {
3856            s.elements
3857                .first()
3858                .and_then(|e| e.first())
3859                .is_some_and(|v| v == "Z02")
3860                && s.elements
3861                    .get(1)
3862                    .and_then(|e| e.first())
3863                    .is_some_and(|v| ["1-02-0-017", "1-02-0-018"].contains(&v.as_str()))
3864        });
3865        ConditionResult::from(found)
3866    }
3867
3868    /// [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...
3869    // 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)
3870    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
3871        use std::collections::{HashMap, HashSet};
3872        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3873        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3874        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
3875        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
3876        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
3877        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
3878        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
3879            if q == "Z45" && !zid.is_empty() {
3880                idx_to_zeitraum.insert(*gi, zid.clone());
3881            }
3882        }
3883        if idx_to_zeitraum.is_empty() {
3884            return ConditionResult::False;
3885        }
3886        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
3887        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
3888        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
3889            if qual == "Z02" {
3890                if let Some(zid) = idx_to_zeitraum.get(gi) {
3891                    if let Some((_, prod)) = pia_prods.get(i) {
3892                        if !prod.is_empty() {
3893                            zeitraum_products
3894                                .entry(zid.clone())
3895                                .or_default()
3896                                .insert(prod.clone());
3897                        }
3898                    }
3899                }
3900            }
3901        }
3902        ConditionResult::from(
3903            zeitraum_products
3904                .values()
3905                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
3906        )
3907    }
3908
3909    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
3910    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
3911        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
3912        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
3913        let found = pia_segs.iter().any(|s| {
3914            s.elements
3915                .first()
3916                .and_then(|e| e.first())
3917                .is_some_and(|v| v == "Z02")
3918                && s.elements
3919                    .get(1)
3920                    .and_then(|e| e.first())
3921                    .is_some_and(|v| ["1-02-0-002"].contains(&v.as_str()))
3922        });
3923        ConditionResult::from(found)
3924    }
3925
3926    /// [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...
3927    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
3928        ctx.external.evaluate("code_list_membership_check")
3929    }
3930
3931    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
3932    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
3933        let ftx_segs = ctx.find_segments("FTX");
3934        let found = ftx_segs.iter().any(|s| {
3935            s.elements
3936                .first()
3937                .and_then(|e| e.first())
3938                .is_some_and(|v| v == "Z28")
3939        });
3940        ConditionResult::from(!found)
3941    }
3942
3943    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
3944    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
3945        let ftx_segs = ctx.find_segments("FTX");
3946        let found = ftx_segs.iter().any(|s| {
3947            s.elements
3948                .first()
3949                .and_then(|e| e.first())
3950                .is_some_and(|v| v == "Z27")
3951        });
3952        ConditionResult::from(!found)
3953    }
3954
3955    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
3956    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
3957        let ftx_segs = ctx.find_segments("FTX");
3958        let found = ftx_segs.iter().any(|s| {
3959            s.elements
3960                .first()
3961                .and_then(|e| e.first())
3962                .is_some_and(|v| v == "Z17")
3963        });
3964        ConditionResult::from(found)
3965    }
3966
3967    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
3968    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
3969        let sts_segs = ctx.find_segments("STS");
3970        let found = sts_segs.iter().any(|s| {
3971            s.elements
3972                .first()
3973                .and_then(|e| e.first())
3974                .is_some_and(|v| v == "7")
3975                && s.elements
3976                    .get(2)
3977                    .and_then(|e| e.first())
3978                    .is_some_and(|v| v == "E03")
3979                && s.elements
3980                    .get(3)
3981                    .and_then(|e| e.first())
3982                    .is_some_and(|v| v == "ZW8")
3983        });
3984        ConditionResult::from(found)
3985    }
3986
3987    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
3988    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
3989        let sts_segs = ctx.find_segments("STS");
3990        let found = sts_segs.iter().any(|s| {
3991            s.elements
3992                .first()
3993                .and_then(|e| e.first())
3994                .is_some_and(|v| v == "7")
3995                && s.elements
3996                    .get(2)
3997                    .and_then(|e| e.first())
3998                    .is_some_and(|v| v == "E03")
3999                && s.elements
4000                    .get(3)
4001                    .and_then(|e| e.first())
4002                    .is_some_and(|v| v == "ZW9")
4003        });
4004        ConditionResult::from(found)
4005    }
4006
4007    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
4008    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
4009        let sts_segs = ctx.find_segments("STS");
4010        let found = sts_segs.iter().any(|s| {
4011            s.elements
4012                .first()
4013                .and_then(|e| e.first())
4014                .is_some_and(|v| v == "7")
4015                && s.elements
4016                    .get(2)
4017                    .and_then(|e| e.first())
4018                    .is_some_and(|v| v == "E03")
4019                && s.elements
4020                    .get(3)
4021                    .and_then(|e| e.first())
4022                    .is_some_and(|v| v == "ZX0")
4023        });
4024        ConditionResult::from(found)
4025    }
4026
4027    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
4028    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4029        let sts_segs = ctx.find_segments("STS");
4030        let found = sts_segs.iter().any(|s| {
4031            s.elements
4032                .first()
4033                .and_then(|e| e.first())
4034                .is_some_and(|v| v == "7")
4035                && s.elements
4036                    .get(2)
4037                    .and_then(|e| e.first())
4038                    .is_some_and(|v| v == "E03")
4039                && s.elements
4040                    .get(3)
4041                    .and_then(|e| e.first())
4042                    .is_some_and(|v| v == "ZX1")
4043        });
4044        ConditionResult::from(found)
4045    }
4046
4047    /// [165] Wenn bekannt
4048    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4049        // TODO: implement
4050        ConditionResult::Unknown
4051    }
4052
4053    /// [166] Wenn vorhanden
4054    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4055        // "If present" — always true when the field is being validated
4056        ConditionResult::True
4057    }
4058
4059    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4060    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4061        let loc_segs = ctx.find_segments("LOC");
4062        let found = loc_segs.iter().any(|s| {
4063            s.elements
4064                .first()
4065                .and_then(|e| e.first())
4066                .is_some_and(|v| v == "Z21")
4067        });
4068        ConditionResult::from(found)
4069    }
4070
4071    /// [170] Wenn Anschrift der Marktlokation vorhanden
4072    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4073        // TODO: implement
4074        ConditionResult::Unknown
4075    }
4076
4077    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4078    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4079        let qty_segs = ctx.find_segments("QTY");
4080        let found = qty_segs.iter().any(|s| {
4081            s.elements
4082                .first()
4083                .and_then(|e| e.first())
4084                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4085        });
4086        ConditionResult::from(found)
4087    }
4088
4089    /// [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 (...
4090    // 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)
4091    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4092        // HAND-EDITED: "in derselben SG8" — this SG8, a SEQ+Z58/ZC9/ZD0/ZD6, has an
4093        // RFF+Z33 without DE1156.
4094        let kinds = ctx.scoped_find_segments_in("SG8", "SEQ");
4095        if !kinds
4096            .iter()
4097            .any(|s| matches!(s.get_element(0), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4098        {
4099            return ConditionResult::Unknown;
4100        }
4101        let z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4102        if z33.is_empty() {
4103            return ConditionResult::Unknown;
4104        }
4105        ConditionResult::from(z33.iter().any(|s| s.get_component(0, 2).is_empty()))
4106    }
4107
4108    /// [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...
4109    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4110        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4111        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4112        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
4113        let found = rff_z33.iter().any(|s| {
4114            s.elements
4115                .first()
4116                .and_then(|e| e.get(2))
4117                .is_some_and(|v| !v.is_empty())
4118        });
4119        ConditionResult::from(found)
4120    }
4121
4122    /// [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...
4123    // 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)
4124    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4125        // HAND-EDITED: "in einem weiteren SG8 SEQ+Z58 mit derselben fortlaufenden Nummer" — compared within the enclosing SG4 when the scope
4126        // reaches an SG8; the message as before otherwise.
4127        if let Some(scope) = ctx.enclosing_scope("SG8") {
4128            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4129                ctx,
4130                scope,
4131                &["Z58"],
4132                true,
4133                false,
4134            ));
4135        }
4136        let nav = match ctx.navigator {
4137            Some(n) => n,
4138            None => return ConditionResult::Unknown,
4139        };
4140        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4141        let mut entries: Vec<(usize, String, String)> = Vec::new();
4142        for i in 0..sg8_count {
4143            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4144            if !seqs.iter().any(|s| {
4145                s.elements
4146                    .first()
4147                    .and_then(|e: &Vec<String>| e.first())
4148                    .is_some_and(|v: &String| v == "Z58")
4149            }) {
4150                continue;
4151            }
4152            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4153            for rff in &rffs {
4154                let elems = match rff.elements.first() {
4155                    Some(e) => e,
4156                    None => continue,
4157                };
4158                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4159                    continue;
4160                }
4161                let objektcode = elems.get(1).cloned().unwrap_or_default();
4162                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4163                if !objektcode.is_empty() {
4164                    entries.push((i, objektcode, fort_nr));
4165                }
4166            }
4167        }
4168        for a in 0..entries.len() {
4169            for b in (a + 1)..entries.len() {
4170                if entries[a].0 != entries[b].0
4171                    && entries[a].1 == entries[b].1
4172                    && entries[a].2 == entries[b].2
4173                {
4174                    return ConditionResult::True;
4175                }
4176            }
4177        }
4178        ConditionResult::False
4179    }
4180
4181    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4182    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4183        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z34"))
4184    }
4185
4186    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4187    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4188        ConditionResult::from(ctx.self_segment_value_equals("RFF", 0, 0, "Z35"))
4189    }
4190
4191    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4192    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4193        let loc_segs = ctx.find_segments("LOC");
4194        let found = loc_segs.iter().any(|s| {
4195            s.elements
4196                .first()
4197                .and_then(|e| e.first())
4198                .is_some_and(|v| v == "Z18")
4199        });
4200        ConditionResult::from(found)
4201    }
4202
4203    /// [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...
4204    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4205        // HAND-EDITED: this SG8's Objektcode (RFF+Z33 DE1154) must be in a
4206        // sibling SG8 with the same SEQ code (Z58/ZC9/ZD0/ZD6). Unknown without
4207        // a group scope: "derselben SG8" cannot be told apart then.
4208        const KINDS: [&str; 4] = ["Z58", "ZC9", "ZD0", "ZD6"];
4209        let Some(siblings) = ctx.sibling_scopes("SG8") else {
4210            return ConditionResult::Unknown;
4211        };
4212        let seq_of = |segs: &[OwnedSegment]| {
4213            segs.iter()
4214                .find(|s| s.id == "SEQ")
4215                .map(|s| s.get_element(0).to_string())
4216        };
4217        let objektcodes = |segs: &[OwnedSegment]| -> Vec<String> {
4218            segs.iter()
4219                .filter(|s| s.id == "RFF" && s.get_element(0) == "Z33")
4220                .map(|s| s.get_component(0, 1).to_string())
4221                .filter(|v| !v.is_empty())
4222                .collect()
4223        };
4224        let own_seq = seq_of(&ctx.scoped_find_segments_in("SG8", "SEQ"));
4225        let Some(own_seq) = own_seq.filter(|q| KINDS.contains(&q.as_str())) else {
4226            return ConditionResult::False;
4227        };
4228        let own = objektcodes(&ctx.scoped_find_segments_in("SG8", "RFF"));
4229        if own.is_empty() {
4230            return ConditionResult::False;
4231        }
4232        let found = siblings.iter().any(|path| {
4233            seq_of(&ctx.find_segments_at(path, "SEQ")).as_deref() == Some(own_seq.as_str())
4234                && objektcodes(&ctx.find_segments_at(path, "RFF"))
4235                    .iter()
4236                    .any(|code| own.contains(code))
4237        });
4238        ConditionResult::from(found)
4239    }
4240
4241    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4242    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4243        let qty_segs = ctx.find_segments("QTY");
4244        let found = qty_segs.iter().any(|s| {
4245            s.elements
4246                .first()
4247                .and_then(|e| e.first())
4248                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4249        });
4250        ConditionResult::from(found)
4251    }
4252
4253    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4254    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4255        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
4256        // and the CCI+Z01++Z82 the condition names is part of it: a second
4257        // Messlokation (CCI+Z01++Z81) needs no Messstellenbetreiber Alt.
4258        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4259        let seq_z18 = seq_segs.iter().any(|s| {
4260            s.elements
4261                .first()
4262                .and_then(|e| e.first())
4263                .is_some_and(|v| v == "Z18")
4264        });
4265        let cci_z82 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4266            s.elements
4267                .first()
4268                .and_then(|e| e.first())
4269                .is_some_and(|v| v == "Z01")
4270                && s.elements
4271                    .get(2)
4272                    .and_then(|e| e.first())
4273                    .is_some_and(|v| v == "Z82")
4274        });
4275        ConditionResult::from(seq_z18 && cci_z82)
4276    }
4277
4278    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4279    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4280        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4281        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4282        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
4283        let found = pia_segs.iter().any(|s| {
4284            s.elements
4285                .first()
4286                .and_then(|e| e.first())
4287                .is_some_and(|v| v == "5")
4288                && s.elements
4289                    .get(1)
4290                    .and_then(|e| e.first())
4291                    .is_some_and(|v| v == "9991000000721")
4292        });
4293        ConditionResult::from(found)
4294    }
4295
4296    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4297    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4298        // HAND-EDITED (#343): the list's own STS+E01, in the SG4 opened by IDE+Z01; a
4299        // Vorgang's STS+E01 (IDE+24) answers the Vorgang, not the list.
4300        let mut in_list = false;
4301        let mut found = false;
4302        for s in ctx.segments.iter() {
4303            match s.id.as_str() {
4304                "IDE" => in_list = s.get_element(0) == "Z01",
4305                "STS" if in_list && s.get_element(0) == "E01" => found = true,
4306                _ => {}
4307            }
4308        }
4309        ConditionResult::from(!found)
4310    }
4311
4312    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4313    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4314        let sts_segs = ctx.find_segments("STS");
4315        let found = sts_segs.iter().any(|s| {
4316            s.elements
4317                .first()
4318                .and_then(|e| e.first())
4319                .is_some_and(|v| v == "7")
4320                && s.elements
4321                    .get(3)
4322                    .and_then(|e| e.first())
4323                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4324        });
4325        ConditionResult::from(found)
4326    }
4327
4328    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4329    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4330        let nav = match ctx.navigator {
4331            Some(n) => n,
4332            None => {
4333                let cavs = ctx.find_segments("CAV");
4334                return ConditionResult::from(cavs.iter().any(|s| {
4335                    s.elements
4336                        .first()
4337                        .and_then(|e: &Vec<String>| e.first())
4338                        .is_some_and(|v: &String| v == "E06")
4339                }));
4340            }
4341        };
4342        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4343        for i in 0..sg8_count {
4344            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4345            for j in 0..sg10_count {
4346                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4347                let has_cci_zb5 = ccis.iter().any(|s| {
4348                    s.elements
4349                        .get(2)
4350                        .and_then(|e: &Vec<String>| e.first())
4351                        .is_some_and(|v: &String| v == "ZB5")
4352                });
4353                if !has_cci_zb5 {
4354                    continue;
4355                }
4356                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4357                if cavs.iter().any(|s| {
4358                    s.elements
4359                        .first()
4360                        .and_then(|e: &Vec<String>| e.first())
4361                        .is_some_and(|v: &String| v == "E06")
4362                }) {
4363                    return ConditionResult::True;
4364                }
4365            }
4366        }
4367        ConditionResult::False
4368    }
4369
4370    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4371    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4372        let cci_segs = ctx.find_segments("CCI");
4373        let has_cci = cci_segs.iter().any(|s| {
4374            s.elements
4375                .iter()
4376                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4377        });
4378        let cav_segs = ctx.find_segments("CAV");
4379        let has_cav = cav_segs.iter().any(|s| {
4380            s.elements
4381                .first()
4382                .and_then(|e| e.first())
4383                .is_some_and(|v| v == "E05")
4384        });
4385        ConditionResult::from(has_cci && has_cav)
4386    }
4387
4388    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4389    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4390        let cci_segs = ctx.find_segments("CCI");
4391        let has_cci = cci_segs.iter().any(|s| {
4392            s.elements
4393                .iter()
4394                .any(|e| e.first().is_some_and(|v| v == "ZB5"))
4395        });
4396        let cav_segs = ctx.find_segments("CAV");
4397        let has_cav = cav_segs.iter().any(|s| {
4398            s.elements
4399                .first()
4400                .and_then(|e| e.first())
4401                .is_some_and(|v| v == "E04")
4402        });
4403        ConditionResult::from(has_cci && has_cav)
4404    }
4405
4406    /// [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...
4407    // 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)
4408    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4409        // HAND-EDITED: "in einem weiteren SG8 … Nummer, Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
4410        // reaches an SG8; the message as before otherwise.
4411        if let Some(scope) = ctx.enclosing_scope("SG8") {
4412            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
4413                ctx,
4414                scope,
4415                &["Z58", "ZC9", "ZD0"],
4416                true,
4417                true,
4418            ));
4419        }
4420        let nav = match ctx.navigator {
4421            Some(n) => n,
4422            None => return ConditionResult::Unknown,
4423        };
4424        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4425        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4426        for i in 0..sg8_count {
4427            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4428            let seq_opt = seqs.iter().find(|s| {
4429                s.elements
4430                    .first()
4431                    .and_then(|e: &Vec<String>| e.first())
4432                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4433            });
4434            let seq = match seq_opt {
4435                Some(s) => s,
4436                None => continue,
4437            };
4438            let seq_code = seq
4439                .elements
4440                .first()
4441                .and_then(|e: &Vec<String>| e.first())
4442                .cloned()
4443                .unwrap_or_default();
4444            let zeitraum_id = seq
4445                .elements
4446                .get(1)
4447                .and_then(|e: &Vec<String>| e.first())
4448                .cloned()
4449                .unwrap_or_default();
4450            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4451            for rff in &rffs {
4452                let elems = match rff.elements.first() {
4453                    Some(e) => e,
4454                    None => continue,
4455                };
4456                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4457                    continue;
4458                }
4459                let objektcode = elems.get(1).cloned().unwrap_or_default();
4460                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4461                if !objektcode.is_empty() {
4462                    entries.push((
4463                        i,
4464                        seq_code.clone(),
4465                        zeitraum_id.clone(),
4466                        objektcode,
4467                        fort_nr,
4468                    ));
4469                }
4470            }
4471        }
4472        for a in 0..entries.len() {
4473            for b in (a + 1)..entries.len() {
4474                if entries[a].0 != entries[b].0
4475                    && entries[a].1 == entries[b].1
4476                    && entries[a].2 == entries[b].2
4477                    && entries[a].3 == entries[b].3
4478                    && entries[a].4 == entries[b].4
4479                {
4480                    return ConditionResult::True;
4481                }
4482            }
4483        }
4484        ConditionResult::False
4485    }
4486
4487    /// [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 (...
4488    // 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)
4489    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4490        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (SLP/SEP) — compared within the enclosing SG4 when the scope
4491        // reaches an SG8; the message as before otherwise.
4492        if let Some(scope) = ctx.enclosing_scope("SG8") {
4493            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4494            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4495                sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "E02")
4496            }));
4497        }
4498        let nav = match ctx.navigator {
4499            Some(n) => n,
4500            None => return ConditionResult::Unknown,
4501        };
4502        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4503        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4504        for i in 0..sg8_count {
4505            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4506            if !seqs.iter().any(|s| {
4507                s.elements
4508                    .first()
4509                    .and_then(|e: &Vec<String>| e.first())
4510                    .is_some_and(|v: &String| v == "Z01")
4511            }) {
4512                continue;
4513            }
4514            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4515            let has_prog = (0..sg10_count).any(|j| {
4516                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4517                let has_za6 = ccis.iter().any(|s| {
4518                    s.elements
4519                        .get(2)
4520                        .and_then(|e: &Vec<String>| e.first())
4521                        .is_some_and(|v: &String| v == "ZA6")
4522                });
4523                if !has_za6 {
4524                    return false;
4525                }
4526                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4527                cavs.iter().any(|s| {
4528                    s.elements
4529                        .first()
4530                        .and_then(|e: &Vec<String>| e.first())
4531                        .is_some_and(|v: &String| v == "E02")
4532                })
4533            });
4534            if !has_prog {
4535                continue;
4536            }
4537            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4538            for rff in &rffs {
4539                let elems = match rff.elements.first() {
4540                    Some(e) => e,
4541                    None => continue,
4542                };
4543                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4544                    continue;
4545                }
4546                if let Some(malo_id) = elems.get(1) {
4547                    if !malo_id.is_empty() {
4548                        qualifying_malo_ids.push(malo_id.clone());
4549                    }
4550                }
4551            }
4552        }
4553        if qualifying_malo_ids.is_empty() {
4554            return ConditionResult::False;
4555        }
4556        for i in 0..sg8_count {
4557            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4558            for rff in &rffs {
4559                let elems = match rff.elements.first() {
4560                    Some(e) => e,
4561                    None => continue,
4562                };
4563                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4564                    continue;
4565                }
4566                if let Some(malo_id) = elems.get(1) {
4567                    if qualifying_malo_ids.contains(malo_id) {
4568                        return ConditionResult::True;
4569                    }
4570                }
4571            }
4572        }
4573        ConditionResult::False
4574    }
4575
4576    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4577    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4578        let sts_segs = ctx.find_segments("STS");
4579        let found = sts_segs.iter().any(|s| {
4580            s.elements
4581                .first()
4582                .and_then(|e| e.first())
4583                .is_some_and(|v| v == "7")
4584                && s.elements
4585                    .get(2)
4586                    .and_then(|e| e.first())
4587                    .is_some_and(|v| ["E03"].contains(&v.as_str()))
4588        });
4589        ConditionResult::from(found)
4590    }
4591
4592    /// [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
4593    // 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)
4594    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4595        let nav = match ctx.navigator {
4596            Some(n) => n,
4597            None => {
4598                return ctx.filtered_parent_child_has_qualifier(
4599                    &["SG4", "SG8"],
4600                    "SEQ",
4601                    0,
4602                    "Z01",
4603                    "SG10",
4604                    "CCI",
4605                    2,
4606                    "ZA6",
4607                )
4608            }
4609        };
4610        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4611        for i in 0..sg8_count {
4612            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4613            if !seqs.iter().any(|s| {
4614                s.elements
4615                    .first()
4616                    .and_then(|e: &Vec<String>| e.first())
4617                    .is_some_and(|v: &String| v == "Z01")
4618            }) {
4619                continue;
4620            }
4621            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4622            for j in 0..sg10_count {
4623                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4624                if !ccis.iter().any(|s| {
4625                    s.elements
4626                        .get(2)
4627                        .and_then(|e: &Vec<String>| e.first())
4628                        .is_some_and(|v: &String| v == "ZA6")
4629                }) {
4630                    continue;
4631                }
4632                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4633                if cavs.iter().any(|s| {
4634                    s.elements
4635                        .first()
4636                        .and_then(|e: &Vec<String>| e.first())
4637                        .is_some_and(|v: &String| v == "E02")
4638                }) {
4639                    return ConditionResult::True;
4640                }
4641            }
4642        }
4643        ConditionResult::False
4644    }
4645
4646    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4647    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4648        // TODO: implement
4649        ConditionResult::Unknown
4650    }
4651
4652    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4653    // 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)
4654    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4655        use std::collections::HashSet;
4656        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4657        let loc_zeitraum_ids: HashSet<String> = ctx
4658            .find_segments("LOC")
4659            .into_iter()
4660            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4661            .filter(|v| !v.is_empty())
4662            .collect();
4663        if loc_zeitraum_ids.is_empty() {
4664            return ConditionResult::False;
4665        }
4666        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4667        let matching_seq = ctx
4668            .find_segments_with_qualifier("SEQ", 0, "Z78")
4669            .into_iter()
4670            .any(|s| {
4671                s.elements
4672                    .get(1)
4673                    .and_then(|e: &Vec<String>| e.first())
4674                    .is_some_and(|v: &String| {
4675                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4676                    })
4677            });
4678        if !matching_seq {
4679            return ConditionResult::False;
4680        }
4681        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4682        ctx.has_qualifier("RFF", 0, "Z39")
4683    }
4684
4685    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4686    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4687        let sts_segs = ctx.find_segments("STS");
4688        let found = sts_segs.iter().any(|s| {
4689            s.elements
4690                .first()
4691                .and_then(|e| e.first())
4692                .is_some_and(|v| v == "7")
4693                && s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
4694                    ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str())
4695                })
4696        });
4697        ConditionResult::from(found)
4698    }
4699
4700    /// [204] Wenn im SG8+SEQ+Z03/ ZA3/ ZA4 (Zähleinrichtungsdaten) mit der selben Zeitraum-ID im SG8 SEQ DE1050, sowie identischer Qualität im DE1229 der SG8 SEQ für die in diesem RFF DE1154 genannte Gerätenummer eines Zählers das RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden ist
4701    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4702        // HAND-EDITED (#392): the Zähleinrichtungsdaten of the meter this RFF
4703        // names (its number in the SG10 CAV+Z30 DE7110), with this SG8's
4704        // Zeitraum-ID and quality — erwartet (ZA3) beside an erwartete SG8
4705        // (ZA5, ZB7, …), im System vorhanden (ZA4) beside a vorhandene one —
4706        // not any Zähleinrichtungsdaten of the message. No such SG8: no
4707        // RFF+Z14 for that meter either.
4708        let Some(number) = rff_reference(ctx) else {
4709            return ConditionResult::Unknown;
4710        };
4711        let Some(blocks) = sg8_blocks(ctx) else {
4712            return ConditionResult::Unknown;
4713        };
4714        let own = ctx
4715            .enclosing_scope("SG8")
4716            .and_then(|scope| ctx.find_segments_at(scope, "SEQ").into_iter().next());
4717        let own_seq = own.as_ref().map(|s| s.get_element(0).to_string());
4718        let own_zeitraum = own
4719            .as_ref()
4720            .and_then(|s| component(s, 1, 0).map(str::to_string));
4721        let quality = |seq: &str| match seq {
4722            "ZA3" | "ZA5" | "ZB7" | "ZB9" | "ZC1" | "ZC3" | "ZG6" | "ZH3" => Some(true),
4723            "ZA4" | "ZA6" | "ZB8" | "ZC0" | "ZC2" | "ZC4" | "ZG7" | "ZH4" => Some(false),
4724            _ => None,
4725        };
4726        let meter = blocks
4727            .iter()
4728            .filter(|b| matches!(b.seq.as_str(), "Z03" | "ZA3" | "ZA4"))
4729            .filter(
4730                |b| match (quality(&b.seq), own_seq.as_deref().and_then(quality)) {
4731                    (Some(theirs), Some(ours)) => theirs == ours,
4732                    _ => true,
4733                },
4734            )
4735            .filter(|b| match own_zeitraum.as_deref() {
4736                Some(id) if !id.is_empty() && !b.zeitraum.is_empty() => b.zeitraum == id,
4737                _ => true,
4738            })
4739            .find(|b| {
4740                b.sg10.iter().any(|(_, cavs)| {
4741                    cavs.iter().any(|c| {
4742                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
4743                    })
4744                })
4745            });
4746        ConditionResult::from(meter.map_or(true, |b| {
4747            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
4748        }))
4749    }
4750
4751    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
4752    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
4753        let loc_segs = ctx.find_segments("LOC");
4754        let found = loc_segs.iter().any(|s| {
4755            s.elements
4756                .first()
4757                .and_then(|e| e.first())
4758                .is_some_and(|v| v == "Z19")
4759        });
4760        ConditionResult::from(found)
4761    }
4762
4763    /// [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
4764    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
4765        let sts_segs = ctx.find_segments("STS");
4766        let found = sts_segs.iter().any(|s| {
4767            s.elements
4768                .first()
4769                .and_then(|e| e.first())
4770                .is_some_and(|v| v == "7")
4771                && s.elements
4772                    .get(2)
4773                    .and_then(|e| e.first())
4774                    .is_some_and(|v| ["ZG5"].contains(&v.as_str()))
4775        });
4776        ConditionResult::from(!found)
4777    }
4778
4779    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
4780    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
4781        ConditionResult::from(ctx.self_segment_value_equals("DTM", 0, 2, "303"))
4782    }
4783
4784    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
4785    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
4786        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4787        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4788        let found = cci_segs.iter().any(|s| {
4789            s.elements
4790                .get(2)
4791                .and_then(|e| e.first())
4792                .is_some_and(|v| v == "ZA6")
4793        });
4794        ConditionResult::from(found)
4795    }
4796
4797    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
4798    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
4799        let nad_segs = ctx.find_segments("NAD");
4800        let found = nad_segs.iter().any(|s| {
4801            s.elements
4802                .get(3)
4803                .and_then(|e| e.first())
4804                .is_some_and(|v| !v.is_empty())
4805        });
4806        ConditionResult::from(!found)
4807    }
4808
4809    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
4810    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
4811        let nad_segs = ctx.find_segments("NAD");
4812        let found = nad_segs.iter().any(|s| {
4813            s.elements
4814                .first()
4815                .and_then(|e| e.first())
4816                .is_some_and(|v| v == "Z09")
4817        });
4818        ConditionResult::from(found)
4819    }
4820
4821    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
4822    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
4823        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8 (its
4824        // SG10s included). Both segments are looked for anywhere in that SG8,
4825        // not within one SG10.
4826        let has_cci_e13 = ctx.scoped_find_segments_in("SG8", "CCI").iter().any(|s| {
4827            s.elements
4828                .get(2)
4829                .and_then(|e| e.first())
4830                .is_some_and(|v| v == "E13")
4831        });
4832        let has_cav_mme = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
4833            s.elements
4834                .first()
4835                .and_then(|e| e.first())
4836                .is_some_and(|v| v == "MME")
4837        });
4838        ConditionResult::from(has_cci_e13 && has_cav_mme)
4839    }
4840
4841    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
4842    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
4843        // HAND-EDITED: the CCI and the CAV of one SG10; Z08 is DE7110 of the CAV.
4844        sg10_has(ctx, false, |segs| {
4845            sg4_instances::has_cci(segs, "", "Z88")
4846                && segs.iter().any(|s| {
4847                    s.id == "CAV" && s.get_element(0) == "Z74" && s.get_component(0, 3) == "Z08"
4848                })
4849        })
4850    }
4851
4852    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
4853    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
4854        let rff_segs = ctx.find_segments("RFF");
4855        let found = rff_segs.iter().any(|s| {
4856            s.elements
4857                .first()
4858                .and_then(|e| e.first())
4859                .is_some_and(|v| v == "Z49")
4860        });
4861        ConditionResult::from(found)
4862    }
4863
4864    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
4865    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
4866        let nad_segs = ctx.find_segments("NAD");
4867        let found = nad_segs.iter().any(|s| {
4868            s.elements
4869                .first()
4870                .and_then(|e| e.first())
4871                .is_some_and(|v| v == "Z65")
4872        });
4873        ConditionResult::from(found)
4874    }
4875
4876    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
4877    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
4878        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
4879        let seq_segs = ctx.scoped_find_segments_in("SG8", "SEQ");
4880        let has_seq = seq_segs.iter().any(|s| {
4881            s.elements
4882                .first()
4883                .and_then(|e| e.first())
4884                .is_some_and(|v| ["Z01", "Z98"].contains(&v.as_str()))
4885        });
4886        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
4887        let has_cci = cci_segs.iter().any(|s| {
4888            s.elements
4889                .first()
4890                .and_then(|e| e.first())
4891                .is_some_and(|v| v == "Z30")
4892                && s.elements
4893                    .get(2)
4894                    .and_then(|e| e.first())
4895                    .is_some_and(|v| v == "Z06")
4896        });
4897        ConditionResult::from(has_seq && has_cci)
4898    }
4899
4900    /// [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)
4901    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
4902        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
4903        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
4904        if !has_seq_z15 {
4905            return ConditionResult::True;
4906        }
4907        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
4908        ctx.any_group_has_qualifier_without("SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"])
4909    }
4910
4911    /// [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...
4912    // HAND-EDITED (mako-twin, 55043): the generated version only checked that
4913    // some RFF+Z18 names some LOC+Z16 and dropped the Erzeugung clause, so it
4914    // held for every message with a Marktlokation. The scope never narrows to
4915    // one SG8 (only top-level groups are scoped), so "this SG8" is any SG8
4916    // other than SEQ+Z01, as in the neighbouring conditions.
4917    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
4918        // HAND-EDITED: "wie ein RFF+Z18 aus einer SG8 SEQ+Z01" (Erzeugung) — compared within the enclosing SG4 when the scope
4919        // reaches an SG8; the message as before otherwise.
4920        if let Some(scope) = ctx.enclosing_scope("SG8") {
4921            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
4922            return ConditionResult::from(sg4_instances::z01_of_malo_has(ctx, &refs, |s| {
4923                sg4_instances::has_cci(s, "Z30", "Z06")
4924            }));
4925        }
4926        let Some(blocks) = sg8_blocks(ctx) else {
4927            return ConditionResult::Unknown;
4928        };
4929        let loc_ids: Vec<String> = ctx
4930            .find_segments_with_qualifier("LOC", 0, "Z16")
4931            .iter()
4932            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
4933            .collect();
4934        let malo_refs = |b: &Sg8Block| -> Vec<String> {
4935            b.malo_refs
4936                .iter()
4937                .filter(|id| loc_ids.contains(id))
4938                .cloned()
4939                .collect()
4940        };
4941        // Marktlokationen whose SEQ+Z01 carries SG10 CCI+Z30++Z06 (Erzeugung).
4942        let erzeugung: Vec<String> = blocks
4943            .iter()
4944            .filter(|b| b.seq == "Z01")
4945            .filter(|b| {
4946                b.sg10.iter().any(|(ccis, _)| {
4947                    ccis.iter().any(|c| {
4948                        component(c, 0, 0) == Some("Z30") && component(c, 2, 0) == Some("Z06")
4949                    })
4950                })
4951            })
4952            .flat_map(|b| malo_refs(b))
4953            .collect();
4954        let referenced = blocks
4955            .iter()
4956            .filter(|b| b.seq != "Z01")
4957            .any(|b| malo_refs(b).iter().any(|id| erzeugung.contains(id)));
4958        ConditionResult::from(referenced)
4959    }
4960
4961    /// [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)
4962    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
4963        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
4964        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
4965        if !has_seq_ze7 {
4966            return ConditionResult::True;
4967        }
4968        ctx.any_group_has_qualifier_without("SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"])
4969    }
4970
4971    /// [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
4972    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
4973        // Check: in SG8 with SEQ+Z98, SG10 has CCI+++ZA6 and CAV+E14 or ETK
4974        let r1 = ctx.filtered_parent_child_has_qualifier(
4975            &["SG4", "SG8"],
4976            "SEQ",
4977            0,
4978            "Z98",
4979            "SG10",
4980            "CAV",
4981            0,
4982            "E14",
4983        );
4984        if r1 == ConditionResult::True {
4985            return ConditionResult::True;
4986        }
4987        ctx.filtered_parent_child_has_qualifier(
4988            &["SG4", "SG8"],
4989            "SEQ",
4990            0,
4991            "Z98",
4992            "SG10",
4993            "CAV",
4994            0,
4995            "ETK",
4996        )
4997    }
4998
4999    /// [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)...
5000    // 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)
5001    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
5002        // HAND-EDITED: "in einem weiteren SG8 … Zeitraum-ID und DE1229" — compared within the enclosing SG4 when the scope
5003        // reaches an SG8; the message as before otherwise.
5004        if let Some(scope) = ctx.enclosing_scope("SG8") {
5005            return ConditionResult::from(sg4_instances::objektcode_elsewhere(
5006                ctx,
5007                scope,
5008                &["Z58", "ZC9", "ZD0", "ZD6"],
5009                false,
5010                true,
5011            ));
5012        }
5013        use std::collections::HashSet;
5014        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
5015        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
5016        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
5017        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
5018        // Collect per-SG8-instance: RFF qualifier and DE1154 value
5019        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
5020        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
5021        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
5022        let mut seen: HashSet<(String, String, String)> = HashSet::new();
5023        for (idx, seq_qual) in &seq_quals {
5024            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
5025                continue;
5026            }
5027            let zeitraum_id = seq_zeitraums
5028                .iter()
5029                .find(|(i, _)| i == idx)
5030                .map(|(_, v)| v.as_str())
5031                .unwrap_or("");
5032            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5033            for (ridx, rff_qual) in rff_quals.iter() {
5034                if ridx != idx || rff_qual != "Z33" {
5035                    continue;
5036                }
5037                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5038                    if obj_code.is_empty() {
5039                        continue;
5040                    }
5041                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5042                    if seen.contains(&key) {
5043                        return ConditionResult::True;
5044                    }
5045                    seen.insert(key);
5046                }
5047            }
5048        }
5049        ConditionResult::False
5050    }
5051
5052    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5053    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5054        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
5055        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
5056        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
5057        sg10_has(ctx, true, |segs| {
5058            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z52")
5059        })
5060    }
5061
5062    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5063    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5064        ctx.external.evaluate("recipient_role_check")
5065    }
5066
5067    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5068    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5069        // HAND-EDITED: the CCI and the CAV of one SG10, of this SG8 when the
5070        // scope reaches one (#334): its SG10 "Zugeordnete Marktpartner" is
5071        // `Muss [232] ⊻ [234]`, which holds only per Marktlokation.
5072        sg10_has(ctx, true, |segs| {
5073            sg4_instances::has_cci(segs, "", "Z83") && sg4_instances::has_cav(segs, "Z53")
5074        })
5075    }
5076
5077    /// [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
5078    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5079        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5080        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5081        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5082        let found = pia_segs.iter().any(|s| {
5083            s.elements
5084                .first()
5085                .and_then(|e| e.first())
5086                .is_some_and(|v| v == "Z02")
5087                && s.elements
5088                    .get(1)
5089                    .and_then(|e| e.first())
5090                    .is_some_and(|v| v.starts_with("1-08") && v.contains("AGS"))
5091        });
5092        ConditionResult::from(found)
5093    }
5094
5095    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5096    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5097        let ide_segs = ctx.find_segments("IDE");
5098        let found = ide_segs.iter().any(|s| {
5099            s.elements
5100                .first()
5101                .and_then(|e| e.first())
5102                .is_some_and(|v| v == "24")
5103        });
5104        ConditionResult::from(!found)
5105    }
5106
5107    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5108    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5109        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
5110        // SG10, when the scope reaches one; of any one SG10 otherwise.
5111        this_sg10_has(ctx, |segs| {
5112            segs.iter()
5113                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
5114                && sg4_instances::has_cav(segs, "Z65")
5115        })
5116    }
5117
5118    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5119    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5120        let seq_segs = ctx.find_segments("SEQ");
5121        let found = seq_segs.iter().any(|s| {
5122            s.elements
5123                .first()
5124                .and_then(|e| e.first())
5125                .is_some_and(|v| ["Z01"].contains(&v.as_str()))
5126        });
5127        ConditionResult::from(found)
5128    }
5129
5130    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5131    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5132        ctx.external.evaluate("recipient_role_check")
5133    }
5134
5135    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5136    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5137        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5138        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5139        let found = cci_segs.iter().any(|s| {
5140            s.elements
5141                .first()
5142                .and_then(|e| e.first())
5143                .is_some_and(|v| v == "6")
5144                && s.elements
5145                    .get(2)
5146                    .and_then(|e| e.first())
5147                    .is_some_and(|v| v == "ZA8")
5148        });
5149        ConditionResult::from(found)
5150    }
5151
5152    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5153    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5154        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5155        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
5156        let found = cci_segs.iter().any(|s| {
5157            s.elements
5158                .first()
5159                .and_then(|e| e.first())
5160                .is_some_and(|v| v == "6")
5161                && s.elements
5162                    .get(2)
5163                    .and_then(|e| e.first())
5164                    .is_some_and(|v| v == "ZA9")
5165        });
5166        ConditionResult::from(found)
5167    }
5168
5169    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5170    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5171        // TODO: implement
5172        ConditionResult::Unknown
5173    }
5174
5175    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5176    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5177        // Check: all STS+E01 DE1131 values are identical
5178        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5179        if sts_segs.len() <= 1 {
5180            return ConditionResult::True;
5181        }
5182        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5183        let all_same = sts_segs
5184            .iter()
5185            .all(|s| s.elements.first().and_then(|e| e.get(1)) == first_val);
5186        ConditionResult::from(all_same)
5187    }
5188
5189    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5190    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5191        ctx.external.evaluate("sender_role_check")
5192    }
5193
5194    /// [252] Wenn DE0068 vorhanden
5195    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5196        let unb_segs = ctx.find_segments("UNB");
5197        let found = unb_segs.iter().any(|s| {
5198            s.elements
5199                .get(4)
5200                .and_then(|e| e.first())
5201                .is_some_and(|v| !v.is_empty())
5202        });
5203        ConditionResult::from(found)
5204    }
5205
5206    /// [254] Es ist der Code \"AUA\" einzutragen
5207    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5208        // Check: the code "AUA" is present
5209        ConditionResult::True // AUA is always the required code when this condition applies
5210    }
5211
5212    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5213    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5214        // TODO: implement
5215        ConditionResult::Unknown
5216    }
5217
5218    /// [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
5219    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5220        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5221        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5222        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5223        let found = pia_segs.iter().any(|s| {
5224            s.elements
5225                .first()
5226                .and_then(|e| e.first())
5227                .is_some_and(|v| v == "5")
5228                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5229                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5230                })
5231        });
5232        ConditionResult::from(found)
5233    }
5234
5235    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5236    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5237        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5238        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5239        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5240        let found = pia_segs.iter().any(|s| {
5241            s.elements
5242                .first()
5243                .and_then(|e| e.first())
5244                .is_some_and(|v| v == "5")
5245                && s.elements
5246                    .get(1)
5247                    .and_then(|e| e.first())
5248                    .is_some_and(|v| v.contains(":1.9.") && v.starts_with("1-"))
5249        });
5250        ConditionResult::from(found)
5251    }
5252
5253    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5254    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5255        let pia_segs = ctx.find_segments("PIA");
5256        let found = pia_segs.iter().any(|s| {
5257            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5258                let prefixes = ["1-01", "1-03", "1-05"];
5259                prefixes.iter().any(|p| v.starts_with(p))
5260            })
5261        });
5262        ConditionResult::from(found)
5263    }
5264
5265    /// [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
5266    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5267        let pia_segs = ctx.find_segments("PIA");
5268        let found = pia_segs.iter().any(|s| {
5269            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5270                let prefixes = [
5271                    "1-07-1", "2", "1-10-1", "2", "4", "5", "6", "7", "8", "9", "1-11-1",
5272                ];
5273                prefixes.iter().any(|p| v.starts_with(p))
5274            })
5275        });
5276        ConditionResult::from(found)
5277    }
5278
5279    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5280    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5281        // TODO: implement
5282        ConditionResult::Unknown
5283    }
5284
5285    /// [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
5286    // HAND-EDITED (mako-twin, 55043): the generated version looked for
5287    // CAV+E14/ETK in any SG10 under SEQ+Z01/Z98, ignoring CCI+++ZA6; the AHB
5288    // asks for CAV+Z36 (TEP m. Referenzmessung) in the SG10 CCI+++ZA6.
5289    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5290        let Some(blocks) = sg8_blocks(ctx) else {
5291            return ConditionResult::Unknown;
5292        };
5293        let found = blocks
5294            .iter()
5295            .filter(|b| b.seq == "Z01" || b.seq == "Z98")
5296            .any(|b| {
5297                b.sg10.iter().any(|(ccis, cavs)| {
5298                    ccis.iter().any(|c| component(c, 2, 0) == Some("ZA6"))
5299                        && cavs.iter().any(|c| component(c, 0, 0) == Some("Z36"))
5300                })
5301            });
5302        ConditionResult::from(found)
5303    }
5304
5305    /// [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...
5306    // 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)
5307    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5308        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8" — compared within the enclosing SG4 when the scope
5309        // reaches an SG8; the message as before otherwise.
5310        if let Some(scope) = ctx.enclosing_scope("SG8") {
5311            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
5312            return ConditionResult::from(z01s.iter().any(|p| {
5313                sg4_instances::sg10_at(ctx, p, |s| {
5314                    sg4_instances::has_cci(s, "", "ZA6") && sg4_instances::has_cav(s, "Z36")
5315                })
5316            }));
5317        }
5318        let nav = match ctx.navigator {
5319            Some(n) => n,
5320            None => {
5321                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5322                return ctx.filtered_parent_child_has_qualifier(
5323                    &["SG4", "SG8"],
5324                    "SEQ",
5325                    0,
5326                    "Z01",
5327                    "SG10",
5328                    "CAV",
5329                    0,
5330                    "Z36",
5331                );
5332            }
5333        };
5334        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5335        for i in 0..sg8_count {
5336            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5337            for j in 0..sg10_count {
5338                // CCI+++ZA6 means elements[2][0] == "ZA6"
5339                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5340                let has_cci_za6 = ccis.iter().any(|s| {
5341                    s.elements
5342                        .get(2)
5343                        .and_then(|e: &Vec<String>| e.first())
5344                        .is_some_and(|v: &String| v == "ZA6")
5345                });
5346                if has_cci_za6 {
5347                    let cavs =
5348                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5349                    if cavs.iter().any(|s| {
5350                        s.elements
5351                            .first()
5352                            .and_then(|e: &Vec<String>| e.first())
5353                            .is_some_and(|v: &String| v == "Z36")
5354                    }) {
5355                        return ConditionResult::True;
5356                    }
5357                }
5358            }
5359        }
5360        ConditionResult::False
5361    }
5362
5363    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5364    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5365        let seq_segs = ctx.find_segments("SEQ");
5366        let found = seq_segs.iter().any(|s| {
5367            s.elements
5368                .first()
5369                .and_then(|e| e.first())
5370                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
5371        });
5372        ConditionResult::from(!found)
5373    }
5374
5375    /// [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
5376    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5377        // Most European countries have postal codes — default to true
5378        ConditionResult::True
5379    }
5380
5381    /// [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
5382    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5383        // TODO: implement
5384        ConditionResult::Unknown
5385    }
5386
5387    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5388    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5389        let pia_segs = ctx.find_segments("PIA");
5390        let found = pia_segs.iter().any(|s| {
5391            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5392                let prefixes = ["1-02", "1-04", "1-06", "1-09"];
5393                prefixes.iter().any(|p| v.starts_with(p))
5394            })
5395        });
5396        ConditionResult::from(found)
5397    }
5398
5399    /// [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 ...
5400    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5401        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5402        // The PIA+5 OBIS must be an energy register `1-b:c.8.e` (c = 1..8;
5403        // 1.8.63 is one of them). A load profile such as 1-1:1.29.0 is not
5404        // (mako-twin, 55168: it demanded `produkt` and Z63 of a 1.29.0 item).
5405        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5406        let found = pia_segs.iter().any(|s| {
5407            s.elements
5408                .first()
5409                .and_then(|e| e.first())
5410                .is_some_and(|v| v == "5")
5411                && s.elements
5412                    .get(1)
5413                    .and_then(|e| e.first())
5414                    .is_some_and(|v| is_energy_register_obis(v))
5415        });
5416        ConditionResult::from(found)
5417    }
5418
5419    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5420    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5421        let sts_segs = ctx.find_segments("STS");
5422        let found = sts_segs.iter().any(|s| {
5423            s.elements
5424                .first()
5425                .and_then(|e| e.first())
5426                .is_some_and(|v| v == "7")
5427                && s.elements
5428                    .get(3)
5429                    .and_then(|e| e.first())
5430                    .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5431        });
5432        ConditionResult::from(found)
5433    }
5434
5435    /// [280] Nur MP-ID aus Sparte Strom
5436    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5437        // Check: Marktpartner-ID is from Sparte Strom
5438        // Approximate: check NAD+MS C082 for Strom MP-ID format
5439        ctx.external.evaluate("sector_check")
5440    }
5441
5442    /// [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
5443    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5444        // Check: PIA DE7140 has one of the specific codes
5445        let pia_segs = ctx.find_segments("PIA");
5446        let found = pia_segs.iter().any(|s| {
5447            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5448                v.starts_with("1-01-6-005")
5449                    || v.starts_with("1-01-9-001")
5450                    || v.starts_with("1-01-9-002")
5451                    || v.starts_with("1-07-3-001")
5452                    || v.starts_with("1-08-1-001")
5453                    || v.starts_with("1-08-3-001")
5454                    || v.starts_with("1-08-4-001")
5455                    || v.starts_with("1-08-4-002")
5456                    || v.starts_with("1-08-4-003")
5457                    || v.starts_with("1-08-5")
5458            })
5459        });
5460        ConditionResult::from(found)
5461    }
5462
5463    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5464    // HAND-EDITED — the codegen prompt (conditions/prompt.rs) now documents
5465    // both the SCOPE RULES (Task B6) and the codelist API (review followup).
5466    // Regenerating this function should preserve the shape, but re-verify
5467    // against the committed body if generate-conditions is run.
5468    fn evaluate_284(&self, ctx: &EvaluationContext) -> ConditionResult {
5469        // [284] Erlaubte Codes aus der Codeliste der Gruppenartikel-
5470        // und Artikel-ID mit X in der Spalte UTILMD/Codeverwendung.
5471        //
5472        // Scope-aware: when validate_tree sets a GroupScope on ctx, this
5473        // narrows to the enclosing SG8 instance; otherwise falls back to
5474        // message-wide find_segments. Matches the AHB wording
5475        // "in derselben SG8 im PIA DE7140 ein Code...".
5476        use crate::eval::CodelistColumn;
5477        let any_match = ctx.scoped_find_segments("PIA").iter().any(|s| {
5478            let is_z02 = s
5479                .elements
5480                .first()
5481                .and_then(|e| e.first())
5482                .is_some_and(|v| v == "Z02");
5483            if !is_z02 {
5484                return false;
5485            }
5486            let code = match s.elements.get(1).and_then(|e| e.first()) {
5487                Some(c) if !c.is_empty() => c,
5488                _ => return false,
5489            };
5490            ctx.codelist
5491                .flag(code, CodelistColumn::UtilmdCodeverwendung)
5492                .unwrap_or(false)
5493        });
5494        ConditionResult::from(any_match)
5495    }
5496
5497    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5498    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5499        // Check: PIA DE7140 starts with 1-08-3-AGS
5500        let pia_segs = ctx.find_segments("PIA");
5501        let found = pia_segs.iter().any(|s| {
5502            s.elements
5503                .get(1)
5504                .and_then(|e| e.first())
5505                .is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5506        });
5507        ConditionResult::from(found)
5508    }
5509
5510    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5511    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5512        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5513        let pia_segs = ctx.find_segments("PIA");
5514        let found = pia_segs.iter().any(|s| {
5515            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5516                (v.starts_with("1-08-1")
5517                    || v.starts_with("1-08-2")
5518                    || v.starts_with("1-08-4")
5519                    || v.starts_with("1-08-5"))
5520                    && v.contains("AGS")
5521                    && v.contains("KG")
5522            })
5523        });
5524        ConditionResult::from(found)
5525    }
5526
5527    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5528    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5529        // TODO: implement
5530        ConditionResult::Unknown
5531    }
5532
5533    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5534    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5535        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5536        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5537        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
5538        let found = pia_segs.iter().any(|s| {
5539            s.elements
5540                .first()
5541                .and_then(|e| e.first())
5542                .is_some_and(|v| v == "5")
5543                && s.elements
5544                    .get(1)
5545                    .and_then(|e| e.first())
5546                    .is_some_and(|v| v.contains(":2.9.") && v.starts_with("1-"))
5547        });
5548        ConditionResult::from(found)
5549    }
5550
5551    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5552    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5553        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
5554        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
5555        let found = rff_segs.iter().any(|s| {
5556            s.elements
5557                .first()
5558                .and_then(|e| e.first())
5559                .is_some_and(|v| v == "Z37")
5560        });
5561        ConditionResult::from(found)
5562    }
5563
5564    /// [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...
5565    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5566        ctx.external.evaluate("code_list_membership_check")
5567    }
5568
5569    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5570    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5571        let loc_segs = ctx.find_segments("LOC");
5572        let found = loc_segs.iter().any(|s| {
5573            s.elements
5574                .first()
5575                .and_then(|e| e.first())
5576                .is_some_and(|v| v == "Z22")
5577        });
5578        ConditionResult::from(found)
5579    }
5580
5581    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5582    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5583        let loc_segs = ctx.find_segments("LOC");
5584        let found = loc_segs.iter().any(|s| {
5585            s.elements
5586                .first()
5587                .and_then(|e| e.first())
5588                .is_some_and(|v| v == "Z18")
5589        });
5590        ConditionResult::from(found)
5591    }
5592
5593    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5594    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5595        let loc_segs = ctx.find_segments("LOC");
5596        let found = loc_segs.iter().any(|s| {
5597            s.elements
5598                .first()
5599                .and_then(|e| e.first())
5600                .is_some_and(|v| v == "Z15")
5601        });
5602        ConditionResult::from(!found)
5603    }
5604
5605    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5606    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5607        let loc_segs = ctx.find_segments("LOC");
5608        let found = loc_segs.iter().any(|s| {
5609            s.elements
5610                .first()
5611                .and_then(|e| e.first())
5612                .is_some_and(|v| v == "Z20")
5613        });
5614        ConditionResult::from(found)
5615    }
5616
5617    /// [297] #kv# Wenn SG5 LOC+Z18 (Netzlokation) nicht vorhanden
5618    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
5619        let loc_segs = ctx.find_segments("LOC");
5620        let found = loc_segs.iter().any(|s| {
5621            s.elements
5622                .first()
5623                .and_then(|e| e.first())
5624                .is_some_and(|v| v == "Z18")
5625        });
5626        ConditionResult::from(!found)
5627    }
5628
5629    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5630    fn evaluate_298(&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 == "Z20")
5637        });
5638        ConditionResult::from(!found)
5639    }
5640
5641    /// [299] #kv# Wenn SG5 LOC+Z19 (Steuerbare Ressource) nicht vorhanden
5642    fn evaluate_299(&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 == "Z19")
5649        });
5650        ConditionResult::from(!found)
5651    }
5652
5653    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5654    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5655        let seq_segs = ctx.find_segments("SEQ");
5656        let found = seq_segs.iter().any(|s| {
5657            s.elements
5658                .first()
5659                .and_then(|e| e.first())
5660                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5661        });
5662        ConditionResult::from(!found)
5663    }
5664
5665    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5666    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5667        let bgm_segs = ctx.find_segments("BGM");
5668        let found = bgm_segs.iter().any(|s| {
5669            s.elements
5670                .first()
5671                .and_then(|e| e.first())
5672                .is_some_and(|v| v == "E03")
5673        });
5674        ConditionResult::from(found)
5675    }
5676
5677    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5678    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5679        let seq_segs = ctx.find_segments("SEQ");
5680        let found = seq_segs.iter().any(|s| {
5681            s.elements
5682                .first()
5683                .and_then(|e| e.first())
5684                .is_some_and(|v| ["Z08"].contains(&v.as_str()))
5685        });
5686        ConditionResult::from(!found)
5687    }
5688
5689    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5690    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5691        let seq_segs = ctx.find_segments("SEQ");
5692        let found = seq_segs.iter().any(|s| {
5693            s.elements
5694                .first()
5695                .and_then(|e| e.first())
5696                .is_some_and(|v| ["Z21"].contains(&v.as_str()))
5697        });
5698        ConditionResult::from(!found)
5699    }
5700
5701    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5702    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5703        let seq_segs = ctx.find_segments("SEQ");
5704        let found = seq_segs.iter().any(|s| {
5705            s.elements
5706                .first()
5707                .and_then(|e| e.first())
5708                .is_some_and(|v| ["Z22"].contains(&v.as_str()))
5709        });
5710        ConditionResult::from(found)
5711    }
5712
5713    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5714    fn evaluate_305(&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| ["Z24"].contains(&v.as_str()))
5721        });
5722        ConditionResult::from(found)
5723    }
5724
5725    /// [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
5726    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5727        // HAND-EDITED: "SG5 LOC+Z22 mit derselben Zeitraum-ID wie in dieser SG8 … des SEQ+Z58" — compared within the enclosing SG4 when the scope
5728        // reaches an SG8; the message as before otherwise.
5729        if let Some(scope) = ctx.enclosing_scope("SG8") {
5730            return ConditionResult::from(
5731                sg4_instances::seq_at(ctx, scope)
5732                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5733                    .is_some_and(|(_, zeitraum)| {
5734                        sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5735                    }),
5736            );
5737        }
5738        use std::collections::HashSet;
5739        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5740        let loc_z22_ids: HashSet<String> = ctx
5741            .find_segments_with_qualifier("LOC", 0, "Z22")
5742            .into_iter()
5743            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5744            .filter(|v| !v.is_empty())
5745            .collect();
5746        if loc_z22_ids.is_empty() {
5747            return ConditionResult::False;
5748        }
5749        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5750        let has_match = ctx
5751            .find_segments_with_qualifier("SEQ", 0, "Z58")
5752            .iter()
5753            .any(|s| {
5754                s.elements
5755                    .get(1)
5756                    .and_then(|e: &Vec<String>| e.first())
5757                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5758            });
5759        ConditionResult::from(has_match)
5760    }
5761
5762    /// [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
5763    // 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)
5764    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5765        // 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
5766        // reaches an SG8; the message as before otherwise.
5767        if let Some(scope) = ctx.enclosing_scope("SG8") {
5768            return ConditionResult::from(
5769                sg4_instances::seq_at(ctx, scope)
5770                    .filter(|(code, zeitraum)| code == "Z58" && !zeitraum.is_empty())
5771                    .is_some_and(|(_, zeitraum)| {
5772                        !sg4_instances::ruhende_malo_with_zeitraum(ctx, &zeitraum)
5773                    }),
5774            );
5775        }
5776        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5777        if seq_segs.is_empty() {
5778            return ConditionResult::Unknown;
5779        }
5780        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5781        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5782            .iter()
5783            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5784            .filter(|v| !v.is_empty())
5785            .collect();
5786        ConditionResult::from(seq_segs.iter().any(|seq| {
5787            let zid = seq
5788                .elements
5789                .get(1)
5790                .and_then(|e: &Vec<String>| e.first())
5791                .map(|v| v.as_str())
5792                .unwrap_or("");
5793            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5794        }))
5795    }
5796
5797    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5798    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5799        let loc_segs = ctx.find_segments("LOC");
5800        let found = loc_segs.iter().any(|s| {
5801            s.elements
5802                .first()
5803                .and_then(|e| e.first())
5804                .is_some_and(|v| v == "Z22")
5805        });
5806        ConditionResult::from(!found)
5807    }
5808
5809    /// [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...
5810    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5811        ctx.external.evaluate("code_list_membership_check")
5812    }
5813
5814    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5815    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5816        ctx.external.evaluate("data_clearing_required")
5817    }
5818
5819    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5820    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5821        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8,
5822        // whatever its SEQ (Z03, ZA3/ZA4, ZF5 all carry [316]; #305).
5823        ctx.this_group_lacks_qualifier("RFF", 0, "Z14", &["SG4", "SG8"])
5824    }
5825
5826    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5827    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5828        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5829        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5830        if found {
5831            ConditionResult::True
5832        } else {
5833            ConditionResult::Unknown
5834        }
5835    }
5836
5837    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5838    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5839        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5840        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5841        if found {
5842            ConditionResult::True
5843        } else {
5844            ConditionResult::Unknown
5845        }
5846    }
5847
5848    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5849    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5850        let com_segs = ctx.find_segments("COM");
5851        let has_em = com_segs.iter().any(|s| {
5852            s.elements
5853                .first()
5854                .and_then(|e| e.get(1))
5855                .is_some_and(|v| v == "EM")
5856        });
5857        ConditionResult::from(has_em)
5858    }
5859
5860    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5861    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5862        let com_segs = ctx.find_segments("COM");
5863        let has_phone = com_segs.iter().any(|s| {
5864            s.elements
5865                .first()
5866                .and_then(|e| e.get(1))
5867                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5868        });
5869        ConditionResult::from(has_phone)
5870    }
5871
5872    /// [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...
5873    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5874        ctx.external.evaluate("code_list_membership_check")
5875    }
5876
5877    /// [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...
5878    // 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)
5879    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5880        // HAND-EDITED: "die Zeitraum-ID im DE1050 dieser SG8 SEQ … einer SG6 RFF+Z47/Z48/Z49" — compared within the enclosing SG4 when the scope
5881        // reaches an SG8; the message as before otherwise.
5882        if let Some(scope) = ctx.enclosing_scope("SG8") {
5883            let Some((_, zeitraum)) =
5884                sg4_instances::seq_at(ctx, scope).filter(|(_, z)| !z.is_empty())
5885            else {
5886                return ConditionResult::False;
5887            };
5888            let sg6s = ctx.scopes_under("SG4", "SG6").unwrap_or_default();
5889            return ConditionResult::from(sg6s.iter().any(|p| {
5890                ctx.find_segments_at(p, "RFF").iter().any(|s| {
5891                    matches!(s.get_element(0), "Z47" | "Z48" | "Z49")
5892                        && s.get_component(0, 2) == zeitraum
5893                }) && ctx.find_segments_at(p, "DTM").iter().any(|s| {
5894                    s.get_element(0) == "Z25"
5895                        && s.get_component(0, 1).replace("?+", "+").as_str() >= "202212312300+00"
5896                })
5897            }));
5898        }
5899        const THRESHOLD: &str = "202212312300+00";
5900        let seq_zeitraum_ids: Vec<String> = ctx
5901            .find_segments("SEQ")
5902            .iter()
5903            .filter_map(|s| {
5904                s.elements
5905                    .get(1)
5906                    .and_then(|e: &Vec<String>| e.first())
5907                    .cloned()
5908            })
5909            .filter(|v| !v.is_empty())
5910            .collect();
5911        if seq_zeitraum_ids.is_empty() {
5912            return ConditionResult::Unknown;
5913        }
5914        let rff_segs = ctx.find_segments("RFF");
5915        let matching_rff = rff_segs.iter().any(|rff| {
5916            let qual = rff
5917                .elements
5918                .first()
5919                .and_then(|e: &Vec<String>| e.first())
5920                .map(|v| v.as_str())
5921                .unwrap_or("");
5922            if !["Z47", "Z48", "Z49"].contains(&qual) {
5923                return false;
5924            }
5925            let zid = rff
5926                .elements
5927                .first()
5928                .and_then(|e| e.get(2))
5929                .map(|v| v.as_str())
5930                .unwrap_or("");
5931            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5932        });
5933        if !matching_rff {
5934            return ConditionResult::False;
5935        }
5936        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5937            let val = dtm
5938                .elements
5939                .first()
5940                .and_then(|e| e.get(1))
5941                .map(|v| v.replace("?+", "+"))
5942                .unwrap_or_default();
5943            if !val.is_empty() && val.as_str() >= THRESHOLD {
5944                return ConditionResult::True;
5945            }
5946        }
5947        ConditionResult::False
5948    }
5949
5950    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5951    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5952        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
5953        // Check: STS+E01 does NOT have A03 with E_0096 or A04 with E_0097
5954        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
5955        let has_a03_e0096 = sts_segs.iter().any(|s| {
5956            s.elements
5957                .get(2)
5958                .and_then(|e| e.first())
5959                .is_some_and(|v| v == "A03")
5960                && s.elements
5961                    .get(2)
5962                    .and_then(|e| e.get(1))
5963                    .is_some_and(|v| v == "E_0096")
5964        });
5965        let has_a04_e0097 = sts_segs.iter().any(|s| {
5966            s.elements
5967                .get(2)
5968                .and_then(|e| e.first())
5969                .is_some_and(|v| v == "A04")
5970                && s.elements
5971                    .get(2)
5972                    .and_then(|e| e.get(1))
5973                    .is_some_and(|v| v == "E_0097")
5974        });
5975        ConditionResult::from(!has_a03_e0096 && !has_a04_e0097)
5976    }
5977
5978    /// [331] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5979    fn evaluate_331(&self, ctx: &EvaluationContext) -> ConditionResult {
5980        let seq_segs = ctx.find_segments("SEQ");
5981        let has_seq = seq_segs.iter().any(|s| {
5982            s.elements
5983                .first()
5984                .and_then(|e| e.first())
5985                .is_some_and(|v| v == "Z98")
5986        });
5987        let cci_segs = ctx.find_segments("CCI");
5988        let has_cci = cci_segs.iter().any(|s| {
5989            s.elements
5990                .first()
5991                .and_then(|e| e.first())
5992                .is_some_and(|v| v == "6")
5993                && s.elements
5994                    .get(2)
5995                    .and_then(|e| e.first())
5996                    .is_some_and(|v| v == "ZA9")
5997        });
5998        ConditionResult::from(!(has_seq && has_cci))
5999    }
6000
6001    /// [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
6002    // 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)
6003    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
6004        // HAND-EDITED: "im SG8 SEQ+Z01 mit identischer Zeitraum-ID wie in dieser SG8 … nicht vorhanden" — compared within the enclosing SG4 when the scope
6005        // reaches an SG8; the message as before otherwise.
6006        if let Some(scope) = ctx.enclosing_scope("SG8") {
6007            let z01s = sg4_instances::z01_of_zeitraum(ctx, scope);
6008            return ConditionResult::from(z01s.iter().any(|p| {
6009                !sg4_instances::sg10_at(ctx, p, |s| sg4_instances::has_cci(s, "6", "ZA9"))
6010            }));
6011        }
6012        let nav = match ctx.navigator {
6013            Some(n) => n,
6014            None => {
6015                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
6016                    return ConditionResult::Unknown;
6017                }
6018                let has_cci_za9 = ctx
6019                    .find_segments_with_qualifier("CCI", 0, "6")
6020                    .iter()
6021                    .any(|s| {
6022                        s.elements
6023                            .get(2)
6024                            .and_then(|e: &Vec<String>| e.first())
6025                            .map(|v| v.as_str())
6026                            == Some("ZA9")
6027                    });
6028                return ConditionResult::from(!has_cci_za9);
6029            }
6030        };
6031        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6032        if sg8_count == 0 {
6033            return ConditionResult::Unknown;
6034        }
6035        for sg8_idx in 0..sg8_count {
6036            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
6037            let mut has_cci_za9 = false;
6038            for sg10_idx in 0..sg10_count {
6039                let ccis = nav.find_segments_in_child_group(
6040                    "CCI",
6041                    &["SG4", "SG8"],
6042                    sg8_idx,
6043                    "SG10",
6044                    sg10_idx,
6045                );
6046                if ccis.iter().any(|s| {
6047                    s.elements
6048                        .first()
6049                        .and_then(|e: &Vec<String>| e.first())
6050                        .map(|v| v.as_str())
6051                        == Some("6")
6052                        && s.elements
6053                            .get(2)
6054                            .and_then(|e: &Vec<String>| e.first())
6055                            .map(|v| v.as_str())
6056                            == Some("ZA9")
6057                }) {
6058                    has_cci_za9 = true;
6059                    break;
6060                }
6061            }
6062            if !has_cci_za9 {
6063                return ConditionResult::True;
6064            }
6065        }
6066        ConditionResult::False
6067    }
6068
6069    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
6070    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
6071        let rff_segs = ctx.find_segments("RFF");
6072        let found = rff_segs.iter().any(|s| {
6073            s.elements
6074                .first()
6075                .and_then(|e| e.first())
6076                .is_some_and(|v| v == "Z31")
6077        });
6078        ConditionResult::from(found)
6079    }
6080
6081    /// [334] Nur MP-ID aus Sparte Gas
6082    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
6083        // Check: Marktpartner-ID is from Sparte Gas
6084        ctx.external.evaluate("sector_check")
6085    }
6086
6087    /// [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...
6088    // 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)
6089    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
6090        // HAND-EDITED: the Zeitraum-ID of *this* SG8's SEQ+Z58 (DE1050), looked
6091        // for in the RFF+Z31 of any SG8 SEQ+Z78 of the message.
6092        let z58_ids: Vec<String> = ctx
6093            .scoped_find_segments_in_with_qualifier("SG8", "SEQ", 0, "Z58")
6094            .iter()
6095            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).cloned())
6096            .filter(|v| !v.is_empty())
6097            .collect();
6098        if z58_ids.is_empty() {
6099            return ConditionResult::Unknown;
6100        }
6101        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
6102            return ConditionResult::False;
6103        }
6104        let rff_z31_ids: Vec<String> = ctx
6105            .find_segments_with_qualifier("RFF", 0, "Z31")
6106            .iter()
6107            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6108            .filter(|v| !v.is_empty())
6109            .collect();
6110        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6111    }
6112
6113    /// [336] Wenn in Änderungsmeldung gefüllt
6114    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6115        // TODO: implement
6116        ConditionResult::Unknown
6117    }
6118
6119    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6120    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6121        let cci_segs = ctx.find_segments("CCI");
6122        let has_cci = cci_segs.iter().any(|s| {
6123            s.elements
6124                .get(2)
6125                .and_then(|e| e.first())
6126                .is_some_and(|v| v == "ZB4")
6127        });
6128        let cav_segs = ctx.find_segments("CAV");
6129        let has_cav = cav_segs.iter().any(|s| {
6130            s.elements
6131                .first()
6132                .and_then(|e| e.first())
6133                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6134        });
6135        ConditionResult::from(has_cci && has_cav)
6136    }
6137
6138    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6139    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6140        let cci_segs = ctx.find_segments("CCI");
6141        let has_cci = cci_segs.iter().any(|s| {
6142            s.elements
6143                .get(2)
6144                .and_then(|e| e.first())
6145                .is_some_and(|v| v == "ZB4")
6146        });
6147        let cav_segs = ctx.find_segments("CAV");
6148        let has_cav = cav_segs.iter().any(|s| {
6149            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6150                ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str())
6151            })
6152        });
6153        ConditionResult::from(has_cci && has_cav)
6154    }
6155
6156    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6157    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6158        let cci_segs = ctx.find_segments("CCI");
6159        let has_cci = cci_segs.iter().any(|s| {
6160            s.elements
6161                .get(2)
6162                .and_then(|e| e.first())
6163                .is_some_and(|v| v == "ZB4")
6164        });
6165        let cav_segs = ctx.find_segments("CAV");
6166        let has_cav = cav_segs.iter().any(|s| {
6167            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6168                [
6169                    "Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7",
6170                ]
6171                .contains(&v.as_str())
6172            })
6173        });
6174        ConditionResult::from(has_cci && has_cav)
6175    }
6176
6177    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6178    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6179        let cci_segs = ctx.find_segments("CCI");
6180        let has_cci = cci_segs.iter().any(|s| {
6181            s.elements
6182                .get(2)
6183                .and_then(|e| e.first())
6184                .is_some_and(|v| v == "ZB4")
6185        });
6186        let cav_segs = ctx.find_segments("CAV");
6187        let has_cav = cav_segs.iter().any(|s| {
6188            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6189                [
6190                    "Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6",
6191                ]
6192                .contains(&v.as_str())
6193            })
6194        });
6195        ConditionResult::from(has_cci && has_cav)
6196    }
6197
6198    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6199    fn evaluate_341(&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
6210                .first()
6211                .and_then(|e| e.first())
6212                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6213        });
6214        ConditionResult::from(has_cci && has_cav)
6215    }
6216
6217    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6218    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6219        // TODO: implement
6220        ConditionResult::Unknown
6221    }
6222
6223    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6224    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6225        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6226        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6227        let has_cci = cci_segs.iter().any(|s| {
6228            s.elements
6229                .get(2)
6230                .and_then(|e| e.first())
6231                .is_some_and(|v| v == "ZB4")
6232        });
6233        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
6234        let has_cav = cav_segs.iter().any(|s| {
6235            s.elements
6236                .first()
6237                .and_then(|e| e.first())
6238                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6239        });
6240        ConditionResult::from(has_cci && has_cav)
6241    }
6242
6243    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6244    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6245        let loc_segs = ctx.find_segments("LOC");
6246        let found = loc_segs.iter().any(|s| {
6247            s.elements
6248                .first()
6249                .and_then(|e| e.first())
6250                .is_some_and(|v| v == "Z17")
6251        });
6252        ConditionResult::from(found)
6253    }
6254
6255    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) vorhanden
6256    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
6257        // HAND-EDITED: the CCI and the CAV of one SG10.
6258        sg10_has(ctx, false, |segs| {
6259            sg4_instances::has_cci(segs, "", "E13") && sg4_instances::has_cav(segs, "MME")
6260        })
6261    }
6262
6263    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6264    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6265        let loc_segs = ctx.find_segments("LOC");
6266        let found = loc_segs.iter().any(|s| {
6267            s.elements
6268                .first()
6269                .and_then(|e| e.first())
6270                .is_some_and(|v| v == "Z16")
6271        });
6272        ConditionResult::from(!found)
6273    }
6274
6275    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6276    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6277        let loc_segs = ctx.find_segments("LOC");
6278        let found = loc_segs.iter().any(|s| {
6279            s.elements
6280                .first()
6281                .and_then(|e| e.first())
6282                .is_some_and(|v| v == "Z21")
6283        });
6284        ConditionResult::from(!found)
6285    }
6286
6287    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6288    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6289        let loc_segs = ctx.find_segments("LOC");
6290        let found = loc_segs.iter().any(|s| {
6291            s.elements
6292                .first()
6293                .and_then(|e| e.first())
6294                .is_some_and(|v| v == "Z17")
6295        });
6296        ConditionResult::from(!found)
6297    }
6298
6299    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6300    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6301        let seq_segs = ctx.find_segments("SEQ");
6302        let has_seq = seq_segs.iter().any(|s| {
6303            s.elements
6304                .first()
6305                .and_then(|e| e.first())
6306                .is_some_and(|v| ["Z78", "ZD5"].contains(&v.as_str()))
6307        });
6308        let rff_segs = ctx.find_segments("RFF");
6309        let has_rff = rff_segs.iter().any(|s| {
6310            s.elements
6311                .first()
6312                .and_then(|e| e.first())
6313                .is_some_and(|v| v == "Z31")
6314        });
6315        ConditionResult::from(has_seq && has_rff)
6316    }
6317
6318    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6319    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6320        let sts_segs = ctx.find_segments("STS");
6321        let found = sts_segs.iter().any(|s| {
6322            s.elements
6323                .first()
6324                .and_then(|e| e.first())
6325                .is_some_and(|v| v == "E01")
6326                && s.elements
6327                    .get(2)
6328                    .and_then(|e| e.first())
6329                    .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6330        });
6331        ConditionResult::from(found)
6332    }
6333
6334    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6335    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6336        // HAND-EDITED: the answer stands in C556 DE9013 (element 2), as in [351].
6337        let sts_segs = ctx.find_segments("STS");
6338        let found = sts_segs.iter().any(|s| {
6339            s.elements
6340                .get(2)
6341                .and_then(|e| e.first())
6342                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6343        });
6344        ConditionResult::from(found)
6345    }
6346
6347    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6348    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6349        let sts_segs = ctx.find_segments("STS");
6350        let found = sts_segs.iter().any(|s| {
6351            s.elements
6352                .first()
6353                .and_then(|e| e.first())
6354                .is_some_and(|v| v == "E01")
6355                && s.elements
6356                    .get(2)
6357                    .and_then(|e| e.first())
6358                    .is_some_and(|v| ["A45"].contains(&v.as_str()))
6359        });
6360        ConditionResult::from(found)
6361    }
6362
6363    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6364    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6365        let sts_segs = ctx.find_segments("STS");
6366        let found = sts_segs.iter().any(|s| {
6367            s.elements
6368                .first()
6369                .and_then(|e| e.first())
6370                .is_some_and(|v| v == "E01")
6371                && s.elements
6372                    .get(2)
6373                    .and_then(|e| e.first())
6374                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6375        });
6376        ConditionResult::from(found)
6377    }
6378
6379    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6380    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6381        let sts_segs = ctx.find_segments("STS");
6382        let found = sts_segs.iter().any(|s| {
6383            s.elements
6384                .first()
6385                .and_then(|e| e.first())
6386                .is_some_and(|v| v == "E01")
6387                && s.elements
6388                    .get(2)
6389                    .and_then(|e| e.first())
6390                    .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6391        });
6392        ConditionResult::from(found)
6393    }
6394
6395    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6396    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6397        let sts_segs = ctx.find_segments("STS");
6398        let found = sts_segs.iter().any(|s| {
6399            s.elements
6400                .first()
6401                .and_then(|e| e.first())
6402                .is_some_and(|v| v == "E01")
6403                && s.elements
6404                    .get(2)
6405                    .and_then(|e| e.first())
6406                    .is_some_and(|v| ["A06"].contains(&v.as_str()))
6407        });
6408        ConditionResult::from(found)
6409    }
6410
6411    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6412    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6413    // Preserve across regeneration until the codegen prompt is updated.
6414    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6415        all_sts_e01_in_cluster(ctx, Cluster::is_ablehnung)
6416    }
6417
6418    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6419    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6420    // Preserve across regeneration until the codegen prompt is updated.
6421    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6422        all_sts_e01_in_cluster(ctx, Cluster::is_zustimmung)
6423    }
6424
6425    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6426    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6427        let sts_segs = ctx.find_segments("STS");
6428        let found = sts_segs.iter().any(|s| {
6429            s.elements
6430                .first()
6431                .and_then(|e| e.first())
6432                .is_some_and(|v| v == "E01")
6433                && s.elements
6434                    .get(2)
6435                    .and_then(|e| e.first())
6436                    .is_some_and(|v| ["A50"].contains(&v.as_str()))
6437        });
6438        ConditionResult::from(found)
6439    }
6440
6441    /// [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
6442    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6443        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
6444        // Check: STS+E01 does NOT have A04 with E_0047/E_0014 or A03 with E_0049/E_0004
6445        let sts_segs = ctx.scoped_find_segments_in_with_qualifier("SG4", "STS", 0, "E01");
6446        let has_excluded = sts_segs.iter().any(|s| {
6447            let code = s
6448                .elements
6449                .get(2)
6450                .and_then(|e| e.first())
6451                .map(|v| v.as_str());
6452            let ebd = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
6453            matches!(
6454                (code, ebd),
6455                (Some("A04"), Some("E_0047"))
6456                    | (Some("A04"), Some("E_0014"))
6457                    | (Some("A03"), Some("E_0049"))
6458                    | (Some("A03"), Some("E_0004"))
6459            )
6460        });
6461        ConditionResult::from(!has_excluded)
6462    }
6463
6464    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6465    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6466    // Preserve across regeneration until the codegen prompt is updated.
6467    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6468        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A30"])
6469    }
6470
6471    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6472    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6473        let sts_segs = ctx.find_segments("STS");
6474        let found = sts_segs.iter().any(|s| {
6475            s.elements
6476                .first()
6477                .and_then(|e| e.first())
6478                .is_some_and(|v| v == "E01")
6479                && s.elements
6480                    .get(2)
6481                    .and_then(|e| e.first())
6482                    .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6483        });
6484        ConditionResult::from(found)
6485    }
6486
6487    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6488    // HAND-EDITED: driven by per-EBD cluster data (mako_prozesse).
6489    // Preserve across regeneration until the codegen prompt is updated.
6490    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6491        ebd_ablehnung_except(ctx, "Z35", "E_0624", &["A41"])
6492    }
6493
6494    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6495    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6496        let cav_segs = ctx.find_segments("CAV");
6497        let found = cav_segs.iter().any(|s| {
6498            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6499                [
6500                    "TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT",
6501                ]
6502                .contains(&v.as_str())
6503            })
6504        });
6505        ConditionResult::from(found)
6506    }
6507
6508    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6509    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6510        let seq_segs = ctx.find_segments("SEQ");
6511        let found = seq_segs.iter().any(|s| {
6512            s.elements
6513                .first()
6514                .and_then(|e| e.first())
6515                .is_some_and(|v| ["Z38"].contains(&v.as_str()))
6516        });
6517        ConditionResult::from(!found)
6518    }
6519
6520    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6521    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6522        let cav_segs = ctx.find_segments("CAV");
6523        let found = cav_segs.iter().any(|s| {
6524            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6525                ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str())
6526            })
6527        });
6528        ConditionResult::from(found)
6529    }
6530
6531    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6532    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6533        // TODO: implement
6534        ConditionResult::Unknown
6535    }
6536
6537    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6538    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6539        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6540        ctx.lacks_qualifier("SEQ", 0, "Z24")
6541    }
6542
6543    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6544    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6545        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6546        ctx.lacks_qualifier("SEQ", 0, "Z22")
6547    }
6548
6549    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6550    // 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)
6551    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6552        let nav = match ctx.navigator {
6553            Some(n) => n,
6554            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6555        };
6556        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6557        for i in 0..sg8_count {
6558            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6559            for j in 0..sg10_count {
6560                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6561                let has_cci = ccis.iter().any(|s| {
6562                    s.elements
6563                        .first()
6564                        .and_then(|e: &Vec<String>| e.first())
6565                        .is_some_and(|v: &String| v == "15")
6566                        && s.elements
6567                            .get(2)
6568                            .and_then(|e: &Vec<String>| e.first())
6569                            .is_some_and(|v: &String| v == "Z21")
6570                });
6571                if has_cci {
6572                    let cavs =
6573                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6574                    if cavs.iter().any(|s| {
6575                        s.elements
6576                            .first()
6577                            .and_then(|e: &Vec<String>| e.first())
6578                            .is_some_and(|v: &String| v == "AUS")
6579                    }) {
6580                        return ConditionResult::True;
6581                    }
6582                }
6583            }
6584        }
6585        ConditionResult::False
6586    }
6587
6588    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6589    // 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)
6590    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6591        let nav = match ctx.navigator {
6592            Some(n) => n,
6593            None => {
6594                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6595                return match r {
6596                    ConditionResult::True => ConditionResult::False,
6597                    ConditionResult::False => ConditionResult::True,
6598                    ConditionResult::Unknown => ConditionResult::Unknown,
6599                };
6600            }
6601        };
6602        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6603        for i in 0..sg8_count {
6604            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6605            for j in 0..sg10_count {
6606                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6607                let has_cci = ccis.iter().any(|s| {
6608                    s.elements
6609                        .first()
6610                        .and_then(|e: &Vec<String>| e.first())
6611                        .is_some_and(|v: &String| v == "15")
6612                        && s.elements
6613                            .get(2)
6614                            .and_then(|e: &Vec<String>| e.first())
6615                            .is_some_and(|v: &String| v == "Z21")
6616                });
6617                if has_cci {
6618                    let cavs =
6619                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6620                    if cavs.iter().any(|s| {
6621                        s.elements
6622                            .first()
6623                            .and_then(|e: &Vec<String>| e.first())
6624                            .is_some_and(|v: &String| v == "AUS")
6625                    }) {
6626                        return ConditionResult::False;
6627                    }
6628                }
6629            }
6630        }
6631        ConditionResult::True
6632    }
6633
6634    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6635    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6636        let cci_segs = ctx.find_segments("CCI");
6637        let found = cci_segs.iter().any(|s| {
6638            s.elements
6639                .first()
6640                .and_then(|e| e.first())
6641                .is_some_and(|v| v == "15")
6642                && s.elements
6643                    .get(2)
6644                    .and_then(|e| e.first())
6645                    .is_some_and(|v| ["Z21"].contains(&v.as_str()))
6646        });
6647        ConditionResult::from(found)
6648    }
6649
6650    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6651    // 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)
6652    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6653        let nav = match ctx.navigator {
6654            Some(n) => n,
6655            None => {
6656                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6657            }
6658        };
6659        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6660        for i in 0..sg8_count {
6661            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6662            for j in 0..sg10_count {
6663                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6664                let has_cci = ccis.iter().any(|s| {
6665                    s.elements
6666                        .first()
6667                        .and_then(|e: &Vec<String>| e.first())
6668                        .is_some_and(|v: &String| v == "15")
6669                        && s.elements
6670                            .get(2)
6671                            .and_then(|e: &Vec<String>| e.first())
6672                            .is_some_and(|v: &String| v == "Z21")
6673                });
6674                if has_cci {
6675                    let cavs =
6676                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6677                    if cavs.iter().any(|s| {
6678                        s.elements
6679                            .first()
6680                            .and_then(|e: &Vec<String>| e.first())
6681                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6682                    }) {
6683                        return ConditionResult::True;
6684                    }
6685                }
6686            }
6687        }
6688        ConditionResult::False
6689    }
6690
6691    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6692    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6693        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6694        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
6695        let found = cci_segs.iter().any(|s| {
6696            s.elements
6697                .first()
6698                .and_then(|e| e.first())
6699                .is_some_and(|v| v == "Z37")
6700                && s.elements
6701                    .get(2)
6702                    .and_then(|e| e.first())
6703                    .is_some_and(|v| v == "ZD1")
6704        });
6705        ConditionResult::from(found)
6706    }
6707
6708    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6709    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6710        // Check: more than one SG8 SEQ+Z02/ZE3 with OBIS energy quantity in PIA+5
6711        let seq_count = ctx
6712            .find_segments("SEQ")
6713            .iter()
6714            .filter(|s| {
6715                s.elements
6716                    .first()
6717                    .and_then(|e| e.first())
6718                    .is_some_and(|v| v == "Z02" || v == "ZE3")
6719            })
6720            .count();
6721        ConditionResult::from(seq_count > 1)
6722    }
6723
6724    /// [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...
6725    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
6726        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6727        // Check: same SG8 (OBIS Daten) has OBIS energy quantity code in PIA+5
6728        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6729        let found = pia_segs.iter().any(|s| {
6730            s.elements
6731                .first()
6732                .and_then(|e| e.first())
6733                .is_some_and(|v| v == "5")
6734                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6735                    v.starts_with("1-")
6736                        && (v.contains(":1.8.")
6737                            || v.contains(":2.8.")
6738                            || v.contains(":1.9.")
6739                            || v.contains(":2.9."))
6740                })
6741        });
6742        ConditionResult::from(found)
6743    }
6744
6745    /// [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...
6746    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6747        // Check: at least one RFF+Z49 (Zeitraum) exists
6748        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6749        ConditionResult::from(found)
6750    }
6751
6752    /// [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...
6753    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6754        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6755        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20,
6756        // with a tariff e = 1..62 (#351: 1-0:1.8.0 has no Zählzeit).
6757        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6758        let found = pia_segs.iter().any(|s| {
6759            s.elements
6760                .first()
6761                .and_then(|e| e.first())
6762                .is_some_and(|v| v == "5")
6763                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6764                    v.starts_with("1-")
6765                        && (v.contains(":1.8.") || v.contains(":2.8."))
6766                        && obis_tariff(v).is_some_and(|e| (1..=62).contains(&e))
6767                })
6768        });
6769        ConditionResult::from(found)
6770    }
6771
6772    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6773    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6774        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6775        let pia_segs = ctx.find_segments("PIA");
6776        let found = pia_segs.iter().any(|s| {
6777            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6778                let parts: Vec<&str> = v.split('-').collect();
6779                parts.len() == 3
6780                    && parts[0].len() == 1
6781                    && parts[0].chars().all(|c| c.is_ascii_digit())
6782                    && parts[1].len() == 2
6783                    && parts[1].chars().all(|c| c.is_ascii_digit())
6784                    && parts[2].len() == 1
6785                    && parts[2].chars().all(|c| c.is_ascii_digit())
6786            })
6787        });
6788        ConditionResult::from(found)
6789    }
6790
6791    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6792    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6793        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6794        let pia_segs = ctx.find_segments("PIA");
6795        let found = pia_segs.iter().any(|s| {
6796            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6797                let parts: Vec<&str> = v.split('-').collect();
6798                parts.len() == 4
6799                    && parts[0].len() == 1
6800                    && parts[0].chars().all(|c| c.is_ascii_digit())
6801                    && parts[1].len() == 2
6802                    && parts[1].chars().all(|c| c.is_ascii_digit())
6803                    && parts[2].len() == 1
6804                    && parts[2].chars().all(|c| c.is_ascii_digit())
6805                    && parts[3].len() == 3
6806                    && parts[3].chars().all(|c| c.is_ascii_digit())
6807            })
6808        });
6809        ConditionResult::from(found)
6810    }
6811
6812    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6813    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6814        // Check: PIA code in format n1-n2-n1-n8-n2
6815        let pia_segs = ctx.find_segments("PIA");
6816        let found = pia_segs.iter().any(|s| {
6817            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6818                let parts: Vec<&str> = v.split('-').collect();
6819                parts.len() == 5
6820                    && parts[0].len() == 1
6821                    && parts[0].chars().all(|c| c.is_ascii_digit())
6822                    && parts[1].len() == 2
6823                    && parts[1].chars().all(|c| c.is_ascii_digit())
6824                    && parts[2].len() == 1
6825                    && parts[2].chars().all(|c| c.is_ascii_digit())
6826                    && parts[3].len() == 8
6827                    && parts[3].chars().all(|c| c.is_ascii_digit())
6828                    && parts[4].len() == 2
6829                    && parts[4].chars().all(|c| c.is_ascii_digit())
6830            })
6831        });
6832        ConditionResult::from(found)
6833    }
6834
6835    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6836    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6837        // Check: PIA code in format n1-n2-n1-n8
6838        let pia_segs = ctx.find_segments("PIA");
6839        let found = pia_segs.iter().any(|s| {
6840            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6841                let parts: Vec<&str> = v.split('-').collect();
6842                parts.len() == 4
6843                    && parts[0].len() == 1
6844                    && parts[0].chars().all(|c| c.is_ascii_digit())
6845                    && parts[1].len() == 2
6846                    && parts[1].chars().all(|c| c.is_ascii_digit())
6847                    && parts[2].len() == 1
6848                    && parts[2].chars().all(|c| c.is_ascii_digit())
6849                    && parts[3].len() == 8
6850                    && parts[3].chars().all(|c| c.is_ascii_digit())
6851            })
6852        });
6853        ConditionResult::from(found)
6854    }
6855
6856    /// [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
6857    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6858        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
6859        // Check: OBIS Wirkarbeit und 1/4 Stunde code in PIA+5
6860        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6861        let found = pia_segs.iter().any(|s| {
6862            s.elements
6863                .first()
6864                .and_then(|e| e.first())
6865                .is_some_and(|v| v == "5")
6866                && s.elements
6867                    .get(1)
6868                    .and_then(|e| e.first())
6869                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
6870        });
6871        ConditionResult::from(found)
6872    }
6873
6874    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6875    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6876        let pia_segs = ctx.find_segments("PIA");
6877        let found = pia_segs.iter().any(|s| {
6878            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6879                let prefixes = ["1-08-1", "1-08-4"];
6880                prefixes.iter().any(|p| v.starts_with(p))
6881            })
6882        });
6883        ConditionResult::from(found)
6884    }
6885
6886    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6887    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6888        let pia_segs = ctx.find_segments("PIA");
6889        let found = pia_segs.iter().any(|s| {
6890            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6891                let prefixes = ["1-08-2", "1-08-5"];
6892                prefixes.iter().any(|p| v.starts_with(p))
6893            })
6894        });
6895        ConditionResult::from(found)
6896    }
6897
6898    /// [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...
6899    // 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)
6900    fn evaluate_399(&self, ctx: &EvaluationContext) -> ConditionResult {
6901        let nav = match ctx.navigator() {
6902            Some(n) => n,
6903            None => return ConditionResult::Unknown,
6904        };
6905        // Collect DE3224 values from LOC segments in SG5 — LOC.C517.DE3224 = elements[1][3]
6906        // These serve as the Zeitraum-IDs referenced by the condition's "dieses Segments"
6907        let sg5_count = nav.group_instance_count(&["SG4", "SG5"]);
6908        let mut loc_zeitraum_ids: Vec<String> = Vec::new();
6909        for i in 0..sg5_count {
6910            let locs = nav.find_segments_in_group("LOC", &["SG4", "SG5"], i);
6911            for loc in &locs {
6912                if let Some(zid) = loc.elements.get(1).and_then(|e| e.get(3)) {
6913                    if !zid.is_empty() {
6914                        loc_zeitraum_ids.push(zid.clone());
6915                    }
6916                }
6917            }
6918        }
6919        if loc_zeitraum_ids.is_empty() {
6920            return ConditionResult::Unknown;
6921        }
6922        let pauschal_id = "9992000000018";
6923        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6924        // Find any Z78 SG8 whose Zeitraum-ID (SEQ.C286.DE1050) matches a LOC DE3224
6925        // and that has RFF+Z31 with DE1154 != pauschal_id
6926        for i in 0..sg8_count {
6927            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
6928            for seq in &seqs {
6929                if !seq
6930                    .elements
6931                    .first()
6932                    .and_then(|e| e.first())
6933                    .is_some_and(|v| v == "Z78")
6934                {
6935                    continue;
6936                }
6937                let zid = seq
6938                    .elements
6939                    .get(1)
6940                    .and_then(|e| e.first())
6941                    .map(|s| s.as_str())
6942                    .unwrap_or("");
6943                if !loc_zeitraum_ids.iter().any(|z| z == zid) {
6944                    continue;
6945                }
6946                // Matching Z78 SG8 found — check RFF+Z31 DE1154
6947                let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
6948                for rff in &rffs {
6949                    let qual = rff
6950                        .elements
6951                        .first()
6952                        .and_then(|e| e.first())
6953                        .map(|v| v.as_str())
6954                        .unwrap_or("");
6955                    if qual == "Z31" {
6956                        let id = rff
6957                            .elements
6958                            .first()
6959                            .and_then(|e| e.get(1))
6960                            .map(|v| v.as_str())
6961                            .unwrap_or("");
6962                        let normalized = id.replace(' ', "");
6963                        if !normalized.is_empty() && normalized != pauschal_id {
6964                            return ConditionResult::True;
6965                        }
6966                    }
6967                }
6968            }
6969        }
6970        ConditionResult::False
6971    }
6972
6973    /// [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+...
6974    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6975        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
6976        // Approximate: check if RFF+Z49 or Z53 exists
6977        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6978        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
6979        ConditionResult::from(!has_z49 && !has_z53)
6980    }
6981
6982    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6983    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6984        // HAND-EDITED: "in derselben SG8" — scoped to the enclosing SG8.
6985        // Check: OBIS Zählerstand code in PIA+5
6986        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
6987        let found = pia_segs.iter().any(|s| {
6988            s.elements
6989                .first()
6990                .and_then(|e| e.first())
6991                .is_some_and(|v| v == "5")
6992                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6993                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6994                })
6995        });
6996        ConditionResult::from(found)
6997    }
6998
6999    /// [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
7000    // 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)
7001    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
7002        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
7003            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
7004                Some(id) if !id.is_empty() => id.clone(),
7005                _ => return ConditionResult::Unknown,
7006            },
7007            None => return ConditionResult::Unknown,
7008        };
7009        let nav = match ctx.navigator {
7010            Some(n) => n,
7011            None => return ConditionResult::Unknown,
7012        };
7013        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7014        for i in 0..sg8_count {
7015            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7016            for j in 0..sg10_count {
7017                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7018                let has_cci_zb3 = ccis.iter().any(|s| {
7019                    s.elements
7020                        .get(2)
7021                        .and_then(|e: &Vec<String>| e.first())
7022                        .is_some_and(|v: &String| v == "ZB3")
7023                });
7024                if !has_cci_zb3 {
7025                    continue;
7026                }
7027                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7028                for cav in &cavs {
7029                    if cav
7030                        .elements
7031                        .first()
7032                        .and_then(|e: &Vec<String>| e.first())
7033                        .is_some_and(|v: &String| v == "Z91")
7034                    {
7035                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
7036                            if !msb_id.is_empty() {
7037                                return ConditionResult::from(
7038                                    sender_mp_id.as_str() != msb_id.as_str(),
7039                                );
7040                            }
7041                        }
7042                    }
7043                }
7044            }
7045        }
7046        ConditionResult::Unknown
7047    }
7048
7049    /// [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
7050    // 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)
7051    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
7052        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
7053        let mr_mp_id = match mr_nads.first() {
7054            Some(nad) => nad
7055                .elements
7056                .get(1)
7057                .and_then(|e: &Vec<String>| e.first())
7058                .cloned()
7059                .unwrap_or_default(),
7060            None => return ConditionResult::Unknown,
7061        };
7062        if mr_mp_id.is_empty() {
7063            return ConditionResult::Unknown;
7064        }
7065        // HAND-EDITED: "im SG8 SEQ+Z01/ Z98 … SG10 CCI+++ZB3 CAV+Z91" — the MSB in
7066        // the Marktlokation data of this SG4, of the MaLo this SG8 refers to
7067        // (RFF+Z18) where it refers to one, when the scope reaches an SG8. Any
7068        // SG8 of the message (the MeLo's SEQ+Z18 names the recipient by design)
7069        // otherwise, as before.
7070        if let Some(scope) = ctx.enclosing_scope("SG8") {
7071            let refs = sg4_instances::rff_values_at(ctx, scope, "Z18");
7072            let names_recipient = |s: &[OwnedSegment]| {
7073                sg4_instances::has_cci(s, "", "ZB3")
7074                    && s.iter().any(|c| {
7075                        c.id == "CAV"
7076                            && c.get_element(0) == "Z91"
7077                            && c.get_component(0, 1) == mr_mp_id
7078                    })
7079            };
7080            let same = sg4_instances::sg8s(ctx).iter().any(|p| {
7081                sg4_instances::seq_at(ctx, p)
7082                    .is_some_and(|(code, _)| code == "Z01" || code == "Z98")
7083                    && (refs.is_empty()
7084                        || sg4_instances::rff_values_at(ctx, p, "Z18")
7085                            .iter()
7086                            .any(|v| refs.contains(v)))
7087                    && sg4_instances::sg10_at(ctx, p, names_recipient)
7088            });
7089            return ConditionResult::from(!same);
7090        }
7091
7092        let nav = match ctx.navigator {
7093            Some(n) => n,
7094            None => {
7095                let cavs = ctx.find_segments("CAV");
7096                let same_id_exists = cavs.iter().any(|s| {
7097                    s.elements
7098                        .first()
7099                        .and_then(|e: &Vec<String>| e.first())
7100                        .is_some_and(|v: &String| v == "Z91")
7101                        && s.elements
7102                            .first()
7103                            .and_then(|e| e.get(1))
7104                            .is_some_and(|v: &String| v == &mr_mp_id)
7105                });
7106                return ConditionResult::from(!same_id_exists);
7107            }
7108        };
7109
7110        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7111        for i in 0..sg8_count {
7112            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7113            for j in 0..sg10_count {
7114                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7115                let has_cci_zb3 = ccis.iter().any(|s| {
7116                    s.elements
7117                        .get(2)
7118                        .and_then(|e: &Vec<String>| e.first())
7119                        .is_some_and(|v: &String| v == "ZB3")
7120                });
7121                if !has_cci_zb3 {
7122                    continue;
7123                }
7124                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
7125                for cav in &cavs {
7126                    if cav
7127                        .elements
7128                        .first()
7129                        .and_then(|e: &Vec<String>| e.first())
7130                        .is_some_and(|v: &String| v == "Z91")
7131                    {
7132                        let cav_mp_id = cav
7133                            .elements
7134                            .first()
7135                            .and_then(|e| e.get(1))
7136                            .map(|s| s.as_str())
7137                            .unwrap_or("");
7138                        if cav_mp_id == mr_mp_id.as_str() {
7139                            return ConditionResult::False;
7140                        }
7141                    }
7142                }
7143            }
7144        }
7145
7146        ConditionResult::True
7147    }
7148
7149    /// [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
7150    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
7151        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7152        // Check: OBIS Wirkarbeit kumuliert code in PIA+5
7153        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7154        let found = pia_segs.iter().any(|s| {
7155            s.elements
7156                .first()
7157                .and_then(|e| e.first())
7158                .is_some_and(|v| v == "5")
7159                && s.elements
7160                    .get(1)
7161                    .and_then(|e| e.first())
7162                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7163        });
7164        ConditionResult::from(found)
7165    }
7166
7167    /// [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
7168    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
7169        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7170        // Check: OBIS Wirkarbeit höchste 1/4 Stunde code in PIA+5
7171        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7172        let found = pia_segs.iter().any(|s| {
7173            s.elements
7174                .first()
7175                .and_then(|e| e.first())
7176                .is_some_and(|v| v == "5")
7177                && s.elements
7178                    .get(1)
7179                    .and_then(|e| e.first())
7180                    .is_some_and(|v| v.starts_with("1-") && v.contains(":1.8."))
7181        });
7182        ConditionResult::from(found)
7183    }
7184
7185    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
7186    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
7187        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7188        // Check: OBIS Blindarbeit code in PIA+5
7189        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7190        let found = pia_segs.iter().any(|s| {
7191            s.elements
7192                .first()
7193                .and_then(|e| e.first())
7194                .is_some_and(|v| v == "5")
7195                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
7196                    v.starts_with("1-") && (v.contains(":3.8.") || v.contains(":4.8."))
7197                })
7198        });
7199        ConditionResult::from(found)
7200    }
7201
7202    /// [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...
7203    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
7204        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7205        // reaches an SG8; the message as before otherwise.
7206        if let Some(scope) = ctx.enclosing_scope("SG8") {
7207            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7208                ctx,
7209                scope,
7210                is_obis_wirkarbeit_kumuliert,
7211            ));
7212        }
7213        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7214        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7215        ConditionResult::from(has_seq_z02)
7216    }
7217
7218    /// [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 ...
7219    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
7220        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7221        // reaches an SG8; the message as before otherwise.
7222        if let Some(scope) = ctx.enclosing_scope("SG8") {
7223            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7224                ctx,
7225                scope,
7226                is_obis_wirkarbeit_kumuliert,
7227            ));
7228        }
7229        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
7230        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7231        ConditionResult::from(!has_seq_z02)
7232    }
7233
7234    /// [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...
7235    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
7236        // HAND-EDITED: "eine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7237        // reaches an SG8; the message as before otherwise.
7238        if let Some(scope) = ctx.enclosing_scope("SG8") {
7239            return ConditionResult::from(sg4_instances::z02_of_same_malo_has(
7240                ctx,
7241                scope,
7242                is_obis_wirkarbeit_quarter_hour,
7243            ));
7244        }
7245        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7246        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7247        ConditionResult::from(has_seq_z02)
7248    }
7249
7250    /// [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 ...
7251    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
7252        // HAND-EDITED: "keine SG8 SEQ+Z02 … wie das RFF+Z18 in dieser SG8" — compared within the enclosing SG4 when the scope
7253        // reaches an SG8; the message as before otherwise.
7254        if let Some(scope) = ctx.enclosing_scope("SG8") {
7255            return ConditionResult::from(!sg4_instances::z02_of_same_malo_has(
7256                ctx,
7257                scope,
7258                is_obis_wirkarbeit_quarter_hour,
7259            ));
7260        }
7261        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
7262        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
7263        ConditionResult::from(!has_seq_z02)
7264    }
7265
7266    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
7267    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
7268        ctx.external.evaluate("code_list_membership_check")
7269    }
7270
7271    /// [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
7272    fn evaluate_413(&self, ctx: &EvaluationContext) -> ConditionResult {
7273        // Check: SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7274        let found = ctx.find_segments("SEQ").iter().any(|s| {
7275            s.elements
7276                .first()
7277                .and_then(|e| e.first())
7278                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7279        });
7280        ConditionResult::from(found)
7281    }
7282
7283    /// [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
7284    fn evaluate_414(&self, ctx: &EvaluationContext) -> ConditionResult {
7285        // Check: no SG8 SEQ+Z02/ZA1/ZA2 with OBIS Wirkarbeit kumuliert exists
7286        let found = ctx.find_segments("SEQ").iter().any(|s| {
7287            s.elements
7288                .first()
7289                .and_then(|e| e.first())
7290                .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7291        });
7292        ConditionResult::from(!found)
7293    }
7294
7295    /// [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
7296    fn evaluate_415(&self, ctx: &EvaluationContext) -> ConditionResult {
7297        // Check: another SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7298        let count = ctx
7299            .find_segments("SEQ")
7300            .iter()
7301            .filter(|s| {
7302                s.elements
7303                    .first()
7304                    .and_then(|e| e.first())
7305                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7306            })
7307            .count();
7308        ConditionResult::from(count > 1)
7309    }
7310
7311    /// [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
7312    fn evaluate_416(&self, ctx: &EvaluationContext) -> ConditionResult {
7313        // Check: no other SG8 SEQ+Z02/ZA1/ZA2 with OBIS 1/4 Stunde exists
7314        let count = ctx
7315            .find_segments("SEQ")
7316            .iter()
7317            .filter(|s| {
7318                s.elements
7319                    .first()
7320                    .and_then(|e| e.first())
7321                    .is_some_and(|v| ["Z02", "ZA1", "ZA2"].contains(&v.as_str()))
7322            })
7323            .count();
7324        ConditionResult::from(count <= 1)
7325    }
7326
7327    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
7328    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
7329        // TODO: implement
7330        ConditionResult::Unknown
7331    }
7332
7333    /// [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
7334    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
7335        // TODO: implement
7336        ConditionResult::Unknown
7337    }
7338
7339    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
7340    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
7341        // HAND-EDITED: "in dieser SG8" — scoped to the enclosing SG8.
7342        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7343        let found = rff_segs.iter().any(|s| {
7344            s.elements
7345                .first()
7346                .and_then(|e| e.first())
7347                .is_some_and(|v| v == "Z14")
7348        });
7349        ConditionResult::from(found)
7350    }
7351
7352    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
7353    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
7354        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing group.
7355        // Check: CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden
7356        ctx.this_group_has_qualifier("CCI", 0, "Z39", &["SG4", "SG8"])
7357    }
7358
7359    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
7360    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
7361        ctx.external.evaluate("code_list_membership_check")
7362    }
7363
7364    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
7365    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
7366        ctx.external.evaluate("code_list_membership_check")
7367    }
7368
7369    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
7370    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
7371        ctx.external.evaluate("code_list_membership_check")
7372    }
7373
7374    /// [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...
7375    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
7376        ctx.external.evaluate("code_list_membership_check")
7377    }
7378
7379    /// [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...
7380    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
7381        ctx.external.evaluate("code_list_membership_check")
7382    }
7383
7384    /// [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...
7385    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
7386        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
7387        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7388        ConditionResult::from(seq_z27_count > 1)
7389    }
7390
7391    /// [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+...
7392    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
7393        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
7394        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
7395        ConditionResult::from(seq_z27_count <= 1)
7396    }
7397
7398    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
7399    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
7400        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7401        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
7402        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7403        let found = pia_segs.iter().any(|s| {
7404            s.elements
7405                .first()
7406                .and_then(|e| e.first())
7407                .is_some_and(|v| v == "5")
7408                && s.elements
7409                    .get(1)
7410                    .and_then(|e| e.first())
7411                    .is_some_and(|v| v == "9991000000078")
7412        });
7413        ConditionResult::from(found)
7414    }
7415
7416    /// [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 ...
7417    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
7418        ctx.external.evaluate("code_list_membership_check")
7419    }
7420
7421    /// [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...
7422    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
7423        ctx.external.evaluate("code_list_membership_check")
7424    }
7425
7426    /// [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
7427    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
7428        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7429        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
7430        let pia_segs = ctx.scoped_find_segments_in("SG8", "PIA");
7431        let found = pia_segs.iter().any(|s| {
7432            s.elements
7433                .first()
7434                .and_then(|e| e.first())
7435                .is_some_and(|v| v == "5")
7436                && s.elements
7437                    .get(1)
7438                    .and_then(|e| e.first())
7439                    .is_some_and(|v| v == "9991000000086")
7440        });
7441        ConditionResult::from(found)
7442    }
7443
7444    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
7445    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
7446        // HAND-EDITED: "in dieser/derselben SG4" — scoped to the enclosing SG4.
7447        let sts_segs = ctx.scoped_find_segments_in("SG4", "STS");
7448        let found = sts_segs.iter().any(|s| {
7449            s.elements
7450                .first()
7451                .and_then(|e| e.first())
7452                .is_some_and(|v| v == "E01")
7453                && s.elements
7454                    .get(2)
7455                    .and_then(|e| e.first())
7456                    .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
7457        });
7458        ConditionResult::from(found)
7459    }
7460
7461    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
7462    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
7463        // HAND-EDITED: "im selben CCI" — the CCI the field is in (its own segment
7464        // in tree validation), else the enclosing SG10's CCI.
7465        let is_z39 = |elements: &[Vec<String>]| {
7466            elements
7467                .first()
7468                .and_then(|e| e.first())
7469                .is_some_and(|v| v == "Z39")
7470        };
7471        if let Some(own) = ctx.resolved_segment.filter(|_| {
7472            ctx.scoped_find_segments_in("SG10", "CCI")
7473                .iter()
7474                .any(|s| ctx.resolved_segment == Some(&s.elements[..]))
7475        }) {
7476            return ConditionResult::from(is_z39(own));
7477        }
7478        ConditionResult::from(
7479            ctx.scoped_find_segments_in("SG10", "CCI")
7480                .iter()
7481                .any(|s| is_z39(&s.elements)),
7482        )
7483    }
7484
7485    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
7486    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
7487        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7488        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7489        // alternative of the `⊻` held and the XOR failed on each RFF).
7490        same_rff_qualifier(ctx, "Z35")
7491    }
7492
7493    /// [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
7494    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
7495        // HAND-EDITED (#379): the Zähleinrichtungsdaten of the meter this RFF
7496        // names — the SEQ+Z03/ZF5 whose SG10 CAV+Z30 carries its number in
7497        // DE7110 — not any SEQ+Z03 of the message. No such SG8: no RFF+Z14
7498        // for that meter either.
7499        let Some(number) = rff_reference(ctx) else {
7500            return ConditionResult::Unknown;
7501        };
7502        let Some(blocks) = sg8_blocks(ctx) else {
7503            return ConditionResult::Unknown;
7504        };
7505        let meter = blocks
7506            .iter()
7507            .filter(|b| b.seq == "Z03" || b.seq == "ZF5")
7508            .find(|b| {
7509                b.sg10.iter().any(|(_, cavs)| {
7510                    cavs.iter().any(|c| {
7511                        component(c, 0, 0) == Some("Z30") && component(c, 0, 3) == Some(number)
7512                    })
7513                })
7514            });
7515        ConditionResult::from(meter.map_or(true, |b| {
7516            !b.rffs.iter().any(|r| component(r, 0, 0) == Some("Z14"))
7517        }))
7518    }
7519
7520    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
7521    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
7522        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7523        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7524        // alternative of the `⊻` held and the XOR failed on each RFF).
7525        same_rff_qualifier(ctx, "Z34")
7526    }
7527
7528    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
7529    // 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)
7530    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
7531        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7532        // scope reaches one; the message as before otherwise.
7533        if let Some(scope) = ctx.enclosing_scope("SG8") {
7534            return ConditionResult::from(
7535                ctx.find_segments_at(scope, "SEQ")
7536                    .iter()
7537                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7538                    && !ctx
7539                        .find_segments_at(scope, "PIA")
7540                        .iter()
7541                        .any(|s| s.get_element(0) == "5"),
7542            );
7543        }
7544        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
7545            if matches!(
7546                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
7547                ConditionResult::True
7548            ) {
7549                return ConditionResult::True;
7550            }
7551        }
7552        ConditionResult::False
7553    }
7554
7555    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
7556    // 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)
7557    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
7558        // HAND-EDITED: "in dieser/derselben SG8" — the enclosing SG8 only, when the
7559        // scope reaches one; the message as before otherwise.
7560        if let Some(scope) = ctx.enclosing_scope("SG8") {
7561            return ConditionResult::from(
7562                ctx.find_segments_at(scope, "SEQ")
7563                    .iter()
7564                    .any(|s| matches!(s.get_element(0), "Z60" | "ZE0" | "ZG8" | "ZG9"))
7565                    && !ctx
7566                        .find_segments_at(scope, "CCI")
7567                        .iter()
7568                        .any(|s| s.get_element(0) == "11"),
7569            );
7570        }
7571        let nav = match ctx.navigator {
7572            Some(n) => n,
7573            None => {
7574                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
7575                    matches!(
7576                        ctx.filtered_parent_child_has_qualifier(
7577                            &["SG4", "SG8"],
7578                            "SEQ",
7579                            0,
7580                            code,
7581                            "SG10",
7582                            "CCI",
7583                            0,
7584                            "11",
7585                        ),
7586                        ConditionResult::True
7587                    )
7588                });
7589                return ConditionResult::from(!has_cci11);
7590            }
7591        };
7592
7593        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7594        for i in 0..sg8_count {
7595            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7596            let has_cci11 = (0..sg10_count).any(|j| {
7597                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7598                ccis.iter().any(|s| {
7599                    s.elements
7600                        .first()
7601                        .and_then(|e: &Vec<String>| e.first())
7602                        .is_some_and(|v: &String| v == "11")
7603                })
7604            });
7605            if !has_cci11 {
7606                return ConditionResult::True;
7607            }
7608        }
7609        ConditionResult::False
7610    }
7611
7612    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7613    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7614        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7615        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7616        let found = cci_segs.iter().any(|s| {
7617            s.elements
7618                .first()
7619                .and_then(|e| e.first())
7620                .is_some_and(|v| v == "Z17")
7621        });
7622        ConditionResult::from(found)
7623    }
7624
7625    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7626    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7627        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7628        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7629        let found = cci_segs.iter().any(|s| {
7630            s.elements
7631                .first()
7632                .and_then(|e| e.first())
7633                .is_some_and(|v| v == "Z50")
7634        });
7635        ConditionResult::from(found)
7636    }
7637
7638    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7639    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7640        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7641        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7642        let found = cci_segs.iter().any(|s| {
7643            s.elements
7644                .first()
7645                .and_then(|e| e.first())
7646                .is_some_and(|v| v == "Z56")
7647        });
7648        ConditionResult::from(found)
7649    }
7650
7651    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7652    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7653        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7654        let cci_segs = ctx.scoped_find_segments_in("SG8", "CCI");
7655        let has_cci = cci_segs.iter().any(|s| {
7656            s.elements
7657                .first()
7658                .and_then(|e| e.first())
7659                .is_some_and(|v| v == "6")
7660                && s.elements
7661                    .get(2)
7662                    .and_then(|e| e.first())
7663                    .is_some_and(|v| v == "ZA9")
7664        });
7665        let cav_segs = ctx.scoped_find_segments_in("SG8", "CAV");
7666        let has_cav = cav_segs.iter().any(|s| {
7667            s.elements
7668                .first()
7669                .and_then(|e| e.first())
7670                .is_some_and(|v| v == "ZG3")
7671        });
7672        ConditionResult::from(has_cci && has_cav)
7673    }
7674
7675    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7676    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7677        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7678        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7679        // alternative of the `⊻` held and the XOR failed on each RFF).
7680        same_rff_qualifier(ctx, "Z32")
7681    }
7682
7683    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7684    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7685        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7686        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7687        // alternative of the `⊻` held and the XOR failed on each RFF).
7688        same_rff_qualifier(ctx, "Z18")
7689    }
7690
7691    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7692    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7693        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7694        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7695        // alternative of the `⊻` held and the XOR failed on each RFF).
7696        same_rff_qualifier(ctx, "Z19")
7697    }
7698
7699    /// [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...
7700    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7701        ctx.external.evaluate("code_list_membership_check")
7702    }
7703
7704    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7705    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7706        // Check: begin date < end date (structural — always true for valid messages)
7707        ConditionResult::True
7708    }
7709
7710    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7711    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7712        // Check: end date > begin date (structural — always true for valid messages)
7713        ConditionResult::True
7714    }
7715
7716    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7717    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7718        // HAND-EDITED: "in demselben RFF" — the RFF whose field is judged, not
7719        // any RFF of the message (with Z18, Z19, Z32 and Z37 all present, every
7720        // alternative of the `⊻` held and the XOR failed on each RFF).
7721        same_rff_qualifier(ctx, "Z37")
7722    }
7723
7724    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7725    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7726        // Check: PIA+5 with Messprodukt 9991000001498 exists
7727        let pia_segs = ctx.find_segments("PIA");
7728        let found = pia_segs.iter().any(|s| {
7729            s.elements
7730                .first()
7731                .and_then(|e| e.first())
7732                .is_some_and(|v| v == "5")
7733                && s.elements
7734                    .get(1)
7735                    .and_then(|e| e.first())
7736                    .is_some_and(|v| v == "9991000001498")
7737        });
7738        ConditionResult::from(found)
7739    }
7740
7741    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7742    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7743        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7744        let pia_segs = ctx.find_segments("PIA");
7745        let found = pia_segs.iter().any(|s| {
7746            s.elements
7747                .first()
7748                .and_then(|e| e.first())
7749                .is_some_and(|v| v == "5")
7750                && s.elements
7751                    .get(1)
7752                    .and_then(|e| e.first())
7753                    .is_some_and(|v| v == "9991000000739")
7754        });
7755        ConditionResult::from(found)
7756    }
7757
7758    /// [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
7759    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7760        ctx.external.evaluate("code_list_membership_check")
7761    }
7762
7763    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7764    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7765        ctx.external.evaluate("code_list_membership_check")
7766    }
7767
7768    /// [461] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
7769    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
7770        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7771        // SG10, when the scope reaches one; of any one SG10 otherwise.
7772        this_sg10_has(ctx, |segs| {
7773            segs.iter()
7774                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7775                && sg4_instances::has_cav(segs, "ZG8")
7776        })
7777    }
7778
7779    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7780    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7781        // HAND-EDITED: "in derselben SG10" — the CCI and the CAV of the enclosing
7782        // SG10, when the scope reaches one; of any one SG10 otherwise.
7783        this_sg10_has(ctx, |segs| {
7784            segs.iter()
7785                .any(|s| s.id == "CCI" && s.get_element(0) == "Z17")
7786                && sg4_instances::has_cav(segs, "ZH1")
7787        })
7788    }
7789
7790    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7791    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7792        // HAND-EDITED: "in dieser/derselben SG10" — scoped to the enclosing SG10.
7793        let cci_segs = ctx.scoped_find_segments_in("SG10", "CCI");
7794        let found = cci_segs.iter().any(|s| {
7795            s.elements
7796                .first()
7797                .and_then(|e| e.first())
7798                .is_some_and(|v| v == "Z61")
7799                && s.elements
7800                    .get(2)
7801                    .and_then(|e| e.first())
7802                    .is_some_and(|v| v == "ZF9")
7803        });
7804        ConditionResult::from(found)
7805    }
7806
7807    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7808    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7809        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7810        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7811        let found = rff_segs.iter().any(|s| {
7812            s.elements
7813                .first()
7814                .and_then(|e| e.first())
7815                .is_some_and(|v| v == "Z38")
7816        });
7817        ConditionResult::from(found)
7818    }
7819
7820    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7821    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7822        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7823        let rff_segs = ctx.scoped_find_segments_in("SG8", "RFF");
7824        let found = rff_segs.iter().any(|s| {
7825            s.elements
7826                .first()
7827                .and_then(|e| e.first())
7828                .is_some_and(|v| v == "Z32")
7829        });
7830        ConditionResult::from(found)
7831    }
7832
7833    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7834    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7835        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7836        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7837        let found = segs.iter().any(|s| {
7838            s.elements
7839                .first()
7840                .and_then(|e| e.first())
7841                .is_some_and(|v| v == "ZG8")
7842        });
7843        ConditionResult::from(found)
7844    }
7845
7846    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7847    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7848        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7849        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7850        let found = segs.iter().any(|s| {
7851            s.elements
7852                .first()
7853                .and_then(|e| e.first())
7854                .is_some_and(|v| v == "ZH0")
7855        });
7856        ConditionResult::from(found)
7857    }
7858
7859    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7860    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7861        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7862        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7863        let found = segs.iter().any(|s| {
7864            s.elements
7865                .first()
7866                .and_then(|e| e.first())
7867                .is_some_and(|v| v == "ZH1")
7868        });
7869        ConditionResult::from(found)
7870    }
7871
7872    /// [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
7873    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7874        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
7875        let segs = ctx.scoped_find_segments_in("SG8", "CAV");
7876        let found = segs.iter().any(|s| {
7877            s.elements
7878                .first()
7879                .and_then(|e| e.first())
7880                .is_some_and(|v| v == "ZH5")
7881        });
7882        ConditionResult::from(found)
7883    }
7884
7885    /// [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/ ...
7886    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7887        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7888        let found = ctx.find_segments("RFF").iter().any(|s| {
7889            let qual = s
7890                .elements
7891                .first()
7892                .and_then(|e| e.first())
7893                .map(|v| v.as_str());
7894            matches!(qual, Some("Z49") | Some("Z53"))
7895                && s.elements
7896                    .first()
7897                    .and_then(|e| e.get(2))
7898                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7899        });
7900        ConditionResult::from(found)
7901    }
7902
7903    /// [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...
7904    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7905        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7906        let found = ctx.find_segments("RFF").iter().any(|s| {
7907            let qual = s
7908                .elements
7909                .first()
7910                .and_then(|e| e.first())
7911                .map(|v| v.as_str());
7912            matches!(qual, Some("Z48") | Some("Z55"))
7913                && s.elements
7914                    .first()
7915                    .and_then(|e| e.get(2))
7916                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7917        });
7918        ConditionResult::from(found)
7919    }
7920
7921    /// [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...
7922    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7923        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7924        let found = ctx.find_segments("RFF").iter().any(|s| {
7925            let qual = s
7926                .elements
7927                .first()
7928                .and_then(|e| e.first())
7929                .map(|v| v.as_str());
7930            matches!(qual, Some("Z47") | Some("Z54"))
7931                && s.elements
7932                    .first()
7933                    .and_then(|e| e.get(2))
7934                    .is_some_and(|v| v.parse::<u32>().is_ok_and(|n| n > 1))
7935        });
7936        ConditionResult::from(found)
7937    }
7938
7939    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7940    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7941        let sts_segs = ctx.find_segments("STS");
7942        let found = sts_segs.iter().any(|s| {
7943            s.elements
7944                .first()
7945                .and_then(|e| e.first())
7946                .is_some_and(|v| v == "7")
7947                && s.elements
7948                    .get(2)
7949                    .and_then(|e| e.first())
7950                    .is_some_and(|v| ["ZC8"].contains(&v.as_str()))
7951        });
7952        ConditionResult::from(found)
7953    }
7954
7955    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7956    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7957        let sts_segs = ctx.find_segments("STS");
7958        let found = sts_segs.iter().any(|s| {
7959            s.elements
7960                .first()
7961                .and_then(|e| e.first())
7962                .is_some_and(|v| v == "7")
7963                && s.elements
7964                    .get(2)
7965                    .and_then(|e| e.first())
7966                    .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7967        });
7968        ConditionResult::from(found)
7969    }
7970
7971    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7972    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7973        let sts_segs = ctx.find_segments("STS");
7974        let found = sts_segs.iter().any(|s| {
7975            s.elements
7976                .first()
7977                .and_then(|e| e.first())
7978                .is_some_and(|v| v == "7")
7979                && s.elements
7980                    .get(3)
7981                    .and_then(|e| e.first())
7982                    .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7983        });
7984        ConditionResult::from(found)
7985    }
7986
7987    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7988    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7989        let sts_segs = ctx.find_segments("STS");
7990        let found = sts_segs.iter().any(|s| {
7991            s.elements
7992                .first()
7993                .and_then(|e| e.first())
7994                .is_some_and(|v| v == "7")
7995                && s.elements
7996                    .get(3)
7997                    .and_then(|e| e.first())
7998                    .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7999        });
8000        ConditionResult::from(found)
8001    }
8002
8003    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
8004    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
8005        let sts_segs = ctx.find_segments("STS");
8006        let found = sts_segs.iter().any(|s| {
8007            s.elements
8008                .first()
8009                .and_then(|e| e.first())
8010                .is_some_and(|v| v == "7")
8011                && s.elements
8012                    .get(3)
8013                    .and_then(|e| e.first())
8014                    .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
8015        });
8016        ConditionResult::from(found)
8017    }
8018
8019    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
8020    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
8021        let sts_segs = ctx.find_segments("STS");
8022        let found = sts_segs.iter().any(|s| {
8023            s.elements
8024                .first()
8025                .and_then(|e| e.first())
8026                .is_some_and(|v| v == "7")
8027                && s.elements
8028                    .get(3)
8029                    .and_then(|e| e.first())
8030                    .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
8031        });
8032        ConditionResult::from(found)
8033    }
8034
8035    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
8036    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
8037        let sts_segs = ctx.find_segments("STS");
8038        let found = sts_segs.iter().any(|s| {
8039            s.elements
8040                .first()
8041                .and_then(|e| e.first())
8042                .is_some_and(|v| v == "7")
8043                && s.elements
8044                    .get(3)
8045                    .and_then(|e| e.first())
8046                    .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
8047        });
8048        ConditionResult::from(found)
8049    }
8050
8051    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
8052    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
8053        let sts_segs = ctx.find_segments("STS");
8054        let found = sts_segs.iter().any(|s| {
8055            s.elements
8056                .first()
8057                .and_then(|e| e.first())
8058                .is_some_and(|v| v == "7")
8059                && s.elements
8060                    .get(3)
8061                    .and_then(|e| e.first())
8062                    .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
8063        });
8064        ConditionResult::from(found)
8065    }
8066
8067    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
8068    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
8069        let sts_segs = ctx.find_segments("STS");
8070        let found = sts_segs.iter().any(|s| {
8071            s.elements
8072                .first()
8073                .and_then(|e| e.first())
8074                .is_some_and(|v| v == "7")
8075                && s.elements
8076                    .get(3)
8077                    .and_then(|e| e.first())
8078                    .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
8079        });
8080        ConditionResult::from(found)
8081    }
8082
8083    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
8084    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
8085        let sts_segs = ctx.find_segments("STS");
8086        let found = sts_segs.iter().any(|s| {
8087            s.elements
8088                .first()
8089                .and_then(|e| e.first())
8090                .is_some_and(|v| v == "7")
8091                && s.elements
8092                    .get(3)
8093                    .and_then(|e| e.first())
8094                    .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
8095        });
8096        ConditionResult::from(found)
8097    }
8098
8099    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
8100    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
8101        let sts_segs = ctx.find_segments("STS");
8102        let found = sts_segs.iter().any(|s| {
8103            s.elements
8104                .first()
8105                .and_then(|e| e.first())
8106                .is_some_and(|v| v == "7")
8107                && s.elements
8108                    .get(3)
8109                    .and_then(|e| e.first())
8110                    .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
8111        });
8112        ConditionResult::from(found)
8113    }
8114
8115    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
8116    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
8117        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
8118        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
8119        if rff_z49.is_empty() {
8120            return ConditionResult::Unknown;
8121        }
8122        // Approximate: condition applies when RFF+Z49 exists
8123        ConditionResult::True
8124    }
8125
8126    /// [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
8127    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
8128        ctx.format_check("DTM", 0, 1, is_mesz_utc)
8129    }
8130
8131    /// [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
8132    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
8133        ctx.format_check("DTM", 0, 1, is_mez_utc)
8134    }
8135
8136    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
8137    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
8138        // Document date must be <= now. Format 303 is CCYYMMDDHHMM — fixed-width,
8139        // so lexicographic comparison works directly.
8140        let val = ctx.resolved_value.or_else(|| {
8141            ctx.find_segments_with_qualifier("DTM", 0, "137")
8142                .first()
8143                .and_then(|s| s.elements.first())
8144                .and_then(|e| e.get(1))
8145                .map(|v| v.as_str())
8146        });
8147        let Some(dtm_val) = val else {
8148            return ConditionResult::Unknown;
8149        };
8150        if dtm_val.len() < 12 {
8151            return ConditionResult::Unknown;
8152        }
8153        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
8154        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
8155    }
8156
8157    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
8158    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
8159        ConditionResult::True
8160    }
8161
8162    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
8163    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
8164        ConditionResult::True
8165    }
8166
8167    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
8168    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
8169        ConditionResult::True
8170    }
8171
8172    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
8173    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
8174        ConditionResult::True
8175    }
8176
8177    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
8178    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
8179        ConditionResult::True
8180    }
8181
8182    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
8183    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
8184        ConditionResult::True
8185    }
8186
8187    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
8188    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
8189        ConditionResult::True
8190    }
8191
8192    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
8193    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
8194        ConditionResult::True
8195    }
8196
8197    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
8198    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
8199        ConditionResult::True
8200    }
8201
8202    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
8203    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
8204        ConditionResult::True
8205    }
8206
8207    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
8208    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
8209        ConditionResult::True
8210    }
8211
8212    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
8213    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
8214        ConditionResult::True
8215    }
8216
8217    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
8218    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
8219        ConditionResult::True
8220    }
8221
8222    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
8223    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
8224        ConditionResult::True
8225    }
8226
8227    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
8228    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
8229        ConditionResult::True
8230    }
8231
8232    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
8233    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
8234        ConditionResult::True
8235    }
8236
8237    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
8238    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
8239        ConditionResult::True
8240    }
8241
8242    /// [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
8243    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
8244        ConditionResult::True
8245    }
8246
8247    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
8248    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
8249        ConditionResult::True
8250    }
8251
8252    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
8253    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
8254        ConditionResult::True
8255    }
8256
8257    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
8258    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
8259        ConditionResult::True
8260    }
8261
8262    /// [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...
8263    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
8264        ConditionResult::True
8265    }
8266
8267    /// [522] Hinweis: Es ist der NBN anzugeben
8268    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
8269        ConditionResult::True
8270    }
8271
8272    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8273    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
8274        ConditionResult::True
8275    }
8276
8277    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
8278    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
8279        ConditionResult::True
8280    }
8281
8282    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
8283    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
8284        ConditionResult::True
8285    }
8286
8287    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
8288    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
8289        ConditionResult::True
8290    }
8291
8292    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
8293    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
8294        ConditionResult::True
8295    }
8296
8297    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
8298    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
8299        ConditionResult::True
8300    }
8301
8302    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
8303    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
8304        ConditionResult::True
8305    }
8306
8307    /// [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, ...
8308    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
8309        ConditionResult::True
8310    }
8311
8312    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
8313    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
8314        ConditionResult::True
8315    }
8316
8317    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
8318    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
8319        ConditionResult::True
8320    }
8321
8322    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
8323    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
8324        ConditionResult::True
8325    }
8326
8327    /// [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...
8328    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
8329        // TODO: implement
8330        ConditionResult::Unknown
8331    }
8332
8333    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
8334    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
8335        ConditionResult::True
8336    }
8337
8338    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
8339    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
8340        ConditionResult::True
8341    }
8342
8343    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
8344    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
8345        ConditionResult::True
8346    }
8347
8348    /// [563] Hinweis: Für die ID der LieferantensummenZR
8349    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
8350        ConditionResult::True
8351    }
8352
8353    /// [566] Hinweis: Altlieferant
8354    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
8355        ConditionResult::True
8356    }
8357
8358    /// [567] Hinweis: Neulieferant
8359    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
8360        ConditionResult::True
8361    }
8362
8363    /// [568] Hinweis: Lieferant der LieferantensummenZR
8364    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
8365        ConditionResult::True
8366    }
8367
8368    /// [569] Hinweis: Dritter Nutzer
8369    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
8370        ConditionResult::True
8371    }
8372
8373    /// [570] Hinweis: Netzbetreiber Alt
8374    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
8375        ConditionResult::True
8376    }
8377
8378    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
8379    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
8380        ConditionResult::True
8381    }
8382
8383    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
8384    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
8385        ConditionResult::True
8386    }
8387
8388    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
8389    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
8390        ConditionResult::True
8391    }
8392
8393    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
8394    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
8395        ConditionResult::True
8396    }
8397
8398    /// [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...
8399    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
8400        ConditionResult::True
8401    }
8402
8403    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
8404    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
8405        ConditionResult::True
8406    }
8407
8408    /// [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...
8409    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
8410        ConditionResult::True
8411    }
8412
8413    /// [590] Hinweis: Für den Empfang des Steuerbefehls
8414    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
8415        ConditionResult::True
8416    }
8417
8418    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
8419    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
8420        ConditionResult::True
8421    }
8422
8423    /// [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...
8424    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
8425        ConditionResult::True
8426    }
8427
8428    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
8429    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
8430        ConditionResult::True
8431    }
8432
8433    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
8434    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
8435        ConditionResult::True
8436    }
8437
8438    /// [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
8439    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
8440        ConditionResult::True
8441    }
8442
8443    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
8444    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
8445        ConditionResult::True
8446    }
8447
8448    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
8449    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
8450        ConditionResult::True
8451    }
8452
8453    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
8454    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
8455        ConditionResult::True
8456    }
8457
8458    /// [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
8459    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
8460        ConditionResult::True
8461    }
8462
8463    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
8464    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
8465        ConditionResult::True
8466    }
8467
8468    /// [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.
8469    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
8470        ConditionResult::True
8471    }
8472
8473    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
8474    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
8475        ConditionResult::True
8476    }
8477
8478    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
8479    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
8480        ConditionResult::True
8481    }
8482
8483    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
8484    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
8485        ConditionResult::True
8486    }
8487
8488    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
8489    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
8490        ConditionResult::True
8491    }
8492
8493    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
8494    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
8495        ConditionResult::True
8496    }
8497
8498    /// [637] Hinweis: Bei Verpflichtungsanfrage
8499    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
8500        ConditionResult::True
8501    }
8502
8503    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
8504    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
8505        ConditionResult::True
8506    }
8507
8508    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
8509    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
8510        ConditionResult::True
8511    }
8512
8513    /// [640] Hinweis: Wenn Antwort auf Aktivierung
8514    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
8515        ConditionResult::True
8516    }
8517
8518    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
8519    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
8520        ConditionResult::True
8521    }
8522
8523    /// [642] Hinweis: Wenn Abo vorliegt
8524    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
8525        ConditionResult::True
8526    }
8527
8528    /// [643] Hinweis: Nachfolgender Netzbetreiber
8529    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
8530        ConditionResult::True
8531    }
8532
8533    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
8534    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
8535        ConditionResult::True
8536    }
8537
8538    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
8539    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
8540        ConditionResult::True
8541    }
8542
8543    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
8544    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
8545        ConditionResult::True
8546    }
8547
8548    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
8549    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
8550        ConditionResult::True
8551    }
8552
8553    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
8554    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
8555        ConditionResult::True
8556    }
8557
8558    /// [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...
8559    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
8560        ConditionResult::True
8561    }
8562
8563    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
8564    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
8565        ConditionResult::True
8566    }
8567
8568    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
8569    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
8570        ConditionResult::True
8571    }
8572
8573    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
8574    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
8575        ConditionResult::True
8576    }
8577
8578    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
8579    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
8580        ConditionResult::True
8581    }
8582
8583    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
8584    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
8585        ConditionResult::True
8586    }
8587
8588    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
8589    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
8590        ConditionResult::True
8591    }
8592
8593    /// [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
8594    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
8595        ConditionResult::True
8596    }
8597
8598    /// [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...
8599    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
8600        ConditionResult::True
8601    }
8602
8603    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
8604    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
8605        ConditionResult::True
8606    }
8607
8608    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8609    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
8610        ConditionResult::True
8611    }
8612
8613    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
8614    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
8615        ConditionResult::True
8616    }
8617
8618    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8619    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
8620        ConditionResult::True
8621    }
8622
8623    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
8624    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
8625        ConditionResult::True
8626    }
8627
8628    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
8629    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
8630        ConditionResult::True
8631    }
8632
8633    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
8634    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
8635        ConditionResult::True
8636    }
8637
8638    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8639    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
8640        ConditionResult::True
8641    }
8642
8643    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8644    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
8645        ConditionResult::True
8646    }
8647
8648    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8649    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
8650        ConditionResult::True
8651    }
8652
8653    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8654    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
8655        ConditionResult::True
8656    }
8657
8658    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
8659    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
8660        ConditionResult::True
8661    }
8662
8663    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
8664    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
8665        ConditionResult::True
8666    }
8667
8668    /// [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
8669    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
8670        ConditionResult::True
8671    }
8672
8673    /// [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.
8674    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
8675        ConditionResult::True
8676    }
8677
8678    /// [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
8679    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
8680        ConditionResult::True
8681    }
8682
8683    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
8684    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
8685        ConditionResult::True
8686    }
8687
8688    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
8689    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
8690        ConditionResult::True
8691    }
8692
8693    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
8694    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
8695        ConditionResult::True
8696    }
8697
8698    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
8699    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
8700        ConditionResult::True
8701    }
8702
8703    /// [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...
8704    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
8705        ConditionResult::True
8706    }
8707
8708    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
8709    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
8710        ConditionResult::True
8711    }
8712
8713    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
8714    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
8715        ConditionResult::True
8716    }
8717
8718    /// [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...
8719    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
8720        ConditionResult::True
8721    }
8722
8723    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
8724    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
8725        ConditionResult::True
8726    }
8727
8728    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
8729    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
8730        ConditionResult::True
8731    }
8732
8733    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
8734    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
8735        ConditionResult::True
8736    }
8737
8738    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
8739    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
8740        ConditionResult::True
8741    }
8742
8743    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8744    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
8745        ctx.external.evaluate("data_clearing_required")
8746    }
8747
8748    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
8749    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
8750        ConditionResult::True
8751    }
8752
8753    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
8754    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
8755        ConditionResult::True
8756    }
8757
8758    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
8759    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
8760        ConditionResult::True
8761    }
8762
8763    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
8764    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8765        ConditionResult::True
8766    }
8767
8768    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8769    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8770        ConditionResult::True
8771    }
8772
8773    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8774    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8775        ConditionResult::True
8776    }
8777
8778    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8779    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8780        ConditionResult::True
8781    }
8782
8783    /// [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...
8784    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8785        ConditionResult::True
8786    }
8787
8788    /// [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...
8789    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8790        ConditionResult::True
8791    }
8792
8793    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8794    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8795        ConditionResult::True
8796    }
8797
8798    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8799    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8800        ctx.external.evaluate("data_clearing_required")
8801    }
8802
8803    /// [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
8804    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8805        ConditionResult::True
8806    }
8807
8808    /// [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...
8809    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8810        ConditionResult::True
8811    }
8812
8813    /// [902] Format: Möglicher Wert: ≥ 0
8814    fn evaluate_902(&self, ctx: &EvaluationContext) -> ConditionResult {
8815        // HAND-EDITED: judged on the value it annotates; without one it
8816        // says nothing.
8817        match ctx.resolved_value.filter(|v| !v.is_empty()) {
8818            Some(value) => validate_numeric(value, ">=", 0.0),
8819            None => ConditionResult::True,
8820        }
8821    }
8822
8823    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8824    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8825        // Format condition — informational, always applies
8826        ConditionResult::True
8827    }
8828
8829    /// [914] Format: Möglicher Wert: > 0
8830    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8831        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8832    }
8833
8834    /// [922] Format: TR-ID
8835    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8836        // Format condition — informational, always applies
8837        ConditionResult::True
8838    }
8839
8840    /// [926] Format: Möglicher Wert: 0
8841    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8842        // Format condition — informational, always applies
8843        ConditionResult::True
8844    }
8845
8846    /// [930] Format: max. 2 Nachkommastellen
8847    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8848        // Format condition — informational, always applies
8849        ConditionResult::True
8850    }
8851
8852    /// [931] Format: ZZZ = +00
8853    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8854        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8855    }
8856
8857    /// [932] Format: HHMM = 2200
8858    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8859        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8860    }
8861
8862    /// [933] Format: HHMM = 2300
8863    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8864        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8865    }
8866
8867    /// [937] Format: keine Nachkommastelle
8868    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8869        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8870    }
8871
8872    /// [938] Format: Möglicher Wert: <= 10
8873    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8874        ConditionResult::True
8875    }
8876
8877    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8878    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8879        ctx.format_check("COM", 0, 0, validate_email)
8880    }
8881
8882    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8883    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8884        ctx.format_check("COM", 0, 0, validate_phone)
8885    }
8886
8887    /// [942] Format: n1-n2-n1-n3
8888    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8889        // Format condition — informational, always applies
8890        ConditionResult::True
8891    }
8892
8893    /// [943] Format: n1-n2-n1
8894    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8895        // Format condition — informational, always applies
8896        ConditionResult::True
8897    }
8898
8899    /// [946] Format: max. 11 Nachkommastellen
8900    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8901        // Format condition — informational, always applies
8902        ConditionResult::True
8903    }
8904
8905    /// [948] Format: n1-n2-n1-n8-n2
8906    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8907        // Format condition — informational, always applies
8908        ConditionResult::True
8909    }
8910
8911    /// [950] Format: Marktlokations-ID
8912    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8913        // Format condition — informational, always applies
8914        ConditionResult::True
8915    }
8916
8917    /// [951] Format: Zählpunktbezeichnung
8918    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8919        // Format condition — informational, always applies
8920        ConditionResult::True
8921    }
8922
8923    /// [952] Format: Gerätenummer nach DIN 43863-5
8924    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8925        // Format condition — informational, always applies
8926        ConditionResult::True
8927    }
8928
8929    /// [955] Format: Möglicher Wert: < 100
8930    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8931        // Format condition — informational, always applies
8932        ConditionResult::True
8933    }
8934
8935    /// [957] Format: n1-n2-n1-n8
8936    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8937        // Format condition — informational, always applies
8938        ConditionResult::True
8939    }
8940
8941    /// [960] Format: Netzlokations-ID
8942    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8943        // Format condition — informational, always applies
8944        ConditionResult::True
8945    }
8946
8947    /// [961] Format: SR-ID
8948    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8949        // Format condition — informational, always applies
8950        ConditionResult::True
8951    }
8952
8953    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8954    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8955        // Format condition — informational, always applies
8956        ConditionResult::True
8957    }
8958
8959    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8960    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8961        // Cardinality: at least twice per SG4 IDE+24
8962        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8963        ConditionResult::from(ide_count > 0)
8964    }
8965
8966    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8967    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8968        let seq_segs = ctx.find_segments("SEQ");
8969        let has_z79 = seq_segs.iter().any(|s| {
8970            s.elements
8971                .first()
8972                .and_then(|e| e.first())
8973                .is_some_and(|v| v == "Z79")
8974        });
8975        ConditionResult::from(has_z79)
8976    }
8977
8978    /// [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
8979    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8980        // TODO: implement
8981        ConditionResult::Unknown
8982    }
8983
8984    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8985    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
8986    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("genau einmal")
8987    // is a count, not a condition on the group: the generated code counted
8988    // the SG8 of every variant naming the ID, so several variants made it
8989    // false and the group was never judged.
8990    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8991        let named = ctx
8992            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
8993            .iter()
8994            .any(|s| !s.get_component(0, 2).is_empty());
8995        ConditionResult::from(named)
8996    }
8997
8998    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8999    // HAND-EDITED: whether the group applies — an SG6 RFF+Z49 of this SG4
9000    // names a Zeitraum-ID. How often it repeats per Zeitraum-ID ("mindestens einmal")
9001    // is a count, not a condition on the group: the generated code counted
9002    // the SG8 of every variant naming the ID, so several variants made it
9003    // false and the group was never judged.
9004    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
9005        let named = ctx
9006            .scoped_find_segments_in_with_qualifier("SG4", "RFF", 0, "Z49")
9007            .iter()
9008            .any(|s| !s.get_component(0, 2).is_empty());
9009        ConditionResult::from(named)
9010    }
9011
9012    /// [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 ...
9013    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
9014        // TODO: implement
9015        ConditionResult::Unknown
9016    }
9017
9018    /// [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 ...
9019    /// EXTERNAL: Requires context from outside the message.
9020    // 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)
9021    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
9022        ctx.external
9023            .evaluate("request_message_zeitraum_ids_present")
9024    }
9025
9026    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
9027    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
9028        // TODO: implement
9029        ConditionResult::Unknown
9030    }
9031
9032    /// [2013] Mindesten einmal anzugeben wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
9033    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
9034        // Check: condition applies when no RFF+Z54 exists
9035        ctx.lacks_qualifier("RFF", 0, "Z54")
9036    }
9037
9038    /// [2014] Mindesten einmal anzugeben wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
9039    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
9040        // Check: condition applies when no RFF+Z47 exists
9041        ctx.lacks_qualifier("RFF", 0, "Z47")
9042    }
9043
9044    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
9045    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
9046        // TODO: implement
9047        ConditionResult::Unknown
9048    }
9049
9050    /// [2016] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
9051    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
9052        // Cardinality: exactly once per SG5 LOC+Z16 (Marktlokation)
9053        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9054        ConditionResult::from(loc_count > 0)
9055    }
9056
9057    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
9058    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
9059        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
9060        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9061        ConditionResult::from(loc_count > 0)
9062    }
9063
9064    /// [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...
9065    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
9066        // TODO: implement
9067        ConditionResult::Unknown
9068    }
9069
9070    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
9071    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
9072        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
9073        let found = ctx.find_segments("CAV").iter().any(|s| {
9074            s.elements
9075                .first()
9076                .and_then(|e| e.first())
9077                .is_some_and(|v| v == "NZR")
9078        });
9079        ConditionResult::from(found)
9080    }
9081
9082    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
9083    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
9084        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
9085        let ide_count = ctx.find_segments("IDE").len();
9086        ConditionResult::from(ide_count > 0)
9087    }
9088
9089    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
9090    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
9091        // Cardinality: LieferantensummenZR once per SG4 IDE+24
9092        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9093        ConditionResult::from(ide_count > 0)
9094    }
9095
9096    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
9097    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
9098        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
9099        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9100        ConditionResult::from(ide_count > 0)
9101    }
9102
9103    /// [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)
9104    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
9105        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
9106        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9107        ConditionResult::from(ide_count > 0)
9108    }
9109
9110    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
9111    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
9112        // Cardinality: max twice per SG4 IDE+24
9113        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9114        ConditionResult::from(ide_count > 0)
9115    }
9116
9117    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
9118    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
9119        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
9120        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
9121        ConditionResult::from(loc_count > 0)
9122    }
9123
9124    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
9125    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
9126        // Cardinality: exactly twice per SG4 IDE
9127        let ide_count = ctx.find_segments("IDE").len();
9128        ConditionResult::from(ide_count > 0)
9129    }
9130
9131    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
9132    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
9133        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
9134        let found = ctx.find_segments("SEQ").iter().any(|s| {
9135            s.elements
9136                .first()
9137                .and_then(|e| e.first())
9138                .is_some_and(|v| v == "Z13" || v == "ZG0")
9139        });
9140        ConditionResult::from(found)
9141    }
9142
9143    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
9144    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
9145        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
9146        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
9147        ConditionResult::from(ide_count > 0)
9148    }
9149
9150    /// [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
9151    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
9152        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
9153        let found = ctx.find_segments("SEQ").iter().any(|s| {
9154            s.elements
9155                .first()
9156                .and_then(|e| e.first())
9157                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9158        });
9159        ConditionResult::from(found)
9160    }
9161
9162    /// [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
9163    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
9164        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
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| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
9170        });
9171        ConditionResult::from(found)
9172    }
9173
9174    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
9175    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
9176        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe 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    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
9182    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
9183        // TODO: implement
9184        ConditionResult::Unknown
9185    }
9186
9187    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
9188    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
9189        // Cardinality: code once per SG4 IDE+24
9190        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
9191        ConditionResult::from(ide_count > 0)
9192    }
9193
9194    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
9195    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
9196        // TODO: implement
9197        ConditionResult::Unknown
9198    }
9199
9200    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9201    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
9202        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9203        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9204        ConditionResult::from(loc_count > 0)
9205    }
9206
9207    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
9208    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
9209        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
9210        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9211        ConditionResult::from(loc_count > 0)
9212    }
9213
9214    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
9215    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
9216        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
9217        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
9218        ConditionResult::from(found)
9219    }
9220
9221    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
9222    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
9223        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
9224        let found = ctx.find_segments("SEQ").iter().any(|s| {
9225            s.elements
9226                .first()
9227                .and_then(|e| e.first())
9228                .is_some_and(|v| v == "Z03" || v == "ZF5")
9229        });
9230        ConditionResult::from(found)
9231    }
9232
9233    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
9234    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
9235        // TODO: implement
9236        ConditionResult::Unknown
9237    }
9238
9239    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
9240    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
9241        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
9242        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9243        ConditionResult::from(loc_count > 0)
9244    }
9245
9246    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
9247    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
9248        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
9249        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9250        ConditionResult::from(loc_count > 0)
9251    }
9252
9253    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
9254    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
9255        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
9256        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
9257        ConditionResult::from(loc_count > 0)
9258    }
9259
9260    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
9261    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
9262        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
9263        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
9264        ConditionResult::from(loc_count > 0)
9265    }
9266
9267    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
9268    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
9269        // Cardinality: at least once per LOC+Z21 (Tranche) ID
9270        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
9271        ConditionResult::from(loc_count > 0)
9272    }
9273
9274    /// [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...
9275    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
9276        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9277        // Check: RFF+Z33 DE1154 does NOT contain Objektcode 9992000001256 (Netzlokation)
9278        let rff_z33 = ctx.scoped_find_segments_in_with_qualifier("SG8", "RFF", 0, "Z33");
9279        let has_netzlokation = rff_z33.iter().any(|s| {
9280            s.elements
9281                .first()
9282                .and_then(|e| e.get(1))
9283                .is_some_and(|v| v == "9992000001256")
9284        });
9285        ConditionResult::from(!has_netzlokation)
9286    }
9287
9288    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
9289    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
9290        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
9291        let found = ctx.find_segments("SEQ").iter().any(|s| {
9292            s.elements
9293                .first()
9294                .and_then(|e| e.first())
9295                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
9296        });
9297        ConditionResult::from(found)
9298    }
9299
9300    /// [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
9301    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
9302        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9303        // Check: SG10 CCI+++Z25 has CAV+MIW/MPW/MUW (Wandler types)
9304        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9305            s.elements
9306                .first()
9307                .and_then(|e| e.first())
9308                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
9309        });
9310        ConditionResult::from(has_cav)
9311    }
9312
9313    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
9314    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
9315        // HAND-EDITED: "in dieser/derselben SG8" — scoped to the enclosing SG8.
9316        // Check: SG10 CCI+++Z25 has CAV+MBW (Blockstromwandler)
9317        let has_cav = ctx.scoped_find_segments_in("SG8", "CAV").iter().any(|s| {
9318            s.elements
9319                .first()
9320                .and_then(|e| e.first())
9321                .is_some_and(|v| v == "MBW")
9322        });
9323        ConditionResult::from(has_cav)
9324    }
9325
9326    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
9327    fn evaluate_2344(&self, ctx: &EvaluationContext) -> ConditionResult {
9328        // HAND-EDITED (#343): a repetition bound per Marktlokation or Tranche of the
9329        // transaction; as a presence condition it holds where the transaction names one
9330        // (SG5 LOC+Z16 / LOC+Z21), within the enclosing SG4 when the scope reaches one.
9331        let loc = ctx.scoped_find_segments_in("SG4", "LOC");
9332        ConditionResult::from(
9333            loc.iter()
9334                .any(|s| ["Z16", "Z21"].contains(&s.get_element(0))),
9335        )
9336    }
9337
9338    /// [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...
9339    fn evaluate_2350(&self, _ctx: &EvaluationContext) -> ConditionResult {
9340        // TODO: implement
9341        ConditionResult::Unknown
9342    }
9343
9344    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
9345    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
9346        // TODO: implement
9347        ConditionResult::Unknown
9348    }
9349
9350    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
9351    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
9352        // TODO: implement
9353        ConditionResult::Unknown
9354    }
9355
9356    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
9357    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
9358        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
9359        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9360        ConditionResult::from(loc_count > 0)
9361    }
9362
9363    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
9364    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
9365        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
9366        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
9367        ConditionResult::from(loc_count > 0)
9368    }
9369
9370    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
9371    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
9372        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
9373        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
9374        ConditionResult::from(loc_count > 0)
9375    }
9376
9377    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
9378    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
9379        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
9380        let found = ctx.find_segments("LOC").iter().any(|s| {
9381            s.elements
9382                .first()
9383                .and_then(|e| e.first())
9384                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
9385        });
9386        ConditionResult::from(found)
9387    }
9388
9389    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
9390    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
9391        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
9392        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
9393        ConditionResult::from(loc_count > 0)
9394    }
9395
9396    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
9397    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
9398        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
9399        let found = ctx.find_segments("LOC").iter().any(|s| {
9400            s.elements
9401                .first()
9402                .and_then(|e| e.first())
9403                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
9404        });
9405        ConditionResult::from(found)
9406    }
9407}
9408
9409// HAND-EDITED: helpers for [223] / [265].
9410
9411use mig_types::segment::OwnedSegment;
9412
9413/// One SG8 repetition: its SEQ qualifier, its RFF segments and the
9414/// Marktlokation IDs of its RFF+Z18, and its SG10s as `(CCIs, CAVs)`.
9415struct Sg8Block {
9416    seq: String,
9417    /// The SEQ's Zeitraum-ID (DE1050).
9418    zeitraum: String,
9419    rffs: Vec<OwnedSegment>,
9420    malo_refs: Vec<String>,
9421    sg10: Vec<(Vec<OwnedSegment>, Vec<OwnedSegment>)>,
9422}
9423
9424fn component(seg: &OwnedSegment, element: usize, comp: usize) -> Option<&str> {
9425    seg.elements
9426        .get(element)
9427        .and_then(|e| e.get(comp))
9428        .map(String::as_str)
9429}
9430
9431/// The message's SG8 repetitions, from the group navigator when there is one,
9432/// else from the segments in message order: SEQ opens an SG8, CCI opens an
9433/// SG10 within it that takes the CAVs after it. (`validate_pid_with_conditions`
9434/// evaluates BO4E input without a navigator.) `None` without either.
9435fn sg8_blocks(ctx: &EvaluationContext) -> Option<Vec<Sg8Block>> {
9436    let malo_refs = |rffs: &[OwnedSegment]| -> Vec<String> {
9437        rffs.iter()
9438            .filter(|s| component(s, 0, 0) == Some("Z18"))
9439            .filter_map(|s| component(s, 0, 1).map(str::to_string))
9440            .collect()
9441    };
9442    if let Some(nav) = ctx.navigator() {
9443        let sg8 = ["SG4", "SG8"];
9444        return Some(
9445            (0..nav.group_instance_count(&sg8))
9446                .map(|i| Sg8Block {
9447                    seq: nav
9448                        .find_segments_in_group("SEQ", &sg8, i)
9449                        .first()
9450                        .and_then(|s| component(s, 0, 0).map(str::to_string))
9451                        .unwrap_or_default(),
9452                    zeitraum: nav
9453                        .find_segments_in_group("SEQ", &sg8, i)
9454                        .first()
9455                        .and_then(|s| component(s, 1, 0).map(str::to_string))
9456                        .unwrap_or_default(),
9457                    rffs: nav.find_segments_in_group("RFF", &sg8, i),
9458                    malo_refs: malo_refs(&nav.find_segments_in_group("RFF", &sg8, i)),
9459                    sg10: (0..nav.child_group_instance_count(&sg8, i, "SG10"))
9460                        .map(|j| {
9461                            (
9462                                nav.find_segments_in_child_group("CCI", &sg8, i, "SG10", j),
9463                                nav.find_segments_in_child_group("CAV", &sg8, i, "SG10", j),
9464                            )
9465                        })
9466                        .collect(),
9467                })
9468                .collect(),
9469        );
9470    }
9471    if ctx.segments.is_empty() {
9472        return None;
9473    }
9474    let mut blocks: Vec<Sg8Block> = Vec::new();
9475    let mut rffs: Vec<OwnedSegment> = Vec::new();
9476    for seg in ctx.segments {
9477        match seg.id.as_str() {
9478            "SEQ" => {
9479                if let Some(b) = blocks.last_mut() {
9480                    b.malo_refs = malo_refs(&rffs);
9481                    b.rffs = std::mem::take(&mut rffs);
9482                }
9483                rffs.clear();
9484                blocks.push(Sg8Block {
9485                    seq: component(seg, 0, 0).unwrap_or_default().to_string(),
9486                    zeitraum: component(seg, 1, 0).unwrap_or_default().to_string(),
9487                    rffs: Vec::new(),
9488                    malo_refs: Vec::new(),
9489                    sg10: Vec::new(),
9490                });
9491            }
9492            // Segments after the SG8s end the last one.
9493            "NAD" | "UNT" | "IDE" => {
9494                if let Some(b) = blocks.last_mut() {
9495                    if b.rffs.is_empty() {
9496                        b.malo_refs = malo_refs(&rffs);
9497                        b.rffs = std::mem::take(&mut rffs);
9498                    }
9499                }
9500                rffs.clear();
9501                if seg.id == "IDE" {
9502                    continue;
9503                }
9504                break;
9505            }
9506            "RFF" if !blocks.is_empty() => rffs.push(seg.clone()),
9507            "CCI" => {
9508                if let Some(b) = blocks.last_mut() {
9509                    b.sg10.push((vec![seg.clone()], Vec::new()));
9510                }
9511            }
9512            "CAV" => {
9513                if let Some((_, cavs)) = blocks.last_mut().and_then(|b| b.sg10.last_mut()) {
9514                    cavs.push(seg.clone());
9515                }
9516            }
9517            _ => {}
9518        }
9519    }
9520    if let Some(b) = blocks.last_mut() {
9521        if b.rffs.is_empty() {
9522            b.malo_refs = malo_refs(&rffs);
9523            b.rffs = rffs;
9524        }
9525    }
9526    Some(blocks)
9527}
9528
9529// HAND-EDITED: "SG10 CCI+… CAV+… vorhanden" pairs a CCI and a CAV of one SG10
9530// (#255): [101], [102], [136], [216], [232], [234], [239], [346], [461], [462].
9531
9532/// Whether one SG10 — its CCI and CAV segments — satisfies `pred`: an SG10 of
9533/// the enclosing SG8 when `this_sg8` ("in dieser SG8") and the scope reaches
9534/// one; any SG10 of the message otherwise.
9535fn sg10_has(
9536    ctx: &EvaluationContext,
9537    this_sg8: bool,
9538    pred: impl Fn(&[OwnedSegment]) -> bool,
9539) -> ConditionResult {
9540    if this_sg8 {
9541        if let Some(sg10s) = ctx.enclosing_child_segments("SG8", "SG10", &["CCI", "CAV"]) {
9542            return ConditionResult::from(sg10s.iter().any(|segs| pred(segs)));
9543        }
9544    }
9545    let Some(blocks) = sg8_blocks(ctx) else {
9546        return ConditionResult::False;
9547    };
9548    ConditionResult::from(blocks.iter().flat_map(|b| &b.sg10).any(|(ccis, cavs)| {
9549        let segs: Vec<OwnedSegment> = ccis.iter().chain(cavs).cloned().collect();
9550        pred(&segs)
9551    }))
9552}
9553
9554/// "in derselben SG10": whether the enclosing SG10's CCI and CAV segments
9555/// satisfy `pred`, when the scope reaches one; [`sg10_has`] over the message
9556/// otherwise.
9557fn this_sg10_has(
9558    ctx: &EvaluationContext,
9559    pred: impl Fn(&[OwnedSegment]) -> bool,
9560) -> ConditionResult {
9561    if let Some(path) = ctx.enclosing_scope("SG10") {
9562        let segs: Vec<OwnedSegment> = ["CCI", "CAV"]
9563            .iter()
9564            .flat_map(|tag| ctx.find_segments_at(path, tag))
9565            .collect();
9566        return ConditionResult::from(pred(&segs));
9567    }
9568    sg10_has(ctx, false, pred)
9569}
9570
9571/// [114]–[116]: "das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV
9572/// `code` in dieser SG vorhanden".
9573fn spannungsebene_in_this_sg10(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9574    this_sg10_has(ctx, |segs| {
9575        segs.iter()
9576            .any(|s| s.id == "CCI" && component(s, 2, 0) == Some("E03"))
9577            && segs
9578                .iter()
9579                .any(|s| s.id == "CAV" && component(s, 0, 0) == Some(code))
9580    })
9581}
9582
9583/// The reference (C506 DE1154) of the RFF whose field is judged.
9584fn rff_reference<'c>(ctx: &EvaluationContext<'c>) -> Option<&'c str> {
9585    ctx.resolved_segment?
9586        .first()?
9587        .get(1)
9588        .map(String::as_str)
9589        .filter(|v| !v.is_empty())
9590}
9591
9592/// "Wenn in demselben RFF der Code … im DE1153 vorhanden": the qualifier of the
9593/// RFF whose field is judged; message-wide only where no segment is resolved.
9594fn same_rff_qualifier(ctx: &EvaluationContext, code: &str) -> ConditionResult {
9595    match ctx.resolved_segment {
9596        Some(elements) => ConditionResult::from(
9597            elements
9598                .first()
9599                .and_then(|e| e.first())
9600                .is_some_and(|q| q == code),
9601        ),
9602        None => ctx.has_qualifier("RFF", 0, code),
9603    }
9604}
9605
9606/// HAND-EDITED: `[126]`/`[127]`/`[128]` — the value is the 1-based position of
9607/// the resolved RFF among the SG6 RFF of `qualifiers` in its SG4 ("Das erste
9608/// SG6 RFF+Z49/Z53 hat somit die 1, das zweite die 2").
9609fn repetition_ordinal(ctx: &EvaluationContext, qualifiers: &[&str]) -> ConditionResult {
9610    let (Some(value), Some(segment)) = (ctx.resolved_value, ctx.resolved_segment) else {
9611        return ConditionResult::Unknown;
9612    };
9613    let siblings: Vec<_> = ctx
9614        .scoped_find_segments_in("SG4", "RFF")
9615        .into_iter()
9616        .filter(|s| qualifiers.contains(&s.get_component(0, 0)))
9617        .collect();
9618    match siblings
9619        .iter()
9620        .position(|s| s.elements.as_slice() == segment)
9621    {
9622        Some(i) => ConditionResult::from(value == (i + 1).to_string()),
9623        None => ConditionResult::Unknown,
9624    }
9625}
9626
9627/// [131]/[132]: the DTM+Z25 of an SG6 whose RFF carries Zeitraum-ID "1"
9628/// (DE1156), against 0:00 German time of the day after DTM+137
9629/// (Nachrichtendatum). `False` when the SG6 has another Zeitraum-ID — [401],
9630/// [134] and [135] judge those.
9631///
9632/// "Dieses DTM+Z25" is the one of the enclosing SG6. Without a scope, every
9633/// SG6 with Zeitraum-ID 1 must pass.
9634// HAND-EDITED
9635fn first_period_start_vs_next_day(
9636    ctx: &EvaluationContext,
9637    holds: impl Fn(chrono::NaiveDateTime, chrono::NaiveDateTime) -> bool,
9638) -> ConditionResult {
9639    use chrono::Duration;
9640    let Some(sent) = ctx
9641        .find_segments_with_qualifier("DTM", 0, "137")
9642        .first()
9643        .and_then(|dtm| german_local_time(dtm.get_component(0, 1)))
9644    else {
9645        return ConditionResult::Unknown;
9646    };
9647    let next_day = (sent.date() + Duration::days(1)).and_hms_opt(0, 0, 0);
9648    let Some(next_day) = next_day else {
9649        return ConditionResult::Unknown;
9650    };
9651    let judge = |path: &[(&str, usize)]| -> Option<ConditionResult> {
9652        let first_period = ctx
9653            .find_segments_at(path, "RFF")
9654            .iter()
9655            .any(|rff| rff.get_component(0, 2) == "1");
9656        if !first_period {
9657            return None;
9658        }
9659        let start = ctx
9660            .find_segments_at(path, "DTM")
9661            .iter()
9662            .find(|dtm| dtm.get_component(0, 0) == "Z25")
9663            .and_then(|dtm| german_local_time(dtm.get_component(0, 1)));
9664        Some(match start {
9665            Some(start) => ConditionResult::from(holds(start, next_day)),
9666            None => ConditionResult::Unknown,
9667        })
9668    };
9669    if let Some(scope) = ctx.enclosing_scope("SG6") {
9670        return judge(scope).unwrap_or(ConditionResult::False);
9671    }
9672    if ctx.navigator().is_none() {
9673        return ConditionResult::Unknown;
9674    }
9675    let mut result = ConditionResult::False;
9676    for sg4 in 0..ctx.group_instance_count(&["SG4"]) {
9677        for sg6 in 0..ctx.child_group_instance_count(&["SG4"], sg4, "SG6") {
9678            match judge(&[("SG4", sg4), ("SG6", sg6)]) {
9679                Some(ConditionResult::False) => return ConditionResult::False,
9680                Some(ConditionResult::Unknown) => result = ConditionResult::Unknown,
9681                Some(ConditionResult::True) if result == ConditionResult::False => {
9682                    result = ConditionResult::True
9683                }
9684                _ => {}
9685            }
9686        }
9687    }
9688    result
9689}