Přinášíme ksplang,
nejlepší programovací jazyk
Založeno na metodě pánů Pejska a Kočičky
O jazyku
Jazyk byl kolektivně vytvořen řešiteli KSP v úloze 36-1-5, kde měli možnost navrhnout nezávisle na sobě jednotlivé instrukce.
Program v ksplangu se skládá z textových instrukcí oddělených whitespacem (mezery, nové řádky, …). Na velikosti znaků v zápisech instrukcí nezáleží. Jazyk ksplang je zásobníkovým jazykem. To znamená, že jedinou paměť, kterou má program k dispozici, je zásobník, který si můžete představit jako posloupnost čísel uspořádanou shora dolů. Nová čísla přidáváme na vrchol zásobníku, odebíráme opět z vrcholu.
Jazyk ksplang počítá s 64‑bitovými celými čísly se znaménkem reprezentovaných ve dvojkovém doplňku (tedy rozsah od -263 do 263-1). Přetečení čísla při výpočtu způsobí ukončení programu chybou. Zásobník má omezený počet prvků, jeho překročení taktéž ukončí program chybou.
V tomto popisu jazyka zásobník zapisujeme zleva doprava (tj. nejlevější prvek je na dně zásobníku), popřípadě mluvíme o horních či spodních prvcích zásobníku. Odebírá se tedy vždy seshora, popřípadě zprava.
Rychlý přehled instrukcí
Manipulace se zásobníkem
ID | Zápis | Popis | |
---|---|---|---|
0 | praise | N-krát přidá "Mám rád KSP" na zásobník. | více |
1 | pop | Odebere horní hodnotu. | více |
2 | pop2 | Odebere druhou horní hodnotu. | více |
3 | max | Ponechá větší z dvou horních čísel na zásobníku. | více |
4 | L-swap | Vymění čísla na koncích zásobníku. | více |
5 | lroll | Odroluje prvních N hodnot na stacku o K pozic doprava. | více |
6 | -ff | Pokud je na vrcholu stacku 2, 4, tak nic, jinak vyprázdní stack a naplní ho celý -2^63. | více |
7 | swap | Odeber hodnotu, prohoď novou top hodnotu s hodnotou na odebraném indexu. | více |
8 | kPi | Hodnoty z desetinného rozvoje pi. | více |
Aritmetické
ID | Zápis | Popis | |
---|---|---|---|
9 | ++ | Přičte k číslu na vrchu zásobníku 1. | více |
10 | u | Univerzální; přečte typ operace, a pak udělá součet/absolutní rozdíl/součin/vydělí či vymodulí/faktoriál/sign. | více |
11 | REM | Truncated division modulo (jako a % b v C). |
více |
12 | % | Euklidovské modulo (jako a % abs(b) v Pythonu). |
více |
13 | tetr | Tetrace. | více |
14 | ^^ | Tetrace (opačné pořadí parametrů oproti tetr). | více |
15 | m | Medián z horních k čísel, k je číslo na vrcholu zásobníku. | více |
16 | CS | Ciferný součet. | více |
17 | lensum | Suma délek dvou čísel na zásobníku. | více |
18 | bitshift | Bitový posun doleva. | více |
19 | And | Bitový AND dvou čísel. | více |
20 | sum | Součet celého stacku. | více |
21 | gcd | gcd dvou čísel. | více |
22 | d | gcd n čísel. | více |
23 | qeq | Najde celočíselná řešení kvadratické rovnice. | více |
24 | funkcia | Provede prvočíselný rozklad dvou čísel a sloučí je až na společná prvočísla, modulo 1000000007. | více |
25 | bulkxor | Přečte N a poté na N dvojic interpretovaných jako x > 0 aplikuje xor a vyrobí N nul/jedniček | více |
Control flow
ID | Zápis | Popis | |
---|---|---|---|
26 | BRZ | Skok pokud je na vrcholu zásobníku nula. | více |
27 | call | Přidá IP+1 na zásobník + skok. | více |
28 | GOTO | Nepodmíněný skok. | více |
29 | j | Relativní skok. | více |
30 | rev | Skočí dopředu, otočí zásobník a vykoná blok kódu pozpátku. | více |
Další
ID | Zápis | Popis | |
---|---|---|---|
31 | SPANEK | Ohodnotí úlohu dvojnásobkem bodů a jde spát… | více |
32 | deez | Načte n hodnot, přeloží je na instrukce a vyhodnotí podprogram, poté přidá výsledný zásobník na konec programu. | více |
Detailní popis instrukcí
praise
- Mám rád KSP
Nahradí číslo n
na vrchu zásobníku n
-krát po sobě jdoucí
hláškou Unicode znaky „Mám rád KSP“.
Pro záporné n
skončí chybou.
Příklad:
1 -> 77 225 109 32 114 225 100 32 75 83 80
pop
- odebrání
Odebere vrchní hodnotu ze zásobníku.
Příklad:
1 2 3 -> 1 2
pop2
- odebrání druhého čísla
Odebere druhou hodnotu odshora zásobníku.
Příklad:
1 2 3 4 -> 1 2 4
L-swap
- prohození začátku a
konce
Prohodí číslo na začátku a na konci zásobníku.
Pokud je zásobník prázdný, nic se nestane.
Příklad:
1 2 3 4 -> 4 2 3 1
lroll
- rolování pole
Odebere ze zásobníku 2 čísla - n
a x
.
Následně vezme dalších n
prvků, posune je o x
směrem k vrcholu zásobníku a prvních x
prvků je posunuto na
pozice n
.. n - x
.
Pokud na zásobníku není alespoň n
prvků, program skončí
chybou. Pro záporná n
program skončí chybou. Pro
n = 0
nebo x = 0
nedojde k žádnému posunu.
Pro záporná x
operace rotuje prvky druhým směrem. Pro
x
větší než n
či menší než -n
dojde k rotaci několikrát (tedy dojde k rotaci o
x mod n
).
Příklad:
pro 1 2 3 4 1 4 provede rolování 4 prvků o 1, výsledek je tedy 4 1 2 3
pro 1 2 3 4 -1 4 provede rolování 4 prvků o 1 na druhou stranu, výsledek je tedy 2 3 4 1
pro 0 1 2 3 4 2 4 provede rolování 4 prvků o 2, výsledek je tedy 0 3 4 1 2
-ff
Pokud na vrchu zásobníku nejsou čísla 2 a pak 4, tak vyprázdní celý zásobník a kompletně jej naplní hodnotami − 263 (nejmenší int64)
Vyžaduje, aby na zásobníku byly alespoň dvě hodnoty, jinak program skončí chybou.
swap
- adresovaný swap
Odebere z vrchu zásobníku hodnotu i
a další hodnotu
prohodí s hodnotou na indexu i
. Indexuje od nuly od začátku
zásobníku.
Pro záporná i
skončí chybou. Pro příliš velká
i
(odkazující mimo zásobník) skončí chybou.
Příklad:
1 2 3 4 5 6 7 8 3 -> 1 2 3 8 5 6 7 4
kPi
Prohledá zásobník odshora a první hodnota k
, která
zároveň leží na indexu k
je nahrazena k
-tým
číslem z desetinného rozvoje π.
Zásobník indexujeme od začátku od nuly, π indexujeme také od
nuly, nulté číslo je 3, první 1, druhé 4, atd. Pokud na zásobníku žádné
odpovídající k
nenajde, nahradí za číslice π celý
zásobník.
Příklad:
1 2 3 4 5 -> 3 1 4 1 5
2 2 2 2 2 -> 2 2 4 2 2
0 1 2 3 4 -> 0 1 2 3 5
++
- inkrementace
Nahradí číslo navrchu zásobníku o jedna větším číslem.
Pokud na zásobníku není alespoň 1 číslo, skončí chybou.
Příklad:
3 -> 4
max
Z vrchních dvou čísel ponechá na zásobníku jen to větší.
Pokud na zásobníku nejsou alespoň 2 čísla, skončí chybou.
Příklad:
4 2 -> 4
u
- Univerzální
aritmetická instrukce
Odebere ze zásobníku identifikátor instrukce. Pokud je
- 0: nahradí další dvě čísla jejich součtem
- 1: nahradí další dvě čísla absolutní velikostí jejich rozdílu
- 2: nahradí další dvě čísla jejich součinem
- 3: vydělí další dvě čísla – to, které bylo v zásobníku víc nahoře,
tím, které bylo víc dole
- pokud je podíl celočíselný, nahradí je podílem
- pokud není, nahradí je zbytkem po dělení (jako
%
v jazyce C)
- 4: načte další číslo v zásobníku a nahradí ho faktoriálem jeho absolutní hodnoty.
- 5: načte další číslo v zásobníku a nahradí ho
- -1, pokud bylo záporné
- 0, pokud bylo 0
- 1, pokud bylo kladné.
- Jinak končí program s chybou
REM
- truncated division
modulo
Nahradí vrchní 2 čísla zbytkem po dělení prvního čísla druhým číslem.
Pro (nejen) záporná čísla se chová jako Céčkový remainder operator
%
– zachová záporné znaménko ale jinak se chová stejně jako
pro kladná čísla.
Příklad:
3 1 -> 1
-3 1 -> 1
3 -1 -> -1
-3 -1 -> -1
%
- Euklidovské modulo
Nahradí vrchní 2 čísla zbytkem po dělení prvního čísla druhým číslem.
Pro záporná čísla se chová jako Pythoní a % abs(b)
–
výstup je vždy kladné číslo.
Poznámka: Doporučujeme prozkoumat článek Modulo na anglické wikipedii o různých definicích pro záporná čísla a také tabulku ve stejném článku o tom, které jazyky používají kterou definici.
Příklad:
3 1 -> 1
-3 1 -> 1
3 -1 -> 2
-3 -1 -> 2
tetr
- tetrace
Nahradí vrchní 2 čísla jejich tetrací. První číslo je umocňované číslo, druhé je počet opakování.
Pro počet opakování 0 je výsledkem číslo 1. Pro záporný počet opakování program skončí chybou.
^^
- tetrace
Nahradí vrchní 2 čísla jejich tetrací. První číslo je počet opakování, druhé je umocňované číslo.
Pro počet opakování 0 je výsledkem číslo 1. Pro záporný počet opakování program skončí chybou.
m
- medián
Přidá na zásobník medián z vrchních x
čísel, kde
x
je poslední číslo na zásobníku (medián se samozřejmě
počítá i z čísla x
). Pokud je x
sudé, tak je
mediánem průměr z dvou prostředních čísel, zaokrouhlený na celé číslo
směrem k nule.
Pokud x
není kladné, program skončí chybou.
CS
- ciferný součet
Přidá na zásobník ciferný součet vrchního čísla (v desítkové soustavě).
Příklad:
18 -> 18 9
lensum
Nahradí vrchní 2 čísla součtem jejich délek.
Délka je definovaná jako počet celočíselných dělení 10 se zaokrouhlením k nule, které je potřeba udělat, než se dojde k 0. Ekvivalentně jde o počet znaků při zapsání absolutní hodnoty čísla jako textový řetězec v desítkové soustavě, s výjimkou 0, pro kterou je délka 0.
Příklad:
0 0 -> 0
3 2 -> 2
-3 2 -> 2
-22 22 -> 4
bitshift
- bitový posun doleva
Nahradí vrchní 2 čísla bitovým posunem druhého čísla doleva o počet bitů rovným prvnímu číslu. Upozorňujeme, že záporná čísla jsou reprezentována metodou two’s complement. Pokud tedy posunete jedničku do prvního (nejlevějšího) bitu, dostanete záporné číslo.
Přetečení v této instrukci neukončí program, bity prostě zmizí. Pokud je první číslo (počet bitů) záporné, program skončí chybou.
Příklad:
2 1 -> 4
3 1 -> 6
And
- bitový AND dvou čísel
Nahradí vrchní 2 čísla výsledkem bitové operace AND mezi těmito dvěma čísly. Upozorňujeme, že záporná čísla jsou reprezentována metodou two’s complement.
Příklad:
5 3 -> 1
sum
- suma zásobníku
Nahradí celý zásobík součtem všech hodnot.
Výsledná suma se jako vždy musí vejít do 64-bitového signed integeru. Mezivýsledky se vejít nemusí.
„Každý správný dort je potřeba pořádně promíchat.“
gcd
- největší společný
dělitel 2 čísel
Nahradí vrchní 2 čísla jejich největším společným dělitelem.
Výsledek je vždy kladný. Pokud se výsledek nevejde do znaménkového
64-bitového integeru, program skončí chybou (jmenovitě
gcd(0, -2^63) = 2^63
, což je o jedna větší než maximální
hodnota).
d
- největší společný
dělitel n čísel
Odebere číslo n
ze zásobníku. Pak nahradí dalších
n
čísel jejich největším dělitelem.
Pokud n
není kladné, program skončí chybou.
Výsledek je vždy kladný. Pokud se výsledek nevejde do znaménkového
64-bitového integeru, program skončí chybou (jmenovitě
gcd(0, -2^63) = 2^63
).
qeq
- řešení kvadratické
rovnice
Instrukce řeší kvadratickou rovnici ax2 + bx + c = 0
ze zadaných parametrů a
, b
, c
pro
celočíselné x
. Tři koeficienty jsou ze zásobníku odstraněny
a jsou přidány všechna celočíselná řešení (seřazené od nejmenšího).
Pokud rovnice celočíselné řešení nemá, na zásobník se nic nepřidá; pokud
má jeden kořen přidá se jedno číslo, a pokud má dva celočíselné kořeny
tak se přidají dvě čísla.
funkcia
Odoberie 2 čísla zo zásobníka a obe rozloží na prvočísla. Následne z rozkladov zmaže všetky prvočísla, ktoré delia obe dve čísla. Výsledkom je súčin všetkých ostatných prvočísel vrátane exponentov, modulo 1 000 000 007. Pokiaľ je množina prvočísel prázdna, výsledkom je nula.
Napríklad pre čísla 100 = 2^2 * 5^2 a 54 = 3^3 * 2 sa zmaže 2, pretože je v rozkladoch oboch čísel. Zostane nám 5^2 * 3^3, teda 675. Modulo miliarda sedem pre takto malé číslo nič nezmení.
bulkxor
- hromadný XOR
Instrukce odebere z vrcholu zásobníku číslo n
.
Poté odebere 2n
čísel, každé odebrané číslo interpretuje jako 1
pokud je větší než 0,
nebo 0
, pokud je nula či menší. Poté provede na každou dvojici čísel operaci XOR a výsledky přidá na zásobník.
Příklad:
1 -1 3 3 2 -> 1 0
n
je 2, první dvojice 3, 3
se interpretuje jako 1 xor 1 = 0. Druhá dvojice 1, -1
se interpretuje jako 1 xor 0 = 1.
BRZ
- branch if zero
Přečte (nesmaže) ze zásobníku číslo c
. Pokud je
c
rovno nula, přečte (opět nesmaže) ještě druhé číslo
i
a skočí na instrukci i
, v opačném případě
pokračuje další instrukcí. Instrukce jsou indexované od nuly.
Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.
Pokud program běží pozadu, nic se nemění.
Příklad:
Se zásobníkem 0, 0
program brz
vyrobí
nekonečný cyklus.
Se zásobníkem 0, 1
program brz
skončí normálně a nijak nezmění zásobník.
call
- uložení IP + skok
Přečte ze zásobníku i
a přidá na zásobník index
následující instrukce (IP + 1). Následně skočí na i
.
Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.
Pokud program běží pozadu, ukládá se IP - 1.
GOTO
- absolutní skok
Přečte hodnotu i
ze zásobníku a skočí na instrukci
i
.
Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.
Pokud program běží pozadu, nic se nemění.
j
- relativní skok
Přečte číslo z vrchu zásobníku a skočí o specifikovaný počet instrukcí po směru vykonávání programu. Pokud je číslo záporné skáče proti směru vykonávání programu. Nula je následující instrukce, -1 je zpět na tuto instrukci, 1 jednu instrukci přeskočí, atd.
Všechny control flow instrukce končí s chybou, pokud je adresa skoku mimo rozsah programu. Minimum je 0, maximum je délka programu - 1.
rev
Nejdříve spočítá vzdálenost relativního skoku n
(viz další odstavec).
Poté skočí o n
instrukcí dále, otočí celý zásobník a začne vykonávat program v opačném směru.
Jakmile dorazí opět k instrukci rev
, ze které skočil, vrátí se do původního směru a pokračuje
vykonáváním instrukce, která je za tou, na kterou jsme původně skočili (tedy n+1
-té instrukce po rev
).
Narozdíl od instrukce j odpovídá této instrukci rev
hodnota 0, instrukci následující za rev
em hodnota 1, a tak dále.
Návratová instrukce n+1
musí v čase vykonání této instrukce existovat,
a to i když by se program měl ukončit před návratem do rev
u.
Pro výpočet n
se využívají dvě nebo tři hodnoty ze zásobníku, které jsou odebrány.
První odebraná hodnota je a
, druhá odebraná hodnota je b
,
a pokud se a
nerovná nule, tak se ještě odebere třetí hodnota c
.
Tyto dvě či tři využité hodnoty nesmí být záporné, jinak dojde k ukončení programu chybou.
Pokud se a
nerovná nule, tak n
je největším z celočíselných nezáporných výsledků kvadratické rovnice ax2 + bx + c = 0.
Pokud žádný takový výsledek neexistuje, b
se stává hodnotou n
.
Pokud se a
rovná nule (a tedy by nešlo o kvadratickou rovnici), b
se stává hodnotou n
.
Instrukce rev
je možné vnořovat. Druhá instrukce rev
tedy zase povede k běhu dopředu, a tak dále.
K ukončení bloku dojde pouze při navštívení poslední neukončené rev
instrukce (jako na zásobníku).
Bloky rev
je tedy nutno ukončovat ve stejném pořadí jako byly zahájeny, jinak se vykonávaná
instrukce rev
chová jako normální instrukce a začne nový otočený blok.
Je povoleno ukončit program i bez ukončení všech bloků.
Pokud program běží pozadu, standardní instrukce místo přičtení 1 k IP od něj 1 odečítají. Relativní skoky taktéž probíhají v opačném směru. Instrukce call ukládá IP - 1. Pokud program takto dojde na instrukci -1, normálně skončí.
Všechny control flow instrukce končí s chybou, pokud je adresa skoku
mimo rozsah programu. Minimum je 0, maximum je délka programu - 1. V tomto případě je navíc potřeba,
aby návratová instrukce existovala už při vykonání rev
.
Příklad:
Na zásobníku je 1, 2, 3, 4, 2, 0
. Program je rev ++ pop pop
.
Vypočítaná vzdálenost skoku n
je 2
, tedy skočíme na první instrukci pop
.
Zásobník se otočí na 4, 3, 2, 1
a program se začne vykonávat v opačném směru,
proběhne tedy pop a poté increment, a zásobník je 4, 3, 3
.
Další instrukce je (poslední aktivní) rev
, tedy dojde opět k otočení zásobníku
na 3, 3, 4
a skoku na návratovou instrukci pop
, od které už se program
vykonává zase směrem dopředu.
Výsledný zásobník tedy bude 3, 3
.
SPANEK
Ohodnotí tuto úlohu dvojnásobkem možných bodů a pak se na neurčitou dobu uspí.
Upozorňujeme, že skončí-li program chybou či nedoběhne včas, tak nedojde k přidělení žádných bodů.
deez
Odebere ze zásobníku číslo n
. Poté odebere n
prvků,
přeloží je pomocí ID v tabulce rychlého přehledu instrukcí na samostatný program (v pořadí odshora zásobníku).
Tento podprogram je poté vyhodnocen s prázdným zásobníkem, a po jeho skončení se výsledné hodnoty na zásobníku přeloží na instrukce, které jsou přidány na konec původního programu.
Pokud je nějaká hodnota mimo rozsah ID instrukcí, program skončí chybou.