Externí balíčky

Antonín Slavík
MFF UK

Usage messages

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

?Plot

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

smiley[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];
smiley::usage="smiley[color] zobrazí smajlíka se zadanou barvou obličeje (při vynechání argumentu bude bílá).";

?smiley

smiley[Pink]

Clear[smiley]

Externí balíčky

Soubory obsahující definice nových funkcí

Obvykle textový soubor s příponou .m

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

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

Seznam dalších balíčků, které jsou na daném počítači připraveny k použití, 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ů.

Identifikátory, které jsou součástí jádra, 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]

Clear[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 Mathematice 8 a novější dochází např. u balíčku Combinatorica ke konfliktů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]

Struktura balíčku

BeginPackage["název balíčku`"]

(* 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["Private"]

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.

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.

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 čísel, která kódují jednotlivé znaky zadaného řetězce. Prvky tohoto seznamu můžeme chápat jako cifry velkého čísla vyjádřeného v soustavě o základu 65536.

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. Zpráva je nejprve převedena na číslo; je-li toto číslo menší než n, můžeme je přímo zašifrovat, jinak 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]:=PowerMod[StringToInteger[m],e,n]/;StringLength[m]≤Log[65536,n]

RSAEncode[m_String,e_Integer,n_Integer]:=Module[{maxlength=Floor[Log[65536,n]],length=StringLength[m]},
Map[RSAEncode[#,e,n]&,Table[StringTake[m,{pos,Min[length,pos+maxlength-1]}],{pos,1,length,maxlength}]]]/;StringLength[m]>Log[65536,n]

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]:=PowerMod[StringToInteger[m],e,n]/;StringLength[m]<=Log[65536,n]

RSAEncode[m_String,e_Integer,n_Integer]:=Module[{maxlength=Floor[Log[65536,n]],length=StringLength[m]},
Map[RSAEncode[#,e,n]&,Table[StringTake[m,{pos,Min[length,pos+maxlength-1]}],{pos,1,length,maxlength}]]]/;StringLength[m]>Log[65536,n]

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 nebo Get 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, 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.m}})

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, jehož obsah je vyhodnocen při pokusu o načtení balíčku. 

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

Zkouška:

Quit[]

Needs["RSA`"]

?RSAEncode

?RSADecode

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

Adresářová struktura:

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

Obsah PacletInfo.m:

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

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

Součást Premier Service, ke stažení na http://www.wolfram.com/products/workbench/

Volby (options)

Chování většiny funkcí v Mathematice lze ovlivňovat pomocí voleb. Options[identifikátor] zobrazí seznam všech voleb a jejich výchozí hodnoty.

Options[Plot]

Také uživatelem definované funkce mohou mít své volby.

Příklad: Která přirozená čísla n lze psát ve tvaru n=(a^2)+(b^2), kde a, b jsou celá čísla?

n=2009;

Solve umí řešit i nerovnice a lze zadat číselný obor, ve kterém hledáme řešení.

Solve[n==a^2+b^2,{a,b},Integers]

Solve[n==a^2+b^2&&a>=0&&b>=0,{a,b},Integers]

Clear[n]

Funkce pro hledání rozkladu celého čísla s volbou Nonnegative (jen nezáporná řešení?).

Options[SumOfSquares]={Nonnegative->False};

SumOfSquares[n_,opts___]:=Module[{nonnegative,system,solution},
nonnegative=Nonnegative/.{opts}/.Options[SumOfSquares];
system=If[nonnegative,n==a^2+b^2&&a>=0&&b>=0,n==a^2+b^2];
solution=Solve[system,{a,b},Integers];
If[solution≠{},{a,b}/.solution,{}]
]

SumOfSquares[2009]

SumOfSquares[2009,Nonnegative->True]

Volby mohou mít své usage messages:

Nonnegative::usage="Nonnegative is an option to SumOfSquares. When set to True, SumOfSquares returns only nonnegative integers.";

?Nonnegative

SumOfSquares[2010,Nonnegative->True]

Věta (Fermat): Prvočíslo lze vyjádřit jako součet čtverců nezáporných celých čísel, právě když jeho zbytek při dělení čtyřmi je různý od tří. Toto vyjádření je až na pořadí sčítanců jednoznačné.

primes=Prime[Range[100]];
Grid[Map[{#,SumOfSquares[#,Nonnegative->True],Mod[#,4]}&,primes],Dividers->All]

Věta: Přirozené číslo n lze vyjádřit jako součet čtverců nezáporných celých čísel, právě když všechna prvočísla, která při dělení čtyřmi dávají zbytek 3, mají v prvočíselném rozkladu n sudý exponent.

SumOfSquares[2^2*7^3]

SumOfSquares[2^2*7^4,Nonnegative->True]

Některá složená čísla lze rozložit na součet čtverců více způsoby:

SumOfSquares[5000,Nonnegative->True]

Označme symbolem r(n) počet všech celočíselných řešení rovnice n=(a^2)+(b^2) (kde n je dáno a a, b jsou neznámé). Tj. uvažujeme i záporná řešení a bereme ohled na pořadí sčítanců:

r[n_]:=Length[SumOfSquares[n]]

r[5000]

Map[r,Range[5000]]

N[Total[%]/5000]

Věta: Průměrná hodnota r(n) je π, tj. (lim_(N->∞)) (1/N)(∑_(n=1)^N)r(n)=π.

Geometrické vysvětlení:

r(n) je počet bodů s celočíselnými souřadnicemi, které leží na kružnici s poloměrem (Sqrt[n]) a středem v počátku.

(∑_(n=1)^N)r(n) je počet bodů s celočíselnými souřadnicemi, jejichž vzdálenost od počátku je nejvýše (Sqrt[N]).

Nakreslíme-li kolem každého bodu jednotkový čtvereček a sečteme-li jejich obsahy, dostaneme přibližně obsah kruhu o poloměru (Sqrt[N]), tedy (∑_(n=1)^N)r(n)≈π N.

showPointsInside[r_]:=Module[{points={x,y}/.Solve[x^2+y^2≤r^2,{x,y},Integers]},Graphics[{PointSize[Medium],Lighter@Yellow,EdgeForm[Black],Map[Rectangle[#-{1/2,1/2},#+{1/2,1/2}]&,points],Darker@Blue,Thick,Circle[{0,0},r],Darker@Red,Point[points]}]]

Manipulate[showPointsInside[n],{n,5,20,1,Appearance->"Labeled"},SaveDefinitions->True]

SetOptions

Pomocí SetOptions se dají změnit výchozí hodnoty voleb.

Options[SumOfSquares]

SetOptions[SumOfSquares,Nonnegative->True];

SumOfSquares[5000]

SetOptions funguje i pro zabudované funkce:

Plot[ArcTan[x],{x,-10,10}]

SetOptions[Plot,BaseStyle->{FontFamily->"Arial",FontSize->12},PlotStyle->Thick]

Plot[ArcTan[x],{x,-10,10}]