§
    WÇ½j,<  ã                  ór  — d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
  ee¦  «                             ¦   «         j        j        j        Zedz  Zedz  Zedz  d	z  d
z  Zedz  d	z  dz  Zedz  dz  dz  Zedz  Zedz  dz  Zdd„Z G d„ dej        ¦  «        Zedk    r ej        ¦   «          dS dS )a¡  
Empirical Challenger Test Suite for Milestone M2:
- TypeScript compilation & ClientItemSnapshot protobuf schema field parity
- Async fallback query callback (setQueryItemSnapshotCallback) & anti-spoofing
- Unusual UUIDs, special characters, and hyphenated HMAC hex strings
- WebApp UI 200 consecutive messages across 8 channels & 100-msg ring buffer
- Auto-scroll lock state transitions & mobile viewport ergonomics
é    )ÚannotationsN)ÚPath)ÚAnyÚclientÚwebappÚsrcÚchatzChatManager.tsÚdistzChatManager.jsÚjsÚuiz
chat_ui.jsz
index.htmlÚprotoz
chat.protoÚjs_codeÚstrÚreturnr   c           	     óÐ  — t          t          ¦  «                             dd¦  «        }t          t          ¦  «                             dd¦  «        }d|› d|› d| › d�}t	          j        ddd	|gd
d
dt          t          ¦  «        d¬¦  «        }|j        dk    rt          d|j        › d|j	        › �¦  «        ‚|j
                             ¦   «         }|rt          j        |¦  «        ndS )zPExecutes ES module code in Node.js with mock DOM and returns parsed JSON output.ú\ú/a"  
const createMockElement = (id = '') => {
  let _st = 0, _sh = 1000, _ch = 200, _html = '';
  const children = [];
  return {
    id, tagName: 'DIV', dataset: {}, innerText: '', textContent: '',
    value: '', disabled: false,
    get innerHTML() { return _html; },
    set innerHTML(v) {
      _html = v;
      if (!v) children.length = 0;
    },
    classList: {
      _c: new Set(),
      add(...c) { c.forEach(x => this._c.add(x)); },
      remove(...c) { c.forEach(x => this._c.delete(x)); },
      contains(c) { return this._c.has(c); }
    },
    style: { _p: {}, setProperty(k, v) { this._p[k] = v; }, getProperty(k) { return this._p[k]; } },
    get scrollTop() { return _st; }, set scrollTop(v) { _st = v; },
    get scrollHeight() { return _sh; }, set scrollHeight(v) { _sh = v; },
    get clientHeight() { return _ch; }, set clientHeight(v) { _ch = v; },
    appendChild(child) { children.push(child); return child; },
    querySelectorAll(sel) { return children.filter(c => c._sel === sel); },
    addEventListener: () => {},
    focus: () => {}
  };
};

const mockElements = new Map();
const getOrCreate = (id) => {
  if (!mockElements.has(id)) mockElements.set(id, createMockElement(id));
  return mockElements.get(id);
};

global.document = {
  getElementById: (id) => getOrCreate(id),
  createElement: (tag) => createMockElement(tag),
  querySelectorAll: (sel) => [],
  addEventListener: () => {},
  readyState: 'complete'
};
global.window = { addEventListener: () => {}, chatUI: null };

async function main() {
  const ChatMgr = await import('file:///z+');
  const ChatUI = await import('file:///z');
  zi
}
main().then(() => {
  process.exit(0);
}).catch(err => {
  console.error(err);
  process.exit(1);
});
Únodez--input-type=modulez-eTúutf-8é
   )Úcapture_outputÚtextÚencodingÚcwdÚtimeoutr   zNode execution failed (code z):
N)r   ÚCHAT_MGR_JSÚreplaceÚ
CHAT_UI_JSÚ
subprocessÚrunÚPROJECT_ROOTÚ
returncodeÚRuntimeErrorÚstderrÚstdoutÚstripÚjsonÚloads)r   Úmgr_pathÚui_pathÚ	bootstrapÚresÚouts         ú;C:\Projects\FreeExile\tests\unit\test_challenger_chat_m2.pyÚrun_node_evalr/      s  € å•;ÑÔ×'Ò'¨¨cÑ2Ô2€HÝ•*‰oŒo×%Ò% d¨CÑ0Ô0€Gð7ðZ *2ð[7ð 7ð\ )0ð]7ð 7ð^ ð_7ð 7ð 7€Iõp Œ.Ø	Ð&¨¨iÐ8Ø $°½cÅ,Ñ>OÔ>OÐY[ðñ ô €Cð „~˜ÒÐÝÐZ¸#¼.ÐZÐZÈcÌjÐZÐZÑ[Ô[Ð[Ø
Œ*×
Ò
Ñ
Ô
€CØ!Ð+�4Œ:�c‰?Œ?ˆ? tÐ+ó    c                  óz   — e Zd ZdZedd„¦   «         Zdd„Zdd„Zdd„Zdd„Z	dd	„Z
dd
„Zdd„Zdd„Zdd„Zdd„ZdS )ÚTestChallengerChatM2z:Adversarial stress harness for M2 Chat Engine & WebApp UI.r   ÚNonec                óÆ   — t                                d¬¦  «        | _        t                               d¬¦  «        | _        t
                               d¬¦  «        | _        d S )Nr   )r   )Ú
PROTO_FILEÚ	read_textÚ
proto_textÚCHAT_MGR_TSÚts_textÚ
INDEX_HTMLÚ	html_text)Úclss    r.   Ú
setUpClasszTestChallengerChatM2.setUpClassf   sN   € å#×-Ò-°wÐ-Ñ?Ô?ˆŒÝ!×+Ò+°WÐ+Ñ=Ô=ˆŒÝ"×,Ò,°gÐ,Ñ>Ô>ˆŒˆˆr0   c           	     óV  — t          j        g d¢dddt          t          ¦  «        dd¬¦  «        }|                      |j        dd|j        › �¦  «         |                      t           	                    ¦   «         d¦  «         g d	¢}|D ]!}|  
                    || j        d
|› �¦  «         Œ"dS )zPVerify TypeScript builds cleanly and ClientItemSnapshot has 100% proto coverage.)Únpmr    ÚbuildTr   é   )r   r   r   r   Úshellr   r   ztsc failed: z'ChatManager.js was not generated by tsc)
zitemUuid: string;zitemName: string;zrarity: number;zelement: number;zquality: number;zitemLevel: number;zaffixes: ClientItemAffix[];zcrafterName: string;zsignature: string;zcreatedAtMs: number;z%Missing field in ClientItemSnapshot: N)r   r    r   Ú
CLIENT_DIRÚassertEqualr"   r$   Ú
assertTruer   ÚexistsÚassertInr9   )ÚselfÚ	build_resÚexpected_fieldsÚfields       r.   Ú5test_01_typescript_compilation_and_proto_field_parityzJTestChallengerChatM2.test_01_typescript_compilation_and_proto_field_parityn   sÕ   € å”NØ#Ð#Ð#Ø d°WÅ#ÅjÁ/Ä/ÐY]Ðgið
ñ 
ô 
ˆ	ð 	×Ò˜Ô-¨qÐ2SÀÔAQÐ2SÐ2SÑTÔTÐTØ�Š�×*Ò*Ñ,Ô,Ð.WÑXÔXÐXð
ð 
ð 
ˆð %ð 	`ð 	`ˆEØ�MŠM˜% ¤Ð/^ÐW\Ð/^Ð/^Ñ_Ô_Ð_Ð_ð	`ð 	`r0   c                ór  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d¦  «         |                      t          |d         ¦  «        d	¦  «         |                      |d         d
         d         d¦  «         dS )zKEmpirically test storing and retrieving full snapshot in ClientChatManager.uP  
        const mgr = new ChatMgr.ClientChatManager();
        const fullSnap = {
            itemUuid: 'uuid-full-777',
            itemName: 'ThÃ¡i Cá»• Tru TiÃªn Kiáº¿m',
            rarity: 5,
            element: 4,
            quality: 20,
            itemLevel: 100,
            affixes: [
                { affixId: 'aff_1', statName: 'SÃ¡t ThÆ°Æ¡ng Cá»• VÃµ', statValue: 120, isImplicit: true },
                { affixId: 'aff_2', statName: 'ChÃ­ Máº¡ng NgÅ© HÃ nh', statValue: 35, isImplicit: false }
            ],
            crafterName: 'VÃ´ Danh Kiáº¿m ThÃ¡nh',
            signature: 'hmac-sha256-abcdef0123456789',
            createdAtMs: 1775000000000
        };
        mgr.registerItemSnapshot(fullSnap);
        const retrieved = mgr.getCachedSnapshot('uuid-full-777');
        console.log(JSON.stringify(retrieved));
        ÚitemUuidzuuid-full-777Ú	itemLeveléd   ÚcreatedAtMsl    }uÚaffixesé   r   Ú
isImplicitTN©r/   rD   Úlen©rH   ÚcodeÚdatas      r.   Ú2test_02_snapshot_storage_and_exact_field_retrievalzGTestChallengerChatM2.test_02_snapshot_storage_and_exact_field_retrieval€   sµ   € ðˆõ* ˜TÑ"Ô"ˆØ×Ò˜˜jÔ)¨?Ñ;Ô;Ð;Ø×Ò˜˜kÔ*¨CÑ0Ô0Ð0Ø×Ò˜˜mÔ,¨mÑ<Ô<Ð<Ø×Ò�˜T )œ_Ñ-Ô-¨qÑ1Ô1Ð1Ø×Ò˜˜iœ¨Ô+¨LÔ9¸4Ñ@Ô@Ð@Ð@Ð@r0   c                ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d¦  «         |                      |d         ¦  «         d	S )
zCEmpirically verify async fallback callback is triggered and cached.uÙ  
        const mgr = new ChatMgr.ClientChatManager();
        let queriedUuid = '', queriedSig = '', openedItem = null;

        mgr.setItemTooltipHandler((item) => { openedItem = item; });
        mgr.setQueryItemSnapshotCallback(async (uuid, sig) => {
            queriedUuid = uuid;
            queriedSig = sig;
            return {
                itemUuid: uuid,
                itemName: 'BÃ­ Cáº£nh TÃ n Cá»‘t',
                rarity: 4,
                element: 3,
                quality: 18,
                itemLevel: 88,
                affixes: [],
                crafterName: 'Dá»‹ Vá»±c Cá»• Ma',
                signature: sig,
                createdAtMs: Date.now()
            };
        });

        await mgr.onTappedItemLink('item-remote-99', 'sig-verified-99');
        const cached = mgr.getCachedSnapshot('item-remote-99');
        console.log(JSON.stringify({ queriedUuid, queriedSig, openedName: openedItem?.itemName, cachedPresent: !!cached }));
        ÚqueriedUuidzitem-remote-99Ú
queriedSigzsig-verified-99Ú
openedNameu   BÃ­ Cáº£nh TÃ n Cá»‘tÚcachedPresentN)r/   rD   rE   rW   s      r.   Ú:test_03_async_query_fallback_when_snapshot_missing_locallyzOTestChallengerChatM2.test_03_async_query_fallback_when_snapshot_missing_locally    s‰   € ðˆõ4 ˜TÑ"Ô"ˆØ×Ò˜˜mÔ,Ð.>Ñ?Ô?Ð?Ø×Ò˜˜lÔ+Ð->Ñ?Ô?Ð?Ø×Ò˜˜lÔ+Ð-DÑEÔEÐEØ�Š˜˜_Ô-Ñ.Ô.Ð.Ð.Ð.r0   c                ób   — d}t          |¦  «        }|                      |d         dd¦  «         dS )zKEmpirically verify fallback rejects mismatching signature or network error.a~  
        const mgr = new ChatMgr.ClientChatManager();
        let openedCount = 0;
        mgr.setItemTooltipHandler(() => { openedCount++; });

        // Sub-case A: Callback returns snapshot with mismatching signature
        mgr.setQueryItemSnapshotCallback(async (uuid, sig) => ({
            itemUuid: uuid, itemName: 'Spoofed Item', rarity: 5, element: 1,
            quality: 20, itemLevel: 100, affixes: [], crafterName: 'Hacker',
            signature: 'wrong-sig', createdAtMs: Date.now()
        }));
        await mgr.onTappedItemLink('item-spoofed', 'correct-sig');

        // Sub-case B: Callback throws network error
        mgr.setQueryItemSnapshotCallback(async () => {
            throw new Error('Connection timeout 504 Gateway Timeout');
        });
        await mgr.onTappedItemLink('item-err', 'sig-err');

        console.log(JSON.stringify({ openedCount }));
        ÚopenedCountr   z;Tooltip must not open on invalid signature or network errorN©r/   rD   rW   s      r.   Ú:test_04_async_query_anti_spoofing_and_exception_resiliencezOTestChallengerChatM2.test_04_async_query_anti_spoofing_and_exception_resilienceÂ   s<   € ðˆõ* ˜TÑ"Ô"ˆØ×Ò˜˜mÔ,¨aÐ1nÑoÔoÐoÐoÐor0   c                óP  — d}t          |¦  «        }|                      t          |d         ¦  «        d¦  «         |                      |d         |d         d¦  «         |                      |d         d         d         d¦  «         |                      |d         d         d	         d
¦  «         |                      |d         d         d         d¦  «         |                      |d         d         d         d¦  «         |                      |d         d         d         d¦  «         dS )zPVerify parseItemTags handles RFC4122 UUIDs, hyphenated HMACs, and complex names.u­  
        const input = 'Váº­t pháº©m 1: [item:550e8400-e29b-41d4-a716-446655440000:hmac-sha256-abc_def-0123:Cá»• Tháº§n Tráº£m Ma Ä�ao +15 (ThÃ¡i Cá»•):5] vÃ  2: [item:c0a80101_item_99:hmac_sig_v3:BÄƒng / Há»�a Song Cá»±c GiÃ¡p:4]';
        const tsParsed = ChatMgr.ClientChatManager.parseItemTags(input);
        const jsParsed = ChatUI.parseItemTags(input);
        console.log(JSON.stringify({ tsParsed, jsParsed }));
        ÚtsParsedrS   ÚjsParsedz*TS and JS parser outputs must be identicalr   rN   z$550e8400-e29b-41d4-a716-446655440000Ú	signaturezhmac-sha256-abc_def-0123ÚitemNameu+   Cá»• Tháº§n Tráº£m Ma Ä�ao +15 (ThÃ¡i Cá»•)Úrarityé   é   u   BÄƒng / Há»�a Song Cá»±c GiÃ¡pNrU   rW   s      r.   Ú:test_05_unusual_uuids_hyphenated_hmac_and_vietnamese_nameszOTestChallengerChatM2.test_05_unusual_uuids_hyphenated_hmac_and_vietnamese_namesÞ   s!  € ðˆõ ˜TÑ"Ô"ˆØ×Ò�˜T *Ô-Ñ.Ô.°Ñ2Ô2Ð2Ø×Ò˜˜jÔ)¨4°
Ô+;Ð=iÑjÔjÐjØ×Ò˜˜jÔ)¨!Ô,¨ZÔ8Ð:`ÑaÔaÐaØ×Ò˜˜jÔ)¨!Ô,¨[Ô9Ð;UÑVÔVÐVØ×Ò˜˜jÔ)¨!Ô,¨ZÔ8Ð:gÑhÔhÐhØ×Ò˜˜jÔ)¨!Ô,¨XÔ6¸Ñ:Ô:Ð:Ø×Ò˜˜jÔ)¨!Ô,¨ZÔ8Ð:ZÑ[Ô[Ð[Ð[Ð[r0   c                óp   — d}t          |¦  «        }|                      t          |¦  «        dd¦  «         dS )zJVerify parseItemTags safely ignores malformed brackets and missing fields.uÛ   
        const malformed = 'Lá»—i [item:only_uuid] vÃ  [item:::name:1] vÃ  [item:uuid:sig:name:notanumber] vÃ  []';
        const res = ChatUI.parseItemTags(malformed);
        console.log(JSON.stringify(res));
        r   z.Malformed tags must safely parse to empty listNrU   rW   s      r.   Ú!test_06_malformed_tags_resiliencez6TestChallengerChatM2.test_06_malformed_tags_resilienceï   s=   € ðˆõ
 ˜TÑ"Ô"ˆØ×Ò�˜T™œ AÐ'WÑXÔXÐXÐXÐXr0   c                óÖ   — d}t          |¦  «        }|                      |d         dd¦  «         |                      |d         dd¦  «         |                      |d         d	d
¦  «         dS )zIVerify 200 consecutive messages in one channel evicts older 100 messages.uT  
        for (let i = 1; i <= 200; i++) {
            ChatUI.addChatMessage({
                messageId: `msg_c1_${i}`,
                senderName: `Hiá»‡pKhÃ¡ch_${i}`,
                channel: 1,
                displayContent: `Tin nháº¯n thá»© ${i}`,
                linkedItems: [],
                timestampMs: Date.now() + i
            });
        }
        const hist = ChatUI.getChannelHistory(1);
        console.log(JSON.stringify({
            length: hist.length,
            firstMsgId: hist[0].messageId,
            lastMsgId: hist[hist.length - 1].messageId
        }));
        ÚlengthrP   z+Channel history ring buffer must cap at 100Ú
firstMsgIdÚ
msg_c1_101zMessages 1-100 must be evictedÚ	lastMsgIdÚ
msg_c1_200zMessage 200 must be the latestNrc   rW   s      r.   Ú1test_07_200_consecutive_messages_fifo_ring_bufferzFTestChallengerChatM2.test_07_200_consecutive_messages_fifo_ring_bufferû   sw   € ðˆõ$ ˜TÑ"Ô"ˆØ×Ò˜˜hœ¨Ð.[Ñ\Ô\Ð\Ø×Ò˜˜lÔ+¨\Ð;[Ñ\Ô\Ð\Ø×Ò˜˜kÔ*¨LÐ:ZÑ[Ô[Ð[Ð[Ð[r0   c                ó¶   — d}t          |¦  «        }|                      t          |d         ¦  «        dd¦  «         |                      |d         dd¦  «         dS )	zLVerify adding 200 consecutive messages across 8 channels renders within SLA.u®  
        const start = Date.now();
        for (let i = 1; i <= 200; i++) {
            const ch = ((i - 1) % 8) + 1;
            ChatUI.addChatMessage({
                messageId: `msg_seq_${i}`,
                senderName: `Hiá»‡pKhÃ¡ch_${i}`,
                channel: ch,
                displayContent: `ThÃ´ng Ä‘iá»‡p ngÅ© hÃ nh liÃªn kÃªnh ${i}`,
                linkedItems: [],
                timestampMs: Date.now() + i
            });
        }
        const durationMs = Date.now() - start;
        const totalStored = Array.from({ length: 8 }, (_, idx) => ChatUI.getChannelHistory(idx + 1).length);
        console.log(JSON.stringify({ durationMs, totalStored }));
        ÚtotalStoredéÈ   z/Total messages across 8 channels must equal 200Ú
durationMsi^  z5200 messages across 8 channels must render in < 350msN)r/   rD   ÚsumÚ
assertLessrW   s      r.   ÚAtest_08_200_consecutive_messages_across_8_channels_responsivenesszVTestChallengerChatM2.test_08_200_consecutive_messages_across_8_channels_responsiveness  s`   € ðˆõ" ˜TÑ"Ô"ˆØ×Ò�˜T -Ô0Ñ1Ô1°3Ð8iÑjÔjÐjØ�Š˜˜\Ô*¨CÐ1hÑiÔiÐiÐiÐir0   c                ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d¦  «         |                      |d         d	¦  «         d
S )z;Empirically test auto-scroll lock toggle at 35px threshold.a[  
        const container = { scrollHeight: 1000, clientHeight: 200, scrollTop: 800 };
        ChatUI.updateScrollLockState(container);
        const lockedAtBottom = ChatUI.isAutoScrollLocked();

        container.scrollTop = 766; // diff = 1000 - 766 - 200 = 34 <= 35
        ChatUI.updateScrollLockState(container);
        const lockedWithinThreshold = ChatUI.isAutoScrollLocked();

        container.scrollTop = 764; // diff = 1000 - 764 - 200 = 36 > 35
        ChatUI.updateScrollLockState(container);
        const lockedBeyondThreshold = ChatUI.isAutoScrollLocked();

        container.scrollTop = 800; // back to bottom
        ChatUI.updateScrollLockState(container);
        const lockedReset = ChatUI.isAutoScrollLocked();

        console.log(JSON.stringify({ lockedAtBottom, lockedWithinThreshold, lockedBeyondThreshold, lockedReset }));
        ÚlockedAtBottomzAt bottom must not be lockedÚlockedWithinThresholdz(Within 35px threshold must not be lockedÚlockedBeyondThresholdz$Beyond 35px threshold must be lockedÚlockedResetzReturning to bottom must unlockN)r/   ÚassertFalserE   rW   s      r.   Ú*test_09_auto_scroll_lock_state_transitionsz?TestChallengerChatM2.test_09_auto_scroll_lock_state_transitions-  s�   € ðˆõ& ˜TÑ"Ô"ˆØ×Ò˜Ð.Ô/Ð1OÑPÔPÐPØ×Ò˜Ð5Ô6Ð8bÑcÔcÐcØ�Š˜Ð4Ô5Ð7]Ñ^Ô^Ð^Ø×Ò˜˜mÔ,Ð.OÑPÔPÐPÐPÐPr0   c                óà  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         t          j        d| j        ¦  «        }|D ]}t          j        dd|¦  «                             ¦   «         }|rS|                      t          |                     ¦   «         ¦  «        dd|› �¦  «         |  	                    d	|d
|› �¦  «         Œ€dS )zEVerify viewport ergonomics in index.html and strict 1-2 word brevity.zviewport-fit=coverzmaximum-scale=1.0zuser-scalable=noz1<button\b[^>]*id="chat-[^"]*"[^>]*>(.*?)</button>z<[^>]+>Ú rS   zButton label exceeds 2 words: z (z#Button label has bilingual parens: N)
rG   r;   ÚreÚfindallÚsubr&   ÚassertLessEqualrV   ÚsplitÚassertNotIn)rH   Ú	chat_btnsÚbtnÚcleans       r.   Ú0test_10_mobile_viewport_and_microcopy_compliancezETestChallengerChatM2.test_10_mobile_viewport_and_microcopy_complianceJ  s÷   € à�ŠÐ*¨D¬NÑ;Ô;Ð;Ø�ŠÐ)¨4¬>Ñ:Ô:Ð:Ø�ŠÐ(¨$¬.Ñ9Ô9Ð9å”JÐSÐUYÔUcÑdÔdˆ	Øð 	]ð 	]ˆCÝ”F˜: r¨3Ñ/Ô/×5Ò5Ñ7Ô7ˆEØð ]Ø×$Ò$¥S¨¯ª©¬Ñ%7Ô%7¸Ð<dÐ]bÐ<dÐ<dÑeÔeÐeØ× Ò   uÐ.[ÐTYÐ.[Ð.[Ñ\Ô\Ð\øð		]ð 	]r0   N)r   r3   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úclassmethodr=   rL   rZ   r`   rd   rm   ro   rv   r}   r„   r�   © r0   r.   r2   r2   c   s(  € € € € € ØDÐDàð?ð ?ð ?ñ „[ð?ð`ð `ð `ð `ð$Að Að Að Að@ /ð  /ð  /ð  /ðDpð pð pð pð8\ð \ð \ð \ð"Yð Yð Yð Yð\ð \ð \ð \ð2jð jð jð jð2Qð Qð Qð Qð:]ð ]ð ]ð ]ð ]ð ]r0   r2   Ú__main__)r   r   r   r   )r”   Ú
__future__r   r'   r‡   r   ÚunittestÚpathlibr   Útypingr   Ú__file__ÚresolveÚparentr!   rC   Ú
WEBAPP_DIRr8   r   r   r:   r5   r/   ÚTestCaser2   r‘   Úmainr–   r0   r.   ú<module>r¢      sx  ððð ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜HÑ$€
Ø˜(Ñ"€
Ø˜5Ñ  6Ñ)Ð,<Ñ<€Ø˜6Ñ! FÑ*Ð-=Ñ=€Ø˜$Ñ Ñ%¨Ñ4€
Ø˜,Ñ&€
Ø˜GÑ# lÑ2€
ðC,ð C,ð C,ð C,ðLr]ð r]ð r]ð r]ð r]˜8Ô,ñ r]ô r]ð r]ðj ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr0   