
    qj_                        d Z ddlZddlZddlZddlZddlZddlZddlZddlZ	ddl
m
Z
 dZdZdZdZdd	d
ddddZdZdZdZdddddZddddddddZddd d!d"Zd#d$d%Zg d&Zd' Zd( Zd) Zd* Zd+ Zd,ed-efd.Zd/ Z d0ed-efd1Z!d2e"d3ed-e"fd4Z#d5e	j$        d6e	j$        d-e	j$        fd7Z%d8 Z&d@d,ed9e	j$        d3efd:Z'd;ed-edz  fd<Z(d-e"fd=Z)d> Z*e+d?k    r e*             dS dS )Au  
Индексатор документов — строит SQLite базу с эмбедингами через aitunnel.ru.

Что хранит:
  - Все метаданные элемента (element_id, contractor, doc_types, ...)
  - file_ids для загрузки по требованию агентом
  - Текст анализа (PROPERTY_2989) если есть
  - Эмбединг для семантического поиска

Использование:
    python3 indexer.py                        # индексирует matched_records.json
    python3 indexer.py --all                  # забирает ВСЕ записи из API (~12к)
    python3 indexer.py --search "авансирование риск"
    python3 indexer.py --id 66991             # показать запись по ID
    python3 indexer.py --stats                # статистика базы
    python3 indexer.py --model gemini-embedding-001  # выбрать модель
    N)datetimezdata/documents.dbz:https://engeocentr.bitrix24.ru/rest/4391/n8rq6nyzr9z8tvdz/z%https://api.aitunnel.ru/v1/embeddingsz,sk-aitunnel-0H1gwP4EewVgEnwkMKFE1bjm8ZtzkZ6jzgemini-embedding-2-previewzgemini-embedding-001ztext-embedding-3-smallztext-embedding-3-largezgigachat-embeddings-giga-rzqwen3-embedding-8b)gemini2geminismalllargegigachatqwen       u   БДКu   ТЗКu
   ИНГЕОu   ВЦПО)3733753771059u   Камергер (общий)u   Дмитровка (общий)u	   БД-ПАu	   КА-НСu	   КА-ПАu)   Стр.7 Б. Дмитровка, дом.9u	   БД-НС)107110731588115883158851736339129u   Отклоненоu   Согласованоu0   Согласовано и распечатаноu   Не установлен)363365367419u   Договор/ДСu   Счет/Акт)357361)
)u   КС-2/КС-3u   КС-2\s*/\s*КС-3)u   УПДu
   \bУПД\b)u   Счет-фактураu)   [Сс]чет-фактура|[Сс]ч-ф\b)u   Актu
   \bАкт\b)u   Счетu   [Сс]чет\s*№)u   КС-2u   КС-2\b)u   КС-3u   КС-3\b)u   Справкаu   [Сс]правка)u   Договорu   Дог(?:овор)?\.?\s*№)u   ДСu   \bДС\s*№c                     | sdS t          | t                    r-t          |                                           }|r|d         ndS | S )N r   )
isinstancedictlistvaluesfieldvalss     2/Users/genius/Desktop/AI_BOT_V2/baseDoc/indexer.pyscalarr'   S   sK    % 'ELLNN##&tAwwB&L    c                 &   | sg S t          | t                    rRt          |                                           }|r#t          |d         t                    r|d         S d |D             S t          | t                    r| S t	          |           gS )Nr   c                 0    g | ]}|t          |          S  )str).0vs     r&   
<listcomp>z multi_values.<locals>.<listcomp>_   s#    ***1*A***r(   )r   r    r!   r"   r,   r#   s     r&   multi_valuesr0   Z   s    )% +ELLNN##=JtAw--=d1g~******%,uJJ<r(   c                     g }t           D ]1\  }}t          j        || pd          r|                    |           2t	          t
                              |                    S )Nr   )DOC_PATTERNSresearchappendr!   r    fromkeys)textfoundnamepats       r&   parse_doc_typesr;   c   s\    E! : :	c9S$*"%%9u||D'9'9'9e$$%%%r(   c                     g }t          j        d| pd          D ]}|                    d|z              t          j        d| pd          D ]}|                    d|z              |S )Nu-   к\s*Договору\s*№\s*([\w\d\.\-\/]+)r   u   Договор №u   по\s*ДС\s*№\s*([\w\d\.]+)u   ДС №)r3   findallr5   )r7   refsms      r&   extract_contract_refsr@   i   s    DZH$*RTUU . .(1,----Z:DJBGG $ $JN####Kr(   c                 Z    | sdS t          j        dd|                                           S )Nr   z<[^>]+> )r3   substrip)r7   s    r&   
strip_htmlrE   q   s,    6*c4((..000r(   recordreturnc                     |                      dd          |                      dd          |                      dd          d                    |                      dg                     |                      dd          |                      dd          |                      d	d          |                      d
d          |                      dd          |                      dd          |                      dd          d                    |                      dg                     t          |                      dd                    dd         g}d                    d |D                                                       S )u_   Текст для эмбединга — объединяем всё значимое поле.r9   r   
contractorpayerrB   	doc_typesnum_date_strnum_date_inamountstatusobjectexpense_itemcommentcontract_refsanalysis_textNi  c              3      K   | ]}||V  	d S )Nr+   )r-   ps     r&   	<genexpr>z#build_embed_text.<locals>.<genexpr>   s'      **!*A******r(   )getjoinrE   rD   )rF   partss     r&   build_embed_textr[   u   s@    	

62

<$$

7BK,,--

>2&&

="%%

8R  

8R  

8R  

>2&&

9b!!OR00116::or2233ETE:E 88**u*****00222r(   c                     t          |                     d                    }t          |t                    r|                    dd          S |pdS )NPROPERTY_2989TEXTr   )r'   rX   r   r    )itemress     r&   get_analysis_textra      sH    
/**
+
+C#t8SWWVR%8%889"r(   r_   c                    t          |                     dd                    }|                     dd          }g }dD ]'}|t          |                     |                    z  }(d |D             }t          t          |                     dd                              }t          t          |                     dd                              }t          t          |                     dd                              }t	          |          pt	          |          }i d	| d
         d|d|                     dd          d d         dt          |                     dd                    dt          |                     dd                    dt
                              ||          dt                              ||          d|dt          |          d|dt          |                     dd                    dt          |                     dd                    dt                              ||          dt                              t          t          |                     dd                              t          |                     dd                              dt          |                     d d                    d!t          |                     d"d                    d#t          |                     d$d                    t          |                     d%d                    |t          t          |                     t          |           d&S )'NPROPERTY_391r   NAME)PROPERTY_415PROPERTY_451PROPERTY_503PROPERTY_2185c                     g | ]}||S r+   r+   )r-   fs     r&   r/   z)build_record_from_api.<locals>.<listcomp>   s    ++++!+++r(   PROPERTY_427PROPERTY_401PROPERTY_389
element_idIDr9   date_createDATE_CREATE
   rI   PROPERTY_1367
company_idPROPERTY_423rJ   doc_type_rawrK   rS   rL   rM   PROPERTY_383rN   PROPERTY_421rO   rP   PROPERTY_387rQ   PROPERTY_397rR   PROPERTY_413payment_duePROPERTY_1415PROPERTY_431)approved_datefile_idshas_analysisrT   )r'   rX   r0   r,   r;   	PAYER_MAPTYPE_MAPr@   
STATUS_MAP
OBJECT_MAPboolra   )	r_   nom_dattitler   proppayer_id	status_idtype_idrK   s	            r&   build_record_from_apir      sF   4455G&"%%EHQ 1 1L$000++X+++HVDHH^R8899::HVDHH^R8899::IVDHH^R8899::G ))C_U-C-CId 	-44SbS9 	" = =>>	
 	 < <== 	x:: 	gw77 	 	.w77 	 	 < <== 	 < <== 		9== 	F488NB3O3O,P,P(Q(QSYZ^ZbZbcqsuZvZvSwSwxx 	 < <==  	 < <==!" 	" = =>>#$   < <==!/5566*400+   r(   textsmodelc                    t          t                    D ]e}	 t          j        t          dt
           dd|| dd          }|                                 |                                }t          |d         d 	          }d
 |D             }|	                    di           }|	                    dd          }|r*t          d|	                    dd           d| d           |c S # t          $ r}	|t          dz
  k     r6d|z  }
t          d|dz    d|	 d|
 d           t          j        |
           n4t          dt           d|	            dgt          |           z  cY d}	~	c S Y d}	~	_d}	~	ww xY wdS )u   
    Отправить батч текстов в aitunnel, вернуть список numpy-векторов.
    Повторяет при ошибке до RETRY_MAX раз.
    zBearer zapplication/json)AuthorizationzContent-Type)r   input<   )headersjsontimeoutdatac                     | d         S )Nindexr+   )xs    r&   <lambda>zembed_batch.<locals>.<lambda>   s
    qz r(   )keyc                 F    g | ]}t          j        |d          d          S )	embeddingfloat32dtype)nparray)r-   r_   s     r&   r/   zembed_batch.<locals>.<listcomp>   s,    VVVrx[ 1CCCVVVr(   usagecost_rubr   z    tokens: total_tokens?z	 | cost: u    руб.      u       ⚠️  Попытка u    ошибка: u    — повтор через u   сu        ❌ Ошибка после u    попыток: N)range	RETRY_MAXrequestspostAITUNNEL_URLAITUNNEL_KEYraise_for_statusr   sortedrX   print	Exceptiontimesleeplen)r   r   attemptrespr   itemsvectorsr   costewaits              r&   embed_batchr      s   
 ## !+ !+ 	+=%=|%=%=%7   %u55  D !!###99;;D 4<-A-ABBBEVVPUVVVG HHWb))EHHZ,,D ^\UYY~s%C%C\\d\\\]]]NNN 	+ 	+ 	+Q&&G|vGAIvvavvnrvvvwww
4    XXXUVXXYYYvE

********* !    		+5!+ !+s   CC,,
E96A0E4&E94E9	query_vecmatrixc                     | t           j                            |           dz   z  }|t           j                            |dd          dz   z  }||z  S )uf   Косинусное сходство query против матрицы всех эмбедингов.g&.>r   T)axiskeepdims)r   linalgnorm)r   r   qr?   s       r&   cosine_searchr      sK    RY^^I..56A")..a$.??$FGAq5Lr(   c                 X    |                      d           |                                  d S )Na  
        CREATE TABLE IF NOT EXISTS documents (
            element_id      TEXT PRIMARY KEY,
            name            TEXT,
            date_create     TEXT,
            contractor      TEXT,
            company_id    TEXT,
            payer           TEXT,
            doc_type_raw    TEXT,
            doc_types       TEXT,
            contract_refs   TEXT,
            num_date_str    TEXT,
            num_date_in     TEXT,
            amount          TEXT,
            status          TEXT,
            object          TEXT,
            expense_item    TEXT,
            comment         TEXT,
            payment_due     TEXT,
            approved_date   TEXT,
            file_ids        TEXT,
            has_analysis    INTEGER DEFAULT 0,
            analysis_text   TEXT,
            embed_text      TEXT,
            embed_model     TEXT,
            embedding       BLOB,
            indexed_at      TEXT
        );
        CREATE INDEX IF NOT EXISTS idx_contractor ON documents(contractor);
        CREATE INDEX IF NOT EXISTS idx_date       ON documents(date_create);
        CREATE INDEX IF NOT EXISTS idx_status     ON documents(status);
        CREATE INDEX IF NOT EXISTS idx_payer      ON documents(payer);
    )executescriptcommit)conns    r&   init_dbr      s3        	  	  	B 	KKMMMMMr(   r   c                    t          |          }||                                nd }|                     d|d         |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          |                    d	d          t	          j        |                    d
g           d          t	          j        |                    dg           d          |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          |                    dd          t	          j        |                    dg           d          t          |                    dd                    |                    dd          |||t          j                    	                                f           d S )Na@  
        INSERT INTO documents
            (element_id, name, date_create, contractor, company_id, payer, doc_type_raw,
             doc_types, contract_refs, num_date_str, num_date_in, amount, status,
             object, expense_item, comment, payment_due, approved_date,
             file_ids, has_analysis, analysis_text, embed_text,
             embed_model, embedding, indexed_at)
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        ON CONFLICT(element_id) DO UPDATE SET
            name          = excluded.name,
            date_create   = excluded.date_create,
            contractor    = excluded.contractor,
            company_id  = excluded.company_id,
            payer         = excluded.payer,
            doc_type_raw  = excluded.doc_type_raw,
            doc_types     = excluded.doc_types,
            contract_refs = excluded.contract_refs,
            num_date_str  = excluded.num_date_str,
            num_date_in   = excluded.num_date_in,
            amount        = excluded.amount,
            status        = excluded.status,
            object        = excluded.object,
            expense_item  = excluded.expense_item,
            comment       = excluded.comment,
            payment_due   = excluded.payment_due,
            approved_date = excluded.approved_date,
            file_ids      = excluded.file_ids,
            has_analysis  = excluded.has_analysis,
            analysis_text = excluded.analysis_text,
            embed_text    = excluded.embed_text,
            embed_model   = CASE WHEN excluded.embedding IS NOT NULL
                            THEN excluded.embed_model ELSE embed_model END,
            embedding     = CASE WHEN excluded.embedding IS NOT NULL
                            THEN excluded.embedding ELSE embedding END,
            indexed_at    = excluded.indexed_at
    rn   r9   r   rp   rI   rt   rJ   rv   rK   F)ensure_asciirS   rL   rM   rN   rO   rP   rQ   rR   r|   r   r   r   rT   )
r[   tobytesexecuterX   r   dumpsintr   now	isoformat)r   rF   r   r   
embed_textemb_blobs         r&   upsertr   
  s   !&))J(1(=""$$$4HLL #H 	|fjj44fjjPR6S6S

<$$fjjr&B&BFJJwXZD[D[

>2&&
6::k2..GGG
6::or22GGG

>2&&

="(E(E

8R  &**Xr":":

8R  &**^R"@"@

9b!!6::mR#@#@&**_^`BaBa
6::j"--EBBBFJJ~u--..

?B''E8  ""
G2 2 2 2 2r(   rn   c                 D   |                      dt          |          f                                          }|sd S d |                      d          j        D             }t	          t          ||                    }dD ]!}t          j        ||         pd          ||<   "|S )Nz,SELECT * FROM documents WHERE element_id = ?c                     g | ]
}|d          S )r   r+   )r-   ds     r&   r/   zget_by_id.<locals>.<listcomp>E  s    VVVQAaDVVVr(   zSELECT * FROM documents LIMIT 0)r   rK   rS   [])r   r,   fetchonedescriptionr    zipr   loads)r   rn   rowcolsrecrj   s         r&   	get_by_idr   B  s    
,,EJGY
Z
Z
c
c
e
eCttVV$,,'HIIUVVVDD#C7 , ,CFNd++AJr(   c            
         g d} g dd}}}	 t          j        t          dz   dddd	id
di| |dd          }|                                 |                                }|                    dg           }|sn||z  }t          |                    dd                    }|dz  dk    st          |          |k    r%t          d| dt          |           d|            t          |          |k    rn|dz  }|dz  }|S )N)ro   rd   rq   
CREATED_BYrs   ru   rm   rc   rw   rx   rl   rk   ry   rz   r{   re   rf   rg   rh   r}   r~   r]   r   r   Tzlists.element.getbitrix_processes57ACTIVEYrq   DESC)IBLOCK_TYPE_ID	IBLOCK_IDFILTERELEMENT_ORDERSELECTLIST_NUM   )r   r   resulttotalrr   u     Страница : /2   )	r   r   WEBHOOKr   r   rX   r   r   r   )select	all_itemsstartpager   r   r   r   s           r&   fetch_all_apir   N  sK   	 	 	F  AduIg(;;0to8O%C
 C
 	  
 			2&&eU	DHHWa(())"9>>S^^u44HHHIHHHHIIIy>>U""E
!" r(   c                  F  % t          j                    } |                     ddd           |                     dt          d           |                     dt          d	           |                     d
dd           |                     dt          t          dt           dd                    d t                                          D                       z              |                                 }t          	                    |j
        |j
                  }t          j        dd           t          j        t                    }t!          |           |j        rtt%          ||j                  }|st'          d|j         d           n-t'          d|d                     t'          d|d                     t'          d|d                     t'          d|d                     t'          d|d                      t'          d!|d"                     t'          d#|d$                     t'          d%|d&                     t'          d'|d(                     t'          d)|d*                     t'          d+t)          |d,                               t'          d-|d.                     |                                 d S |j        r(|                    d/                                          d0         }|                    d1                                          d0         }|                    d2                                          d0         }|                    d3                                          }t'          d4|            t'          d5|            t'          d6|            |r,t'          d7           |D ]\  }	}
t'          d8|
 d9|	            |                                 d S |j        rF|                    d:                                          }|s%t'          d;           |                                 d S t7          j        |d0         d<         d=>          }t;          |          }t7          j        d?                    d@ |D                       d=>                              t;          |          |          }t'          dA| dB           t?          |j        g|          }|r|d0         %t'          dC           |                                 d S tA          |d0         |          }|!                                d d dD         d dE         }t'          dF|j         dG           |D ]}||         }t'          dH||         dIdJ|d0          dK|dL                     t'          dMtE          j#        |dN         pdO           dK|dP          dK|dQ         pdR            t'          dStE          j#        |dT         pdO                      t'                       |                                 d S |j$        rdU}dV}t          j%        &                    |          r}t'          dW| dB           tO          |dXY          5 }tE          j(        |          }d d d            n# 1 swxY w Y   dZ |D             }t'          d[t;          |           d\           nt          j%        &                    |          rqt'          d]| dB           tO          |dXY          5 }tE          j(        |          }d d d            n# 1 swxY w Y   t'          d[t;          |           d\           n%t'          d^           |                                 d S tS          d_ |                    d`                                          D                       %%rPt;          |          }%fda|D             }t'          dbt;          %           dct;          |           dd| d           nde}t          j%        &                    |          s)t'          df| dg           |                                 d S tO          |dXY          5 }tE          j(        |          }d d d            n# 1 swxY w Y   |	                    dhg           }t'          dit;          |           d\           t'          dj|            t'          dktT           dl           d0}d0}tW          d0t;          |          tT                    D ]}|||tT          z            }dm |D             }|t;          |          z   } t'          dn|  dot;          |           dp           t?          ||          }!tY          ||!          D ](\  }"}#t[          ||"|#|#|nd            |#|dqz  }#|dqz  })|.                                 |                    d/                                          d0         }$t'          dr           t'          dst                      t'          dt|$            t'          du|            |rt'          dv|            |                                 d S )wNz--all
store_trueu/   Загрузить все записи из API)actionhelpz--searchu%   Семантический поиск)typer   z--idu-   Показать запись по element_idz--statsu   Статистика базыz--modelu,   Модель эмбедингов (default: z)

c              3   ,   K   | ]\  }}d | d| V  dS )  r   Nr+   )r-   kr.   s      r&   rW   zmain.<locals>.<genexpr>x  s2      "L"LTQ===Q=="L"L"L"L"L"Lr(   )r  defaultr   r   T)exist_oku   Запись u    не найденаzelement_id:    rn   zname:          r9   zdate:          rp   zcontractor:    rI   zpayer:         rJ   zdoc_types:     rK   zcontract_refs: rS   zamount:        rN   zstatus:        rO   zfile_ids:      r   zhas_analysis:  r   zembed_model:   embed_modelzSELECT COUNT(*) FROM documentsr   z:SELECT COUNT(*) FROM documents WHERE embedding IS NOT NULLz3SELECT COUNT(*) FROM documents WHERE has_analysis=1z^SELECT embed_model, COUNT(*) FROM documents WHERE embed_model IS NOT NULL GROUP BY embed_modelu   Записей в базе:  u   С эмбедингами:   u   С анализом:      u   Модели:r  zx zSELECT element_id, name, contractor, doc_types, status, amount, file_ids, date_create, embedding FROM documents WHERE embedding IS NOT NULLuV   ❌ Нет эмбедингов — сначала запусти индексацию   r   r   r(   c              3   &   K   | ]}|d          V  dS )r	  Nr+   r-   rs     r&   rW   zmain.<locals>.<genexpr>  s&      ((aQqT((((((r(   uE   Запрашиваю эмбединг для поиска через z...uH   ❌ Не удалось получить эмбединг запросаrr   u   
Результаты для: "z"
z  [z.3fz] ID=z | r   z           r   r         u   —z           file_ids:    zdata/raw_all.jsonzdata/all_records.jsonu(   Читаю сырые данные из zutf-8)encodingc                 ,    g | ]}t          |          S r+   )r   )r-   is     r&   r/   zmain.<locals>.<listcomp>  s!    CCCA,Q//CCCr(   u   Загружено: u    записей
u   Читаю из uU   ❌ Нет данных — сначала запусти: python3 fetch_api.py --allc              3   &   K   | ]}|d          V  dS )r   Nr+   r  s     r&   rW   zmain.<locals>.<genexpr>  s7       
 
AaD
 
 
 
 
 
r(   z<SELECT element_id FROM documents WHERE embedding IS NOT NULLc                 (    g | ]}|d          v|S )rn   r+   )r-   r  existings     r&   r/   zmain.<locals>.<listcomp>  s'    MMMQQ|_H-L-Lq-L-L-Lr(   u   Уже в базе: u5   , осталось проиндексировать: u    из zdata/matched_records.jsonu   ❌ Не найден u    — запусти match.pymatchedu   Из matched_records: u#   Модель эмбедингов: u
   Батч: u"    текстов за запрос
c                 ,    g | ]}t          |          S r+   )r[   r  s     r&   r/   zmain.<locals>.<listcomp>  s!    555!"1%%555r(   [r   u"   ] Эмбедингую батч...r   u   
✅ Готовоu      База:            u       Всего записей:   u      Эмбединги OK:    u      Ошибки:          )/argparseArgumentParseradd_argumentr,   DEFAULT_MODELrY   MODELSr   
parse_argsrX   r   osmakedirssqlite3connectDB_PATHr   idr   r   r   closestatsr   r   fetchallr4   r   
frombufferr   reshaper   r   argsortr   r   allpathexistsopenloadset
BATCH_SIZEr   r   r   r   )&apargsr   r   r   r   embanalmodels_usedr?   crows	first_vecdimr   vecsscorestop_idxr  r  raw_pathall_pathrj   	raw_itemsrecordsbeforematched_pathr   total_ok	total_errbatchr   nr   rF   vectotal_dbr  s&                                        @r&   mainrK  p  s   		 	"	"BOOG|:kOlllOOJS:aObbbOOFS:iOjjjOOI|:YOZZZOOIS]ZZZZ"L"LV\\^^"L"L"LLLM  N N N ==??D JJtz4:..EK&&&&?7##DDMMM w dg&& 	:?$'???@@@@7C$5778881CK112228C$6889997C$5778882CL223336C$466777:C$8::;;;3CM334443CM334445C
O55666?D^)<$=$=??@@@8C$688999

 z =>>GGII!LYZZcceefghRSS\\^^_`alll
 

(** 	 	5e556663c334441411222 	%/"""# % %1m1mmmm$$$$

 { $||Z
 
 (** 	
  	jkkkJJLLLF M$q'!*I>>>	)nnHH((4(((((	
 
 

'#d))S
!
! 	 	`V[```aaaDK=%00 	tAw\]]]JJLLLFQ00..""44R4("-AAAABBB 	 	AQA;q	;;;AaD;;QqT;;<<<U
1Q4<4 8 8UUQqTUUadmeUUVVVD$*QqT\T*B*BDDEEEGGGG

 x )H&*7>>(## 	JXJJJKKKh111 )Q IaLL	) ) ) ) ) ) ) ) ) ) ) ) ) ) )CCCCCGHWHHHIIIIW^^H%% 	2X222333h111 'Q)A,,' ' ' ' ' ' ' ' ' ' ' ' ' ' 'HWHHHIIIIijjjJJLLLF  
 
,,N hjj
 
 
 
 

  	M\\FMMMM'MMMG  LX  L  Lmpqxmymy  L  L  BH  L  L  L  M  M  M 3w~~l++ 	U<UUUVVVJJLLLF,111 	 Q9Q<<D	  	  	  	  	  	  	  	  	  	  	  	  	  	  	 ((9b))Fs7||FFFGGG	
7
7
7888	
Fz
F
F
FGGGHI1c'llJ//  1z>)*55u555SZZF!FFc'llFFFGGGeU++ug.. 	/ 	/KFC4seeDIIIQ )Q		||<==FFHHKH	
   	
.W
.
.///	
7X
7
7888	
4(
4
4555 86966777JJLLLLLs6   "[[
[>]]#&]#b==cc__main__)NN),__doc__r   r   r3   r"  r  r   r   numpyr   r   r$  r   r   r   r  r  r2  r   r   r   r   r   r2   r'   r0   r;   r@   rE   r    r,   r[   ra   r   r!   r   ndarrayr   r   r   r   r   rK  __name__r+   r(   r&   <module>rQ     s   $  				 				               %M8? ,%''+#
 
 -
	 h|ZXX	,.8 
 *2JGPkm m
*3DEE       & & &  1 1 13T 3c 3 3 3 3&  
! ! ! ! ! !L&+t &+C &+D &+ &+ &+ &+RRZ  
    " " "J5 5 5"* 5C 5 5 5 5p t    t    Di i iX zDFFFFF r(   