
    Oj                       d Z ddlmZ ddlZddlZddlmZmZmZm	Z	m
Z
mZ ddlmZmZmZ ddlmZ ddlmZ  ej(                  e      ZdZd	Zd
ZdZdZdZdZdZdZdZ dZ!dZ"eeee e!e"fZ# e$ddh      Z% e$eeeh      Z& e$dh       e$dh       e$dh       e$dh       e$dh      fZ'dAdZ(dZ) e$d e)D              Z*dBdZ+dCdZ, e$dh      Z-dZ.dZ/dZ0d Z1d!Z2 G d" d#e3      Z4dDd$Z5	 	 	 	 dEd%Z6dFd&Z7d'Z8d(Z9d)Z:dGd*Z;dHd+Z<dId,Z=d-Z>d.Z?d/Z@d0ZAd1ZBd2ZCd3ZDd4ZEe?e@eAeCeDeBfZFe/e0fZGdJd5ZHd6d7	 	 	 dKd8ZIdLd9ZJd6d6d:	 	 	 dMd;ZKdLd<ZLdd=	 	 	 	 	 	 	 	 	 	 	 dNd>ZMddd?	 	 	 	 	 	 	 	 	 	 	 	 	 dOd@ZNy)Pu%  GROUNDING-V2 — A0 후보를 **병합된 엔티티에 결속**하는 한 자리.

## 왜 별도 모듈인가

같은 일을 두 곳에서 한다 —

    production   `GroundingPlanStep` 이 `entity_merge` 산출에 결속
    측정         `grounding_shadow` 가 저장된 `entity_merge` CP 에 결속

★**둘이 갈리면 잰 것이 뜻을 잃는다.** 실제로 갈려 있었다: production 은
A0 가 건진 **원문 문장**을 분류기에 넘기는데, 측정 도구는 LLM 이 상상해 쓴
`description` 을 넘겼다. 같은 대상을 다른 근거로 판정해 놓고 「production 을
쟀다」고 쓰게 된다.

그래서 결속을 **한 함수**로 모은다. 도구가 프로덕션과 다른 입력을 보내려면
이 파일을 고쳐야 하고, 고치면 양쪽이 같이 바뀐다.
    )annotationsN)AnyDictListOptionalSequenceTuple)FACET_BINDINGHOST_CONTEXTPRODUCER_PAYLOAD)_norm)build_subject   matchednone	ambiguousduplicate_surface	contestedcarriedpromoteddeferred
unresolvedentity_onlylocation_partoutlook	characterlocationpropc                @    t         D ]
  }| |v s|c S  t        | h      S )uX   그 owner 와 **같은 대상일 수 있는** owner 들. 못 찾으면 자기 자신만.)BINDABLE_OWNER_GROUPS	frozenset)
owner_typegs     Z/Users/manta/Documents/Projects/TheRoad-I1/backend/app/modules/pipeline/grounding_carry.pybindable_ownersr%   h   s'    "?H # j\""    ))
charactersr   )	locationsr   )propsr   )location_partsr   c              #  &   K   | ]	  \  }}|  y wN ).0_kos      r$   	<genexpr>r1      s     >*=Q1*=s   c                n    | xs dj                         }|t        v rt        S |t        v rt        S t
        S )u  **안 붙은** 후보가 어디로 가는가.

    ```
    facet owner (location_part · outlook) → deferred   뒤 producer 가 가진다
    base owner  (character·location·prop) → promoted   A0 신원 그대로 승격
    그 밖                                  → unresolved
    ```

    ★`location_part` 는 `prop` 과 같은 묶음이라 **결속은 시도한다**. 그래도
    안 붙으면 `deferred` 다 — 승격하면 §2-6.5 producer 가 만들 때 같은 대상이
    둘이 된다.
     )stripFACET_OWNERSDISP_DEFERREDSCANNED_OWNERSDISP_PROMOTEDDISP_UNRESOLVED)r"   r0   s     r$   disposition_ofr:      s9     
	r  "ALNr&   c                J   i }| xs dD ]^  }|j                  d      xs dt        |j                  dd            f}|d   r|d   s>|j                  |g       j                  |       ` ||j	                         D ch c]  \  }}t        |      dkD  s| c}}dS c c}}w )u  owner 별 표면형 색인. ★같은 표면형이 둘이면 **둘 다 뺀다.**

    A0 는 「같은 대상을 여러 번 적지 마세요」를 지시받지만 지켜진다는 보장이
    없다. 겹친 것을 그냥 두면 아래 짝짓기가 **아무 쪽에나** 붙는다.

    ★같은 key 를 **덮지 않는다.** 덮으면 겹친 후보 중 하나의
    id·anchor·인용이 그 자리에서 사라지고, 뒤에서 「원 후보를 보존한다」가
    거짓이 된다.
    r-   r"   r3   surface_formr      )indexr   )getr   
setdefaultappenditemslen)
candidatesr>   ckeykvs         r$   build_carry_indexrI      s     :<E2uu\"(b%nb0I*JK1vSVb!((+	 
 (-D1Q!!DF FDs   >BBproducer_linkissuercontract_versionfinal_idlocal_idc                      e Zd ZdZy)LinkConflictuH   링크가 **서로 다른 것**을 가리킨다. ★붙이지 않는다.N)__name__
__module____qualname____doc__r-   r&   r$   rP   rP      s    Rr&   rP   c                 t    ddl m}  | j                         D ci c]  \  }}|t        |       c}}S c c}}w )u[   이 코드가 **아는** 발급자와 그 계약 판. ★여기서 손으로 안 적는다.r   )KNOWN_PRODUCERS).app.modules.pipeline.grounding_entity_contractrV   rB   r!   )rV   rG   rH   s      r$   known_producersrX      s4    N(7(=(=(?@(?1Ay|O(?@@@s   4c           
        t        | dd      \  }}|t        t        t        t        fv r|S g }|xs i j                         D ]|  \  }}~|xs dD ]m  }t        |t              st        | t        |j                  d      xs d      t        |            \  }}	|	t        k(  r
t        c c S |j                  |	       o ~ |rt        d |D              rt        S t        S )u  이 후보의 링크가 **아무 엔티티에도 안 맞은** 까닭. ★한 번만 정한다.

    ★엔티티와 **무관한** 흠(모양·발급자·계약·두 자리 충돌)이 먼저다 — 그것이
    있으면 어느 엔티티와 대조해도 같은 답이라 좌표 이야기를 할 필요가 없다.
    ★그 다음이 좌표다: 어디에도 안 맞았으면 `coordinate_mismatch` 다.
    r3   short_idrN   r-   r[   c              3  .   K   | ]  }|t         k(    y wr,   )LINK_COORD_MISSING)r.   ws     r$   r1   zlink_verdict.<locals>.<genexpr>   s     :TA++Ts   )verified_linkLINK_MALFORMEDLINK_UNKNOWN_ISSUERLINK_BAD_CONTRACTLINK_STORAGE_CONFLICTrB   
isinstancedictstrr?   entity_local_idLINK_OKrA   allr]   LINK_COORD_MISMATCH)
candentities_lwhyseenrF   rowse_l2why2s
             r$   link_verdictrt      s     D2;GB
~24E$& &
Dn"++-	TAa&%s155#4#:;(+-IC wKK  . :T::!!r&   c                    | xs i j                  di       j                         D cg c]3  \  }}|D ])  }t        |xs i j                  d      xs d      |k(  r|+ 5 c}}}S c c}}}w )u[   그 갈래의 후보 전부. ★사유를 모으려면 **붙은 것 밖**도 봐야 한다.r>   r"   r3   )r?   rB   rf   )carryownerr/   rH   rE   s        r$   _all_candidatesrx      sp    !KR,,Wb9??A @A%"a1aAG==.4"5> 1AA @ @ @s   8A&	top_level
provenancelink_raw_slotsc                    | xs i }g }t         |ft        |j                  d      xs i ffD ]M  \  }}t        |t              st
        |v s |j                  |t        j                  |t
                 d       O |S )u  링크 칸이 **있는 자리마다** 위치와 값을 원형 그대로.

    ★★★앞 판은 첫 슬롯 하나만 남겨서 두 가지를 잃었다 (Codex · 09-01) —
      ①값이 `None` 이면 「없음」과 구별이 안 됐다(칸은 있는데 안 남았다)
      ②두 자리가 다를 때 **무엇과 부딪혔는지** 두 번째 값이 사라졌다

    ★칸이 **아예 없을 때만** 빈 목록이다.
    rz   )r   value)	LINK_AT_TOPLINK_AT_PROVENANCEr?   rd   re   LEDGER_LINKrA   copydeepcopy)rk   rE   outwhereholders        r$   r{   r{      s     	
A "C&*-quu\/B/HbIKvfd#v(=JJE!%vk/B!CE FK
 Jr&   c                    | xs i }g }||j                  d      xs i fD ]4  }t        |t              st        |v s|j	                  |t                  6 |S )u  링크 칸이 **있는** 자리의 값들. ★두 저장 위치를 다 본다.

    ★★값이 dict 이 아니어도, 비어 있어도 **그대로 담는다** (Codex · 09-01).
    앞 판은 `isinstance(dict) and got` 로 걸러서, 칸이 있는데 문자열·`None`·
    빈 dict 이면 **「없음」으로 접혔다**. 그러면 producer 계약이 깨진 것이
    평범한 「링크 없음」처럼 보이고, 더 나쁘게는 한쪽이 망가졌는데 다른 쪽이
    멀쩡하면 **그것만 보고 붙었다**.
    rz   )r?   rd   re   r   rA   )rk   rE   r   r   s       r$   _link_slotsr     sX     	
ACaeeL)/R0fd#v(=JJvk*+ 1 Jr&   c                Z     t         t              r syt         fdt        D              S )uE   링크 모양이 아닌가. ★필수 칸이 없어도 malformed 다.Tc              3  x   K   | ]1  }t        j                  |      xs d       j                           3 yw)r3   Nrf   r?   r4   )r.   rG   r}   s     r$   r1   z _is_malformed.<locals>.<genexpr>%  s0     JMq3uyy|)r*0022Ms   7:)rd   re   anyLINK_REQUIRED)r}   s   `r$   _is_malformedr   !  s#    eT"%JMJJJr&   no_linkunknown_issuercontract_not_knownstorage_places_disagreemalformed_linkcoordinate_mismatchtarget_has_no_such_coordinateokc                   | xs i }t        |j                  d      xs d      j                         }|r|S |j                  d      xs |j                  d      xs i }t        |xs i j                  d      xs d      j                         S )u  엔티티 행의 `local_id`. ★adapter 는 그것을 **provenance 안**에 둔다.

    top-level 만 보면 늘 비어 있고, 그러면 「대상에 없는 좌표」를 검사할 수
    없어 링크가 낸 엉뚱한 `local_id` 가 **그냥 지나간다** (Codex · 09-01).
    rN   r3   grounding_provenancerz   r   )entityrq   gotpvs       r$   rg   rg   ;  s~     	"A
aeeJ%2
&
,
,
.C

	
%	&	C!%%*=	CBb~~j)/R06688r&   r3   )rN   c               l   t        |       }|sdt        fS t        d |D              rdt        fS |d   }|dd D ]  }||k7  s	dt        fc S  t        |j                  t              xs d      j                         }t               }||vrdt        fS t        |j                  t              xs d      j                         ||   vrdt        fS t        |j                  t              xs d      j                         }t        |j                  t              xs d      j                         }	t        |xs d      j                         t        |xs d      j                         }}
||
f|	|ffD ]#  \  }}|s	|s
dt        fc S ||k7  sdt         fc S  |s
|	sdt        fS t#        |      t$        fS )u=  이 후보를 그 엔티티에 붙일 **검증된 링크**와 **사유**.

    ★**전부 맞아야 한다** —
      ①발급자가 **아는 것**이고 그 발급자의 **아는 계약 판**을 달았다
      ②두 저장 위치(top-level·provenance)에 있으면 **서로 같아야** 한다
      ③★링크가 낸 좌표는 **대상 쪽에도 있어야 하고 같아야** 한다.
        없으면 무시하지 않고 **거절**한다 — 앞 판은 대상 `local_id` 가 비면
        링크의 엉뚱한 `local_id` 를 안 봤고, `final_id` 하나가 맞으면 OR 로
        통과시켰다.
      ④적어도 한 좌표는 있어야 한다

    Returns:
        `(링크 or None, 사유)`. 사유는 `LINK_OK` 이거나 못 믿은 까닭이다 —
        **「없다」와 「틀렸다」를 가른다**.
    Nc              3  2   K   | ]  }t        |        y wr,   )r   )r.   rH   s     r$   r1   z verified_link.<locals>.<genexpr>]  s     
+U=Us   r   r=   r3   )r   	LINK_NONEr   r`   rc   rf   r?   LINK_ISSUERr4   rX   ra   LINK_CONTRACTrb   LINK_FINAL_IDLINK_LOCAL_IDr]   rj   re   rh   )rk   r[   rN   linksfirstotherrK   knownfidlidwant_fwant_lr   wants                 r$   r_   r_   I  s   " EY

+U
++^##!HEqrE>...  ;'-2.446FEU(((
599]#)r*002%-G&&&
eii&,"
-
3
3
5C
eii&,"
-
3
3
5CR(..0#hn"2E2K2K2MFFFmc6]3	T+++$;,,, 4 3''';r&   c                .   t        |      }|sg S t        |      t        z
  }|t        v rg S g }| d   j                         D ]L  \  }}|d   |vs|d   |v s||d   v s|D cg c]  }t	        |      r| }	}|	s:|j                  ||	f       N |S c c}w )Nr>   r   r=   )r   r%   STRUCTURAL_ONLY_OWNERSrB   r{   rA   )
rv   namer"   r   r   r   rG   rH   rE   kepts
             r$   _hitsr   z  s    ;D	 
	$'=	=B++	
Cg$$&1Q4r>!A$$,$!A$,61aN1$516JJ4y! ' J 7s   !B2BrZ   c               R   | d   j                         D cg c]@  \  }}|D ]6  }t        |j                  d      xs d      |k(  rt        |||      d   r|8 B }}}}|r!t	        |      dkD  rdt
        fS |d   t        fS |t        v r-g }	|	sdt        fS t	        |	      dkD  rdt
        fS |	d   t        fS t        |      sdt        fS t        | ||      }
|
sdt        fS t	        |
      dkD  rdt        fS |
d   \  }}t	        |      dkD  rdt
        fS |d   t        fS c c}}}w )u  살아남은 이름에 맞는 A0 후보. ★한 개로 정해질 때만 돌려준다.

    가르는 규칙 셋:

    1. **owner 가 같은 묶음이어야 한다**(`BINDABLE_OWNER_GROUPS`). 사람과 그
       사람이 입은 것은 서로 다른 대상이라 표면형이 겹쳐도 결속하면 안 된다.
       `prop`↔`location_part` 는 그 혼동이 아니라 **같은 대상의 잠정 갈래**다.
    2. **양방향 포함**으로 본다 — 추출이 「고무줄로 묶인 회수권 뭉치」를
       「회수권 뭉치」로 줄여도 잡아야 한다. 뜻으로 묶는 것이 아니라
       **내가 넣은 말이 남았는지**를 보는 것이다.
    3. **둘 이상 걸리면 안 붙인다.** 「가장 긴 것을 고른다」는 임의 선택이고,
       그렇게 붙인 id 는 다른 대상의 근거를 물려받는다.

    Returns:
        (후보 or None, 사유).
    r>   r"   r3   rZ   r   r=   N)rB   rf   r?   r_   rC   CARRY_DUPLICATECARRY_MATCHEDr   
CARRY_NONEr   r   CARRY_AMBIGUOUS)rv   r   r"   r[   rN   r/   rH   rE   linkedsamehits_keycandss                r$   match_candidater     sO   , #7^113 M3EBQQUU<(.B/:=HxHK Qa3F M v;?((ay-''++##t9q=((Aw%%;Zj)DZ
4y1}_$$q'KD%
5zA~_$$8]""3Ms   AD"c                    t        | ||      D cg c]O  \  }}|D ]E  }|j                  d      |j                  d      |j                  d      |j                  d      dG Q c}}}S c c}}}w )uY   걸린 후보를 **전부** 편다 — 하나만 남기면 다른 근거가 사라진다.research_subject_idsource_anchorr<   source_quote)r   r   r<   r   )r   r?   )rv   r   r"   r/   rH   rE   s         r$   
_originalsr     s{     5$
3
 4EBQ	 !"&; <%%0~.~.	0 >?		0 4  s   AA+)a0_candidatesc               
   t        t        |xs d            }g }g }i }d}	i }
t        D ]  \  }}| j                  |      xs dD ]  }t	        |t
              r#|j                  d      xs dj                         s7t        ||d   j                         |t        |j                  d      xs d      t        |            \  }}|s|j                  d      xs d}|
j                  |d      dz   |
|<     |
j                         D ch c]  \  }}|dkD  s| }}}t               }i }i }i }t               }t               }t        D ]  \  }}| j                  |      xs dD ]  }t	        |t
              s|j                  d      xs dj                         }|s;|j                  d      xs dj                         }t        ||||t        |            \  }}d	}|rt        ||t        |            \  }}|r|j                  d      xs d|v rd	t        }}|j                  |d      dz   ||<   |r|	dz  }	|j                  |j                  d      xs d       |r|||j                  d      xs d<   i |j                         D  ci c]  \  }} |d
v r||  c} }|j                  d      rdt        j                   |d         ini |j                  t"              r"t"        t        j                   |t"                 ini |j                  t$              r"t$        t        j                   |t$                 ini |j                  t&              r"t&        t        j                   |t&                 ini i |j                  d      xs i |xs d	dd|rt(        |ini |j                  d      xs ddd}!n|t*        k(  r|t-        |||xs d| |||xs d	||d      }!|j                  d      xs d|!d<   d|!d<   |j                  d      xs i j                  d      }"|"rt        j                   |"      |!d<   ngt/        |||      D ](  }#|#j                  d      s|j                  |#d          * |j1                  dd| dd|xs d	||d	t/        |||      d	       I|!d   }$|$|v rT|j                  |$       |rt2        nt4        ||$<   |j1                  |!         |xs dD ]V  }%t        |%xs i j                  d      xs d      }|r||v st7        |%      s7dt9        |%|       t:        t7        |%      i||<   X |xs dD ]  }%|%xs i j                  d      xs d}|r||v s||v s||v r,||v r1t7        |%      r=t=        |%xs i j                  d      xs d      t>        k7  rg|j                  |       |j1                  i |%j                         D  ci c]  \  }} |d
v r||  c} }|%j                  d      rdt        j                   |%d         ini |%j                  t"              r"t"        t        j                   |%t"                 ini |%j                  t&              r"t&        t        j                   |%t&                 ini |%j                  t$              r"t$        t        j                   |%t$                 ini i |%j                  d      xs i d	dd|%j                  d      xs ddd        tA        t        |xs d      || |||||      }&tB        jE                  d tG        |xs d      |	tG        |      ||&d!          |||||	|&d"S c c}}w c c} }w c c} }w )#u>  병합된 엔티티(`entity_merge`) → 분류할 subject 목록 + 못 붙인 목록.

    ★production 과 측정 도구가 **이 함수 하나**를 쓴다.

    Returns:
        ``subjects``   분류기에 넣을 것 (A0 물려받았거나, 후보가 없던 것)
        ``unbound``    ★분류기에 **안 넣는다** — 후보는 있는데 못 정한 것.
                       원 후보의 id·anchor·원문 인용을 그대로 담은
                       ``unresolved`` 행이다.
        ``carry_reasons`` 사유별 개수 · ``carried`` 물려받은 수
    r-   r   r   r3   r[   rZ   r   r=   N)	rL   r   
project_id
episode_idr   r<   r"   canon_id
bind_stateoccurrencesrz   grounding_a0)r[   carried_fromr   
manuscript)rz   r   quote_sourcezname:)r[   source_stepcarry_reason)r   r   r   r<   r"   rz   descriptionentity_descriptionr   r   r   	a0_carry_)	routeroute_override_reasonsample_count
unanswered	_short_id_surface_form_owner_typer   r   reasonr"   )r[   promoted_from)r   	entangled
used_linkslink_faultsuR   grounding carry: 후보 %d개 중 %d개 결속 · 못 붙임 %d · %s · 장부 %sby_disposition)subjectsunboundsubject_dispositionscarry_reasonsr   candidate_ledger)$rI   listENTITY_KEY_TO_OWNERr?   rd   re   r4   r   rf   rg   rB   setr_   CARRY_CONTESTEDaddr   r   r   r
   r   r   r   r   r   rA   DISP_CARRIEDDISP_ENTITY_ONLYr{   rt   LINK_RAW_SLOTSr:   r8   _candidate_ledgerloggerinforC   )'rl   r   r   r   r   rv   r   r   reasonsr   _claimrF   rw   rq   hitrn   ridrn
_contestedseen_idsr   r   r   	bound_idsr   r   sida0	used_link	_why_linkrG   rH   subj_occ_orsidrE   ledgers'                                          r$   build_subjectsr     s   & d=#6B78E%'H$&G GG
  F)
Uc"(b(Aa&quuV}/B.I.I.K&qy(%QUU:.4"5(+-HC gg34:$jja014s ) * !'81!a%!J8EH!#J"$K ,.UI UI))
Uc"(b(Aa&EE&M'R..0D55$*113C%eT53/>q/ACGB I'4q/A(C$	9 rvv34:zIC";;sA.2GCL1bff%:;ArBFOJrvv&;<BC(*
 /
1  . . !t
 / &&/ &t}}R5F'GH57  &&!12 )$--;K8L*MN8:!$ &&/ &t}}R5F'GH57%( &&. %dmmB|4D&EF46)*#=bff\&:&@b #=/2{d3A#= *3 (3I&>8:	#= %'FF>$:$@b$09< 
"$)j"%"75d$,/K4/:03 5	6 ()uu]';'Ar^$';^$ 56<"]+ *.--*=D' %UD%8Bvv34!b)>&?@ 9 )/8->$%"#!$%)#(+/%/tU%C
  
 -.Dx LL
 ;=,/? !&OOD![ ) *j !r!17-- 56<"=cY&nQ.?$l1h&?*N1,=?C	 " !r!wBmm128bcY&#*;sh) !17--5;<MS 
 !	 '	1 & & !t	 '
 %%& t}}Q}-=>?,.
 %%() !$--2B0C"DE/1
 %%% dmmAlO<=+-
 %%& t}}Q}-=>?,.
<aeeL17R <'+,:<
 EE.17R()
 	 "J tM$7R8%(J)2z+68F KKdM'R('3w<'(* !W$8 W !'( (w 9V/f's   ]3]3]9/]?)r   r   c               x   g }| D ]y  }	|	xs i j                  d      xs d}
|	xs i j                  d      xs dj                         }t        |	      }|
r|
|v rt        n0|
r	|rt        n%|
r|
|v rt
        n|
r|
|v rt        nt        |      |
||	xs i j                  d      xs dd}t        k(  r|rt        j                  |      |t        <   |xs i j                  |
      }|rt        |      |t        <   |xs i j                  |
      }|rXt        |j                  d      xs d      |d<   |j                  t              xs g }|rt        j                  |      |t        <   |j                  |       | t        D ci c]  t        fd|D               }}|D cg c]  }|d   	 }}| D 	cg c]$  }	t        |	xs i j                  d      xs d      & }}	t!        |      D cg c]  \  }}|j                         r| }}}|rt#        d	t%        |       d
|dd  d      t'        |D ch c]  }|j)                  |      dkD  s| c}      }|rt#        d|dd  d      t+        |      t+        |      k7  st%        |      t%        |      k7  rkt#        dt%        |       dt%        |       dt'        t+        |      t+        |      z
        dd  dt'        t+        |      t+        |      z
        dd        |D cg c]  }|d   t        vs| }}|r.t#        dt%        |       d|D cg c]  }|d   	 c}dd        t        |j-                               t%        |      k7  r1t#        dt        |j-                                dt%        |             t%        |       |t'        |D ch c]  }|d   t.        k(  r|d    c}      D ci c]  t        fd|D               c}|dS c c}w c c}w c c}	w c c}}w c c}w c c}w c c}w c c}w c c}w )u
  A0 후보 **전부**가 넷 중 하나로 간다. ★남거나 겹치면 **선다**.

    「소리 없이 사라지는 후보 0건」이 이 함수의 통과 조건이다 (Codex). 합이
    안 맞는데 그냥 돌려주면, 그 장부는 있으나 마나다.
    r   r3   r"   r<   )r   r"   r<   dispositionr   
link_faultc              3  4   K   | ]  }|d    k(  sd  yw)r  r=   Nr-   )r.   r   ds     r$   r1   z$_candidate_ledger.<locals>.<genexpr>  s     >1-(8A(=Qs   u/   후보에 `research_subject_id` 가 빈 것이 u   개 있다 (자리 N   u1   ) — 장부가 어느 후보인지 못 적는다r=   u   후보 id 가 겹친다: u2    — 겹치면 뒤엣것이 조용히 사라진다u-   장부가 후보 집합과 다르다: 장부 u   행 / 후보 u   개 · 장부에만 u    · 후보에만 r  u   갈래 밖 행 u   개: u'   갈래별 합이 행 수와 다르다: z != c              3  J   K   | ]  }|d    t         k(  r|d   k(  rd  yw)r  r"   r=   N)r6   )r.   r   r0   s     r$   r1   z$_candidate_ledger.<locals>.<genexpr>  s0      Rd&-7AlOq<P ds    #)totalr   deferred_ownersrp   )r?   r4   r{   r   r9   DISP_CONTESTEDr:   r   r   r   re   r   rf   rA   DISPOSITIONSsum	enumerateAssertionErrorrC   sortedcountr   valuesr6   )r   rv   rl   r   r   r   r   r   rp   rE   r   rw   slotsrowlinkfaultr  countsr   r   r   irH   blankdupbadr0   s                   `         `r$   r   r     s    "$DwBmm128b'r|,299;q!3)#AU  ASI%ASI%A u%A&) !R}}^<B! E #'--"6C  b%%c*#DzC "'', #EIIh$7$=2 >CIIn-3E '+mmE&:N#C_ b $%#! >>>>#  % .2
2T1"#TC
2EJKUCb34:;UDK$T?<?41a!'')Q?E<=c%j\ JRayk!RTU 	U T7TTZZ]Q%6!T7
8C
'BQy0bce 	e
3x3t9CCI 5;CH:]SVW[S\R] ^%c#hT&:;BQ?@ A%c$i#c(&:;BQ?@BC 	C 
Cda.lB1dC
C
c#hZu-L1a.>-LRa-P,QRT 	T
6==?s4y(5c&--/6J5K4PSTXPY{[] 	] U  d Cd !- 0M A |_d C DED s Rd R R RDE
  =% 3K<
 8 D .MCEsH   P/P)P:PPP#P#P(*P(P-
P2,P7)r"   rf   returnr!   )r"   rf   r  rf   )rD   zSequence[Dict[str, Any]]r  Dict[str, Any])r  zDict[str, frozenset])rk   r  rl   #Dict[str, Sequence[Dict[str, Any]]]r  rf   )rv   r  rw   rf   r  List[Dict[str, Any]])rk   r  r  r  )rk   r  r  z	List[Any])r}   r   r  bool)r   r  r  rf   )rk   r  r[   rf   rN   rf   r  z$Tuple[Optional[Dict[str, Any]], str])rv   r  r   rf   r"   rf   )
rv   r  r   rf   r"   rf   r[   rf   rN   rf   )rl   r  r   rf   r   rf   r   rf   r   z"Optional[Sequence[Dict[str, Any]]]r  r  )r   r  rv   r  rl   r  r   r   r   r   r   r   r   Optional[Dict[str, Any]]r   r  r  r  )OrT   
__future__r   r   loggingtypingr   r   r   r   r   r	   rW   r
   r   r   &app.modules.pipeline.grounding_overlayr   &app.modules.pipeline.grounding_subjectr   	getLoggerrQ   r   CARRY_CONTRACT_VERSIONr   r   r   r   r   r   r8   r6   r	  r9   r   r
  r!   r5   UNBOUND_REASONSr    r%   r   r7   r:   rI   r   r   r   r   r   r   RuntimeErrorrP   rX   rt   rx   r~   r   r   r{   r   r   r   ra   rb   rc   r`   rj   r]   rh   LINK_BROKENr   rg   r_   r   r   r   r   r   r-   r&   r$   <module>r*     s  " #   = =O O 9 @			8	$   
% 
 
 ! m]1AC /956 _oOP. {mykzlvh  #   >*=>>*F. #O#45  "S< SA>CF>@ ! !&"K 	& ( 1  "+ 4 
"$57L"$6H m,9 #%. . )M. b6 (*2/#!$/#58/#d" 9=i(1i( i( 	i(
 i( 6i( i(^ NR>B	d Cd!$d47d "%d 3Kd $<	d HV	dr&   