Externí balíčky

Antonín Slavík
Matematicko-fyzikální fakulta UK

Usage messages

Ke každé funkci v Mathematice je k dispozici stručná nápověda (usage message):

?Plot

Také uživatelem definované funkce mohou mít svou nápovědu:

smajlik[color_:White]:=Graphics[{{color,EdgeForm[Black],Disk[{5,5},4]},{Disk[{3.5,6.5},0.45],Disk[{6.5,6.5},0.45],
Circle[{5,5.5},3,{225°,315°}],Line[{{5,5.5},{4.5,4.5},{5,4.5}}]}},ImageSize->Tiny]

Výraz vzor:výchozí hodnota znamená nepovinný argument s předepsanou výchozí hodnotou.

smajlik::usage="smajlik[color] zobrazí smajlíka se zadanou barvou obličeje.\nsmajlik[] zobrazí smajlíka s bílým obličejem.";

?smajlik

smajlik[RandomColor[]]

smajlik[]

Clear[smajlik]

Externí balíčky

Soubory obsahující definice dalších funkcí

Textové soubory s příponou .m nebo .wl, lze je vytvářet v libovolném textovém editoru nebo přímo v Mathematice (File, New, Package/Script, Wolfram Language Package)

Některé balíčky jsou součástí distribuce (jsou zde umístěny zejména specializované funkce), další mohou vytvářet sami uživatelé.

Přehled standardních balíčků je v dokumentaci pod heslem Wolfram System Standard Extra Packages.

Seznam dalších balíčků, které jsou na daném počítači nainstalovány, je v dokumentaci pod Add-ons and Packages.

Příklad – použití balíčku Combinatorica:

Needs["Combinatorica`"]

?Combinatorica`*

?Derangements

Derangements[{1,2,3,4}]

Kontexty

Každý identifikátor v Mathematice je definován v některém z tzv. kontextů.

Všechny zabudované funkce a konstanty jsou v kontextu System:

Context[Sqrt]

Uživatelem definované identifikátory jsou (pokud není řečeno jinak) v kontextu Global:

f[z_]:=z^2+z+1

Context[f]

Remove vymaže definici identifikátoru včetně informace o tom, v jakém se nachází kontextu.

Remove[f]

Funkce z balíčku Combinatorica jsou v kontextu Combinatorica:

Context[Derangements]

Kontexty zabraňují konfliktům v případech, kdy jsou v různých balíčcích definovány funkce se stejnými jmény – v takové situaci stačí použít úplné jméno identifikátoru ve tvaru kontext`identifikátor.

Combinatorica`Derangements[{1,2,3,4}]

V nových verzích Mathematiky dochází např. u balíčku Combinatorica ke kolizím s funkcemi v jádře:

System`CompleteGraph[5]

Combinatorica`CompleteGraph[5]

CompleteGraph[5]

Seznam všech kontextů, které se automaticky prohledávají, jestliže nezadáme úplné jméno identifikátoru:

$ContextPath

Seznam všech identifikátorů v nějakém kontextu:

Names["Combinatorica`*"]

Chceme-li definovat funkci v jiném kontextu než Global, použijeme konstrukci BeginPackage ... EndPackage nebo Begin ... End.

BeginPackage["kontext1`"];
p[x_]:=x^2;
EndPackage[]

Konstrukce  BeginPackage ... EndPackage přidává kontext do seznamu $ContextPath. Na identifikátory definované uvnitř tohoto kontextu se tedy stačí odvolávat jen názvem:

$ContextPath

p[5]

Remove[p]

Begin["kontext2`"];
q[x_]:=x^2;
End[];

Konstrukce  Begin ... End nemění seznam $ContextPath. Na identifikátory definované uvnitř daného kontextu se musíme odvolávat pomocí úplné specifikace ve tvaru kontext`identifikátor:

$ContextPath

q[5]

kontext2`q[5]

Remove[q]

Kontexty mohou být do sebe vnořeny:

BeginPackage["kontext3`"];
f[x_]:=x^3;
	Begin["`kontext4`"];
	g[x_]:=x^4;
	End[];
End[];

f[2]

kontext3`kontext4`g[2]

Typická struktura externího balíčku

BeginPackage["balíček`"]

(* nápověda ke všem funkcím, které mají být viditelné uživatelům balíčku *)

f::usage="nápověda k funkci f"

Begin["`Private`"]

(* definice všech funkcí *)

f[args]:= ... (* definice funkce f *)

g[args]:= ... (* definice neveřejné funkce g *)

End[]

EndPackage[]

Příklad: Balíček implementující šifrovací metodu RSA

Metoda RSA

R. Rivest, A. Shamir, L. Adleman (1978)

Bob, který chce přijímat šifrované zprávy, vygeneruje dvě velká prvočísla, p a q; jejich vynásobením dostane číslo n=p·q. Navíc potřebuje číslo e, které je nesoudělné s (p-1)(q-1). Na internetu zveřejní dvojici (n,e) (veřejný klíč).

Alice chce Bobovi poslat zprávu m, kde 0≤m<n. Zašifruje ji tak, že spočte s=(m^e) mod n, a výsledek pošle Bobovi.

Pro dešifrování zpráv používá Bob číslo d, které je inverzním prvkem k e modulo (p-1)(q-1), tj. e·d≡1 mod (p-1)(q-1). Vezme zašifrovanou zprávu s a spočte (s^d) mod n, čímž dostane původní nešifrovanou zprávu.

Nepovolaná osoba, která by chtěla zprávu dešifrovat, musí znát číslo d, a tedy nejprve zjistit čísla p, q. Ta lze najít faktorizací n, což je pro velká n časově náročný problém.

Příklad:

p=39197789734438837913;
q=99404432593207691561;

PrimeQ[{p,q}]

(Velká prvočísla lze generovat např. pomocí RandomPrime).

n=p q; 
e=17;
GCD[e,(p-1)(q-1)]

Šifrování:

?PowerMod

m=2800108741150188838726840567123631987261;
s=PowerMod[m,e,n]

Dešifrování:

d=PowerMod[e,-1,(p-1)(q-1)]

PowerMod[s,d,n]

Konverze textu na čísla a obráceně

Chceme-li pomocí RSA šifrovat text, musíme jej nejprve převést na číslo (nebo posloupnost čísel).

Mathematica kóduje znaky podle standardu Unicode; každý znak má přiřazen celočíselný kód z intervalu 0 až 65535 (=(2^16)-1, tj. 16 bitů na jeden znak).

ToCharacterCode[řetězec] vrací seznam kódů znaků zadaného řetězce.

ToCharacterCode["hello"]

FromCharacterCode[%]

Kódy znaků můžeme chápat jako cifry čísla vyjádřeného v soustavě o základu 65 536.

StringToInteger[message_String]:=FromDigits[ToCharacterCode[message],65536]

Při zpětné konverzi nejprve zapíšeme číslo v soustavě o základu 65536, poté převedeme číselné kódy na znaky.

IntegerToString[codedmessage_]:=FromCharacterCode[IntegerDigits[codedmessage,65536]]

StringToInteger["In April 1977, Rivest, Shamir, and Adleman spent Passover at the house of a student and consumed liberal quantities of Manischewitz wine before returning to their respective homes sometime around midnight. Rivest was unable to sleep, so he lay on his couch with a math textbook. He began to mull over the question that had been nagging him all year: Is it possible to find a one-way function that can be reversed only if the receiver has some special information? Suddenly, the mists began to clear and he had a revelation. He spent the rest of the night formalizing his idea, and by daybreak he had effectively written a complete mathematical paper. Rivest had a breakthrough, but it could not have come without the help of Shamir and Adleman. The system was later dubbed RSA, for Rivest, Shamir, and Adleman."]

IntegerToString[%]

Šifrování a dešifrování

K zašifrování zprávy jsou potřeba čísla e, n. Je-li zpráva dostatečně krátká (převod na číslo dá výsledek menší než n), můžeme ji přímo zašifrovat. V opačném případě zprávu rozdělíme na kratší úseky, každý zašifrujeme zvlášť a vrátíme seznam čísel.

RSAEncode[m_String,e_Integer,n_Integer]:=Module[{maxlength=Floor[Log[65536,n]]-1},
If[StringLength[m]<=maxlength,
PowerMod[StringToInteger[m],e,n],
Map[PowerMod[StringToInteger[#],e,n]&,StringPartition[m,UpTo[maxlength]]]]]

K dešifrování zprávy jsou potřeba čísla d, n. Je-li zpráva tvořena pouze jedním číslem, dešifrujeme je a převedeme na text. Je-li argumentem seznam čísel, pak každé dešifrujeme zvlášť, převedeme na text a získané řetězce spojíme.

RSADecode[m_Integer,d_Integer,n_Integer]:=IntegerToString[PowerMod[m,d,n]]

RSADecode[m_List,d_Integer,n_Integer]:=StringJoin@@Map[RSADecode[#,d,n]&,m]

Zkouška:

RSAEncode["The Magic Words are Squeamish Ossifrage.",e,n]

RSADecode[%,d,n]

Základní verze balíčku

BeginPackage["RSA`"]

RSAEncode::usage="RSAEncode[m, e, n] turns the message-string m into a number and then encodes that number by the RSA method using exponent e and modulus n. If m is too long, then a list of integers is returned.";

RSADecode::usage="RSADecode[s, d, n] decodes the integer (or list) s using the decoding inverse d and the modulus n.";

Begin["`Private`"]

StringToInteger[m_String]:=FromDigits[ToCharacterCode[m],65536]

IntegerToString[m_]:=FromCharacterCode[IntegerDigits[m,65536]]

RSAEncode[m_String,e_Integer,n_Integer]:=Module[{maxlength=Floor[Log[65536,n]]-1},
	If[StringLength[m]<=maxlength, PowerMod[StringToInteger[m],e,n], Map[PowerMod[StringToInteger[#],e,n]&,StringPartition[m,UpTo[maxlength]]]]]

RSADecode[s_Integer,d_Integer,n_Integer]:=IntegerToString[PowerMod[s,d,n]]

RSADecode[s_List,d_Integer,n_Integer]:=StringJoin@@Map[RSADecode[#,d,n]&,s]

End[]

EndPackage[]

Umístění balíčku v adresářové struktuře

Při pokusu o načtení balíčku pomocí Needs prohledává Mathematica všechny adresáře uvedené v $Path. Chceme-li načíst balíček z jiného adresáře, je potřeba se do něj přepnout pomocí SetDirectory[adresář].

$Path

Balíčky se obvykle instalují do $BaseDirectory/Applications (soubory přístupné všem uživatelům) nebo do $UserBaseDirectory/Applications (soubory právě přihlášeného uživatele).

$BaseDirectory

$UserBaseDirectory

Balíčky jsou obvykle tvořeny:
a) Jediným souborem s příponou .m nebo .wl, jehož název je shodný s názvem balíčku. Při pokusu o načtení balíčku se vyhodnotí obsah tohoto souboru.

({{$BaseDirectory/Applications}})  ⟶  ({{RSA.wl}})

b) Adresářem obsahujícím více souborů. Jméno adresáře musí být shodné s názvem balíčku. Adresář musí obsahovat soubor init.m nebo init.wl, jehož obsah je vyhodnocen při pokusu o načtení balíčku. 

({{$BaseDirectory/Applications}})  ⟶  ({{RSA}})  ⟶  ({{init.wl}})

Zkouška:

Quit[]

Needs["RSA`"]

?RSAEncode

?RSADecode

Aby se balíček objevil v Add-ons and Packages, je potřeba doplnit soubor PacletInfo.m (PacletInfo.wl) a dokumentaci, která se otevře po kliknutí na tlačítko Documentation.

Adresářová struktura:

RSA
|   init.wl
|   PacletInfo.wl
|___Documentation
    |   
    |___English
        |___Contents.nb

Obsah PacletInfo.wl:

Paclet[
    Name -> "RSA",
    Version -> "1.0",
    Extensions -> 
        {
            {"Application", 
            Root -> "RSA", 
            Context -> "RSA`"},
            {"Documentation", Language -> "English", MainPage -> "Contents"}
        }
]

V Documentation\English\ReferencePages\Symbols mohou být umístěny notebooky s podrobnější dokumentací k jednotlivým funkcím.

Wolfram Workbench

Vývojové prostředí vhodné zejména pro práci na rozsáhlejších projektech

Editor zdrojového kódu

Správa verzí

Debugger

Tvorba dokumentace

Ke stažení na http://www.wolfram.com/products/workbench/

Cvičení

Naprogramujte následující tři funkce pro geometrické transformace obrázků. Ve všech případech značí (x,y) souřadnice pixelů v kartézské soustavě souřadnic a (r,α) příslušné polární souřadnice. Soustavu souřadnic volte tak, aby její počátek byl uprostřed obrázku a jednotky na obou osách odpovídaly velikosti 1 pixelu.

Funkce RadialWave[image,a,t] provede geometrickou transformaci zadaného obrázku, jejíž inverzní transformace je dána předpisem
({{({{(x,y)↦}})({{{, ({{(0,0), pokud r=0,}, {(1+((a sin(((2 π r)/t)))/r))(x,y), pokud r>0.}})}})}})
Parametry a>0, t>0 mají význam amplitudy, resp. periody radiální vlny. Parametry budou nepovinné a pokud je uživatel nezadá, volte a=10, t=38.

Funkce Clover[image,a,n] provede geometrickou transformaci zadaného obrázku, jejíž inverzní transformace je dána předpisem
({{({{(x,y)↦}})(1+a cos(n α))(x,y).}})

Parametry a>0, n∈ℕ lze chápat jako amplitudu, resp. počet lístků. Parametry budou nepovinné a pokud je uživatel nezadá, volte a=0.2, t=8.

Funkce Tapestry[image,a,tx,ty] provede geometrickou transformaci zadaného obrázku, jejíž inverzní transformace je dána předpisem
({{({{(x,y)↦}})(x,y)+a(sin(((2 π x)/tx)),sin(((2 π y)/ty)))}})
Parametry a>0, tx>0, ty>0 lze chápat jako amplitudu, resp. vlnové délky ve směrech souřadnicových os. Parametry budou nepovinné a pokud je uživatel nezadá, volte a=5, tx=ty=30.

Následující obrázky ukazují původní snímek a jeho transformace pomocí RadialWave, Clover a Tapestry s výchozími hodnotami parametrů.

Návod. K provedení transformací použijte funkci ImageTransformation, soustavu souřadnic nastavte pomocí volby DataRange. K převodu z kartézských do polárních souřadnic můžete použít funkci ToPolarCoordinates.

Doplňte ke třem výše uvedeným funkcím stručné nápovědy ve formě usage messages (anglicky / česky / slovensky).

Vytvořte z výše uvedených funkcí externí balíček obsahující soubory init.wl, PacletInfo.wl a stručnou dokumentaci (anglicky / česky / slovensky) s ukázkami použití všech tří funkcí. Odevzdejte kompletní balíček jako zazipovaný soubor.