add tests for exact match of grabbed sol/assignment
This commit is contained in:
parent
460d670e75
commit
4093f6b63e
7 changed files with 298 additions and 5 deletions
36
frontend/src/tests/example_assignments/28-Z1-1.html
Normal file
36
frontend/src/tests/example_assignments/28-Z1-1.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
<p>
|
||||
Kevin otevřel obálku (kterou našel ve schránce) a vyndal z ní všechny papíry. Jeden
|
||||
ho obzvláště zaujal (ten od KSP). Ihned si všiml, že (na začátku) obsahuje nějak
|
||||
mnoho závorek. Tak by ho zajímalo, jestli tam (autoři zadání) neudělali nějakou
|
||||
chybu. Pomůžete mu (s ověřením)?</p>
|
||||
<p>
|
||||
Pro danou posloupnost symbolů <tt>(</tt> a <tt>)</tt>, tedy otevíracích
|
||||
a zavíracích závorek, najděte od začátku co nejdelší úsek, který je platným
|
||||
uzávorkováním – tedy každá závorka má svoji do páru.</p>
|
||||
<p>
|
||||
<i>Tvar vstupu:</i>
|
||||
Na prvním řádku dostanete číslo <span class="math">N</span>. Na dalších <span class="math">N</span> řádcích budou jednotlivé
|
||||
testovací případy.</p>
|
||||
<p>
|
||||
<i>Tvar výstupu:</i>
|
||||
Pro každý testovací případ vypište na řádek délku nalezeného uzávorkování.</p>
|
||||
<p>
|
||||
Slibujeme, že <span class="math">N</span> bude nejvýše <span class="math">1 000</span>, a každý řádek bude mít nejvýše <span class="math">10<sup>5</sup></span> symbolů.</p>
|
||||
<div class="leftfloat" style="width: 48%;">
|
||||
<i>Ukázkový vstup:</i>
|
||||
<pre>3
|
||||
(()())())()
|
||||
((()())())()
|
||||
(()()))()()()()()
|
||||
</pre>
|
||||
</div>
|
||||
<div class="rightfloat" style="width: 48%;">
|
||||
<i>Ukázkový výstup:</i>
|
||||
<pre>8
|
||||
12
|
||||
6
|
||||
</pre>
|
||||
</div>
|
||||
<div class="clearfloat"></div>
|
||||
<p>
|
||||
Uzavírající závorka na deváté pozici nemá svůj protějšek. Druhý řádek je celý platné uzávorkování.</p>
|
51
frontend/src/tests/example_assignments/31-3-3.html
Normal file
51
frontend/src/tests/example_assignments/31-3-3.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
<p>
|
||||
Na podlaze se nachází sesypaný hrách s popelem a mezi tím poskakuje několik
|
||||
holoubků a vrabčáků. Chtějí Popelce pomoct vysbírat všechen hrách. Pro každou
|
||||
kuličku hrachu chceme zjistit, zda ji nějaký ptáček dokáže sezobnout a přenést
|
||||
do ošatky. Holoubci i vrabčáci se však pohybují každý jinak. Vrabčáci poskakují
|
||||
rovně dopředu, dozadu, doleva i doprava, zato holoubci chodí šikmo do všech
|
||||
čtyř stran.</p>
|
||||
<p>
|
||||
Celou situaci si lze představit jako rozmístění figurek na šachovnici.
|
||||
Holoubci představují černé střelce, vrabčáci černé věže a kuličky hrachu pak
|
||||
bílé pěšáky. Pro každou bílou figurku chceme zjistit, zda ji dokážeme v jednom
|
||||
tahu nějakou černou figurkou vyhodit.</p>
|
||||
<p>
|
||||
<i>Formát vstupu:</i> Na prvním řádku vstupu se nachází dvě čísla <span class="math">B</span> a <span class="math">C</span>, a to
|
||||
počet kuliček hrachu (neboli bílých figurek) a počet ptáčků (neboli černých
|
||||
figurek). Na dalších <span class="math">B</span> řádcích se nachází souřadnice kuliček hrachu jako
|
||||
dvojice čísel oddělených mezerou, číslo řádku a číslo sloupce udávajících, kde
|
||||
se kulička nachází. Na dalších <span class="math">C</span> řádcích se pak nachází pozice ptáčků – každý
|
||||
takový řádek obsahuje znak <tt>H</tt> nebo <tt>V</tt> a dvojici čísel udávajících
|
||||
řádek a sloupec, kde ptáček stojí (opět vše oddělené mezerami). Znak <tt>H</tt>
|
||||
značí holoubka (pohybuje se jako střelec) a znak <tt>V</tt> vrabčáka (pohybuje se
|
||||
jako věž). Figurky na vstupu mohou být seřazené náhodně.</p>
|
||||
<p>
|
||||
<i>Formát výstupu:</i> Na <span class="math">B</span> řádků výstupu vypište (ve stejném pořadí, jako na
|
||||
vstupu) pro každou kuličku hrachu <tt>ANO</tt> nebo <tt>NE</tt> podle toho, jestli
|
||||
existuje nějaký holoubek nebo vrabčák, který může tuto kuličku hrachu jedním
|
||||
tahem sezobnout.</p>
|
||||
<div class="leftfloat" style="width: 48%;">
|
||||
<i>Ukázkový vstup:</i>
|
||||
<pre>4 3
|
||||
0 1
|
||||
0 2
|
||||
0 3
|
||||
2 2
|
||||
V 0 0
|
||||
H 1 3
|
||||
V 3 3
|
||||
</pre>
|
||||
</div>
|
||||
<div class="rightfloat" style="width: 48%;">
|
||||
<i>Ukázkový výstup:</i>
|
||||
<pre>ANO
|
||||
ANO
|
||||
NE
|
||||
ANO
|
||||
</pre>
|
||||
<img class="rightfloat image" src="http://localhost/h/ulohy/31/t3133.png" alt="Šachovnice s příkladem" title="Šachovnice s příkladem">
|
||||
</div>
|
||||
<div class="clearfloat"></div>
|
||||
<i>Poznámka:</i>
|
||||
BUG
|
28
frontend/src/tests/example_assignments/32-Z1-1.html
Normal file
28
frontend/src/tests/example_assignments/32-Z1-1.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<p>
|
||||
Kevin se po prázdninách zase vrací do školy. Jako obvykle dostal seznam pomůcek,
|
||||
které si má obstarat. Malé sešity, velké sešity, pravítko, pastelky, barvy na
|
||||
výtvarku, učebnice na češtinu a dějepis a možná i novou aktovku. To vypadá na
|
||||
velký nákup!</p>
|
||||
<p>
|
||||
Kevin se tedy vydal do obchodu. Jak dává věci do košíku, píše si i seznam cen. V
|
||||
tom si ale uvědomí, že má jen <span class="math">P</span> korun. Kolik nejméně z <span class="math">N</span> věcí v košíku musí
|
||||
Kevin vyhodit, aby mu <span class="math">P</span> korun stačilo?</p>
|
||||
<p>
|
||||
<i>Tvar vstupu:</i> Na prvním řádku se nachází počet věcí v košíku <span class="math">N</span> a počet korun <span class="math">P</span>, které Kevin může
|
||||
utratit. Na druhém řádku je pak seznam cen věcí v košíku, tedy <span class="math">N</span> kladných čísel
|
||||
oddělených mezerami.</p>
|
||||
<p>
|
||||
<i>Tvar výstupu:</i> Vypište jedním číslem nejmenší počet věcí, které musí Kevin
|
||||
z košíku vyhodit.</p>
|
||||
<div class="leftfloat" style="width: 48%;">
|
||||
<i>Ukázkový vstup:</i>
|
||||
<pre>5 59
|
||||
8 20 30 1999 40
|
||||
</pre>
|
||||
</div>
|
||||
<div class="rightfloat" style="width: 48%;">
|
||||
<i>Ukázkový výstup:</i>
|
||||
<pre>2
|
||||
</pre>
|
||||
</div>
|
||||
<div class="clearfloat"></div>
|
65
frontend/src/tests/example_solutions/28-Z1-1.html
Normal file
65
frontend/src/tests/example_solutions/28-Z1-1.html
Normal file
|
@ -0,0 +1,65 @@
|
|||
<p>
|
||||
Nejprve si vyřešme jednodušší úlohu: jak o nějaké posloupnosti závorek
|
||||
poznáme, jestli je (celá) správně uzávorkovaná? Určitě musí obsahovat
|
||||
stejný počet levých a pravých závorek – jinak těžko mohou tvořit páry.</p>
|
||||
<p>
|
||||
Ovšem to nestačí. Uvažte třeba posloupnost <tt>())(</tt>. Ta obsahuje dvě
|
||||
levé a dvě pravé závorky, ale korektním uzávorkováním určitě není.
|
||||
Na třetí pozici se pokoušíme ukončit závorku „dřív, než začala“.</p>
|
||||
<p>
|
||||
Jak takovou situaci poznáme? Podíváme-li se na první tři znaky naší
|
||||
posloupnosti, vidíme, že obsahují jednu levou závorku a dvě pravé.
|
||||
Obecně problém nastane, pokud existuje v posloupnosti nějaké místo
|
||||
takové, že v úseku od začátku po toto místo je více pravých závorek
|
||||
než levých. Pak alespoň jedna z těch pravých nemá před sebou žádnou
|
||||
levou, se kterou bychom ji mohli spárovat.</p>
|
||||
<p>
|
||||
Pokud posloupnost obsahuje stejně levých a pravých závorek a nenastane
|
||||
problém popsaný výše, pak už si snadno rozmyslíte, že je vždy jde
|
||||
správně spárovat, a tedy uzávorkování je korektní.</p>
|
||||
<div class="center"><img class="image" src="http://localhost/img/hippo_table_pilot.png" alt="Ilustrace: Hroší pilot" title="Ilustrace: Hroší pilot"></div>
|
||||
<p>
|
||||
Nyní k původní úloze. Představme si, že si chceme označit všechna
|
||||
místa v posloupnosti, kde může končit korektní uzávorkování (začínající
|
||||
na začátku). Třeba pro příklad ze zadání to budou následující místa
|
||||
(označena hvězdičkou): <tt>(()())*()*)()</tt>.</p>
|
||||
<p>
|
||||
Jak je najdeme? Budeme postupně procházet posloupnost od začátku a průběžně
|
||||
si počítat, kolik levých a pravých závorek už jsme viděli. Pokud narazíme
|
||||
na místo, kde jsme napočítali víc pravých než levých, můžeme rovnou skončit.
|
||||
Už víme, že takové uzávorkování korektní není, a přidáním libovolných dalších
|
||||
závorek na konec už nemůžeme napravit chybějící závorky nalevo od aktuální
|
||||
pozice.</p>
|
||||
<p>
|
||||
Pokud tato situace ještě nenastala a objevíme místo, před kterým je počet
|
||||
levých a pravých závorek stejný, označíme si jej. Podle toho, co jsme si
|
||||
řekli výše, uzávorkování končící na tomto místě je korektní.</p>
|
||||
<p>
|
||||
Ukažme si průběh algoritmu na předchozím příkladu:</p>
|
||||
<div class="center">
|
||||
<table class="centertable borderedtable">
|
||||
<tbody><tr><td></td><td> (</td><td> (</td><td> ) </td><td> (</td><td> ) </td><td> ) </td><td> (</td><td> ) </td><td> ) </td><td> (</td><td> )</td></tr>
|
||||
<tr><td>Pozice </td><td> 1 </td><td> 2 </td><td> 3 </td><td> 4 </td><td> 5 </td><td> 6 </td><td> 7 </td><td> 8 </td><td> 9 </td><td> 10 </td><td> 11</td></tr>
|
||||
<tr><td>Počet levých </td><td> 1 </td><td> 2 </td><td> 2 </td><td> 3 </td><td> 3 </td><td> 3 </td><td> 4 </td><td> 4 </td><td> 4 </td><td> - </td><td> -</td></tr>
|
||||
<tr><td>Počet pravých </td><td> 0 </td><td> 0 </td><td> 1 </td><td> 1 </td><td> 2 </td><td> 3 </td><td> 3 </td><td> 4 </td><td> 5 </td><td> - </td><td> -</td></tr>
|
||||
<tr><td>Rozdíl </td><td> 1 </td><td> 2 </td><td> 1 </td><td> 2 </td><td> 1 </td><td> 0 </td><td> 1 </td><td> 0 </td><td> -1 </td><td> - </td><td> -</td></tr>
|
||||
<tr><td>Akce </td><td> </td><td> </td><td> </td><td> </td><td> </td><td> * </td><td> </td><td> * </td><td> <tt>X</tt> </td><td> </td><td></td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<p>
|
||||
Hvězdička znamená označení místa s korektním uzávorkováním
|
||||
a <tt>X</tt> konec prohledávání, když žádné další uzávorkování korektní být
|
||||
nemůže.</p>
|
||||
<p>
|
||||
Na konci jen vypíšeme nejpravější označené místo. Všimněme si, že
|
||||
si nemusíme ukládat všechna označená místa, stačí si vždy pamatovat
|
||||
poslední dosud nalezené. Další drobné zjednodušení je místo dvou
|
||||
počítadel použít jen jedno, uchovávající rozdíl počtu dosud načtených
|
||||
levých a pravých závorek (řádek „rozdíl“ v tabulce výše). Pak označujeme,
|
||||
když je toto počítadlo rovné nule, a končíme, pokud klesne pod nulu.</p>
|
||||
<p><a href="http://localhost/z/ulohy/28/28Z11-1.py">Program (Python 3) – počítání obou druhů závorek</a></p>
|
||||
<p><a href="http://localhost/z/ulohy/28/28Z11-2.py">Program (Python 3) – stačí nám počítat jen levé</a></p>
|
||||
<p class="author"><a href="http://localhost/kontakty/organizatori/">Filip Štědronský</a></p>
|
||||
<p>
|
||||
<i>Pokud máte pocit, že jste zde dříve viděli něco jiného, nemýlíte se. Věříme, že toto řešení pro vás bude stravitelnější.</i></p>
|
||||
<hr class="clearfloat">
|
50
frontend/src/tests/example_solutions/31-3-3.html
Normal file
50
frontend/src/tests/example_solutions/31-3-3.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<p>
|
||||
Pro každou z bílých figurek chceme zjistit, zda je ohrožována aspoň jednou
|
||||
černou. Můžeme zkusit vyzkoušet pro každou dvojici bílé a černé figurky, zda se
|
||||
ohrožují. Těchto dvojic je ovšem až kvadraticky mnoho a navíc se nám může stát, že
|
||||
i když by se dvojice ohrožovala, tak se mezi nimi nachází další figurka, která
|
||||
černé figurce z naší dvojice překáží ve výhledu a znemožňuje jí bílou figurku
|
||||
sebrat. Protože ověřit, zda dvojici nepřekáží ve výhledu další figurka, trvá
|
||||
lineárně dlouho, toto řešení je <span class="math">O(n<sup>3</sup>)</span>.</p>
|
||||
<p>
|
||||
Jelikož černé figurky jsou pouze věže a střelci, bílé figurky mohou být
|
||||
ohroženy pouze z dohromady osmi směrů, kterými se černé dokážou pohybovat.
|
||||
Řešení můžeme vylepšit tím, že budeme zjišťovat, zda je bílá figurka ohrožena,
|
||||
pro každý směr zvlášť. Například pokud chceme pro každou bílou figurku zjistit,
|
||||
zda je ohrožena zleva nebo zprava, stačí nám uvažovat pouze ty figurky, které
|
||||
leží na stejném řádku. Navíc každá figurka má na svém řádku nejvýše dva
|
||||
sousedy, jednoho zleva, druhého zprava. Stačí nám uvažovat jen tyto
|
||||
sousedy, protože budou všem ostatním figurkám na řádku překážet ve výhledu. Pro
|
||||
každou z lineárně mnoho bílých figurek tedy v lineárním čase najdeme ty
|
||||
figurky, které s nimi sdílí řádek (pro ostatní směry ty, které sdílí sloupec či
|
||||
diagonálu), v lineárním čase najdeme mezi nimi dva nejbližší sousedy bílé
|
||||
figurky a pro ty vyzkoušíme, zda figurku neohrožují, to jest zda se jedná o černé figurky a jestli jsou typu, který se umí v tomto směru pohybovat. Celkově je toto řešení
|
||||
<span class="math">O(n · (n + n))</span>, tedy <span class="math">O(n<sup>2</sup>)</span>.</p>
|
||||
<p>
|
||||
Abychom uměli rychle zjistit, které figurky leží na stejném řádku, můžeme si
|
||||
nejprve všechny figurky seřadit podle jejich souřadnice řádku. V seřazeném seznamu figurek
|
||||
leží figurky na stejném řádku vedle sebe. Pokud bychom navíc vzali
|
||||
všechny figurky na stejném řádku a seřadili je podle souřadnice sloupce a
|
||||
uložili do nějakého pole, budeme je mít uspořádané v tom pořadí, v jakém leží
|
||||
na řádku za sebou. Pro libovolnou figurku na řádku tedy umíme najít její
|
||||
sousedy prostě tak, že se podíváme v tomto uspořádaném poli na index o jedna
|
||||
nižší a o jedna vyšší.</p>
|
||||
<p>
|
||||
Tato dvě setřídění můžeme provést zároveň. Primárně figurky setřídíme podle
|
||||
souřadnice řádku, sekundárně pak podle souřadnice sloupce. Takové uspořádání,
|
||||
kde třídíme primárně podle jednoho kritéria a sekundárně podle jiného, se
|
||||
nazývá <i>lexikografické.</i> Nyní nám stačí projít setříděné pole a pro každou
|
||||
bílou figurku se podívat na její dva sousedy a rozhodnout, zda ji neohrožují.
|
||||
Také musíme dát pozor na to, že sousední figurky v setříděném poli figurek se
|
||||
nemusí nacházet na stejném řádku. Pokud je soused bíle figurky na jiném řádku,
|
||||
znamená to, že v daném směru se už na stejném řádku nenachází žádné figurky.
|
||||
Také si všimněme, že tímto uspořádáním najdeme všechny takové figurky, které
|
||||
bíle ohrožují ne jen z jednoho směru z osmi, ale ze dvou (zprava i zleva).</p>
|
||||
<p>
|
||||
Kompletní řešení pro každou ze čtyř „os“ (doleva-doprava, nahoru-dolů,
|
||||
diagonálně doprava nahoru, diagonálně doprava dolů) lexikograficky figurky
|
||||
uspořádá, uspořádané pole v lineárním čase projde a pro každou bílou figurku určí, zda
|
||||
je ohrožována. Toto řešení má tedy časovou složitost <span class="math">O(n <span class="nomath">log</span> n)</span>.</p>
|
||||
<p><a href="http://localhost/h/ulohy/31/3133.c">Program (C)</a></p>
|
||||
<p class="author"><a href="http://localhost/kontakty/organizatori/">Kuba Pelc</a></p>
|
||||
<hr class="clearfloat">
|
27
frontend/src/tests/example_solutions/32-Z1-1.html
Normal file
27
frontend/src/tests/example_solutions/32-Z1-1.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<p>
|
||||
K tomu, abychom za věci utratili co možná nejméně, potřebujeme do košíku
|
||||
postupně vybírat věci od té nejlevnější po tu nejdražší.</p>
|
||||
<p>
|
||||
Pole cen tedy vzestupně setřídíme a budeme jej postupně procházet, přičemž
|
||||
si budeme pamatovat součet cen věcí, které při průchodu potkáme. Jakmile
|
||||
při procházení přesáhne tento součet koruny vyhrazené na nákup, tak podle
|
||||
pozice v poli víme, kolik věcí si můžeme koupit.</p>
|
||||
<p>
|
||||
Jelikož se však zadání ptá na počet věcí, které si koupit nemůžeme, tak jako
|
||||
řešení vypíšeme počet věcí od pozice, kde jsme skončili s procházením, do konce
|
||||
pole cen.</p>
|
||||
<p>
|
||||
Řešení je jistě správné, určitě se totiž nemůže stát, že bychom si mohli koupit
|
||||
víc věcí, než nám vypíše algoritmus. Vyhodili jsme z košíku ty nejdražší věci,
|
||||
které jsme mohli, takže nám jich určitě nestačilo vyhodit méně.</p>
|
||||
<p>
|
||||
Rychlost řešení ovlivní hlavně to, jak rychle dokážeme ceny setřídit. Existují
|
||||
různé třídící algoritmy a pokud použijete nějaký algoritmus zabudovaný přímo
|
||||
v programovacím jazyce, běží většinou v čase <span class="math">O(n <span class="nomath">log</span> n)</span>. Pokud jste si
|
||||
implementovali vlastní jednoduché třídění, pravděpodobně bude mít časovou
|
||||
složitost <span class="math">O(n<sup>2</sup>)</span>. O třídících algoritmech si můžete přečíst
|
||||
v naší <a href="http://localhost/viz/kucharky/trideni">kuchařce o třídění</a>.</p>
|
||||
<p><a href="http://localhost/z/ulohy/32/32Z11.py">Program (Python 3)</a></p>
|
||||
<p><a href="http://localhost/z/ulohy/32/32Z11.cpp">Program (C++)</a></p>
|
||||
<p class="author"><a href="http://localhost/kontakty/organizatori/">Tom Sláma</a></p>
|
||||
<hr class="clearfloat">
|
|
@ -1,7 +1,8 @@
|
|||
import * as g from '../ksp-task-grabber'
|
||||
import { readFileSync } from 'fs';
|
||||
import { readFileSync, readdirSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import type { TaskDescriptor, TasksFile } from '../tasks';
|
||||
import { action_destroyer } from 'svelte/internal';
|
||||
|
||||
const node_fetch: any = require('node-fetch')
|
||||
|
||||
|
@ -41,9 +42,9 @@ describe('tasks assignment', () => {
|
|||
for (const t of tasks.tasks) {
|
||||
if (t.type != "open-data") continue;
|
||||
|
||||
test(`${t.id}`, async () => {
|
||||
const assignment = await g.grabAssignment((t as any).taskReference)
|
||||
expect((t as any).taskReference).toEqual(assignment.id)
|
||||
test.concurrent(`${t.id}`, async () => {
|
||||
const assignment = await g.grabAssignment(t.taskReference)
|
||||
expect(t.taskReference).toEqual(assignment.id)
|
||||
expect(assignment.points).toBeGreaterThanOrEqual(1)
|
||||
expect(assignment.description.trim()).toBeTruthy()
|
||||
expect(assignment.name.trim()).toBeTruthy()
|
||||
|
@ -66,7 +67,7 @@ describe('tasks solutions', () => {
|
|||
continue
|
||||
}
|
||||
|
||||
test(`${t.id}`, async () => {
|
||||
test.concurrent(`${t.id}`, async () => {
|
||||
const sol = await g.grabSolution(t.taskReference)
|
||||
expect(t.taskReference).toEqual(sol.id)
|
||||
expect(sol.description.trim()).toBeTruthy()
|
||||
|
@ -74,3 +75,38 @@ describe('tasks solutions', () => {
|
|||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('task assignment (exact match)', () => {
|
||||
const assignmentDir = resolve(__dirname, "example_assignments")
|
||||
const assignmentFiles = readdirSync(assignmentDir)
|
||||
for (const a of assignmentFiles) {
|
||||
const [_, id] = /(.*?)\.html/.exec(a)!
|
||||
test(id, async () => {
|
||||
const assignment = await g.grabAssignment(id)
|
||||
const expected = readFileSync(resolve(assignmentDir, a)).toString()
|
||||
try {
|
||||
expect(assignment.description.trim()).toEqual(expected.trim())
|
||||
} catch(e) {
|
||||
console.warn(assignment.description)
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
describe('task solution (exact match)', () => {
|
||||
const assignmentDir = resolve(__dirname, "example_solutions")
|
||||
const assignmentFiles = readdirSync(assignmentDir)
|
||||
for (const a of assignmentFiles) {
|
||||
const [_, id] = /(.*?)\.html/.exec(a)!
|
||||
test(id, async () => {
|
||||
const solution = await g.grabSolution(id)
|
||||
const expected = readFileSync(resolve(assignmentDir, a)).toString()
|
||||
try {
|
||||
expect(solution.description.trim()).toEqual(expected.trim())
|
||||
} catch(e) {
|
||||
console.log(solution.description)
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
Reference in a new issue