How can one create a random GUID?
With random
by D. Arsenau.
\documentclass{article}
\usepackage{xparse}
\input{random}
\ExplSyntaxOn
\cs_set_eq:NN \guid_set_random_number:Nnn \setrannum
\cs_set_eq:NN \g_guid_seed_int \randomi
\tl_new:N \l__guid_four_bytes_tl
\int_new:N \l__guid_random_int
\cs_new_protected:Nn \__guid_generate:n
{
\guid_set_random_number:Nnn \l__guid_random_int { 0 } { #1 }
\int_case:nn { #1 }
{
{ 4095 }{ \int_add:Nn \l__guid_random_int { "4000 } }
{ 16383 }{ \int_add:Nn \l__guid_random_int { 32768 } }
}
\tl_set:Nx \l__guid_four_bytes_tl { \int_to_Hex:n { \l__guid_random_int } }
\tl_set:Nx \l__guid_four_bytes_tl
{
\prg_replicate:nn { 4 - \tl_count:N \l__guid_four_bytes_tl } { 0 }
\tl_use:N \l__guid_four_bytes_tl
}
}
\NewDocumentCommand{\GUIDgenerate}{om}
{
\tl_new:c { g_guid_user_#2_tl }
\group_begin:
\IfValueT { #1 } { \int_gset:Nn \g_guid_seed_int { #1 } } % the seed
% Data 1 (eight bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 2 (four bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 3 (four bytes)
\__guid_generate:n { 4095 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 4a (three bytes)
\__guid_generate:n { 16383 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
% hyphen
\tl_gput_right:cx { g_guid_user_#2_tl } { - }
% Data 4b (twelve bytes)
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\__guid_generate:n { 65535 }
\tl_gput_right:cx { g_guid_user_#2_tl } { \l__guid_four_bytes_tl }
\group_end:
}
\DeclareExpandableDocumentCommand{\GUID}{m}
{
\tl_use:c { g_guid_user_#1_tl }
}
\ExplSyntaxOff
\GUIDgenerate{fooA}
\GUIDgenerate{fooB}
\GUIDgenerate{fooC}
\begin{document}
\ttfamily
\GUID{fooA}\par
\GUID{fooB}\par
\GUID{fooC}\par
\GUIDgenerate[2]{fooD}
\GUIDgenerate[3]{fooE}
\GUIDgenerate[42]{fooF}
\GUID{fooD}\par
\GUID{fooE}\par
\GUID{fooF}\par
\end{document}
If you compile this several times, you'll see that the first three GUIDs change, whereas the last three don't, because they're defined with a fixed seed.
using the random generator from PDFTeX and package xintbinhex
for hexadecimal conversions.
edit: I add a simpler no-package version. Still using pdfuniformdeviate
.
I forgot to say that the uppercase letters are produced with catcode letter, they are the usual ones. (in case it matters at all...)
edit2: somehow I misread instructions and was producing only 28 hexadecimal digits. Fixed.
\documentclass{article}
\usepackage{xintbinhex}
\makeatletter
% there is a complication as we need to avoid stripping leading zeros...
% ... but I am going to use \xintDecToHex which does that.
%
\begingroup\catcode0 12
\gdef\@expand@and@gob {\expandafter\@gobble\romannumeral`^^@}
\endgroup
% this one is expandable
\newcommand*{\GUID}
{%
% Data 1: (8 Hex Digits)
% \pdfuniformdeviate will refuse higher than "7FFFFFFF
% \xintDecToHex wants tokens, hence \the\numexpr needed.
% generate five hex digits and gobble the first one (which will be 1)
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}%
% again for a total of 8 Hex Digits
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}-%
% Data 2: (4 Hex Digits)
\@expand@and@gob
\xintDecToHex {\the\numexpr "10000+\pdfuniformdeviate "FFFF\relax}-%
% Data 3: (4 Hex Digits, the first one a 4)
\xintDecToHex {\the\numexpr "4000+\pdfuniformdeviate "FFF\relax}-%
% Data 4: (16=4+12 Hex Digits, the first one 8, 9 , A or B)
\xintDecToHex {\the\numexpr "8000+\pdfuniformdeviate "4000\relax}-%
\@expand@and@gob
\xintDecToHex {\the\numexpr "1000000+\pdfuniformdeviate "FFFFFF\relax}%
\@expand@and@gob
\xintDecToHex {\the\numexpr "1000000+\pdfuniformdeviate "FFFFFF\relax}%
}
% side-note: if we used directly \pdfuniformdeviate in \xintDecToHex,
% we could do it this way
% \xintDecToHex {\pdfuniformdeviate "FFFF }% <-- space needed
% we don't do this above as \xintDecToHex trims leading zeros
\makeatother
% this one is not expandable
\newcommand*{\GUIDnx}[1][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \GUID\endgroup}
% this one is not expandable
\newcommand*{\GUIDset}[2][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \xdef#2{\GUID}\endgroup}
\begin{document}
\ttfamily
\GUID\par
\GUID\par
\GUID\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\GUIDnx [0]\par
\GUIDset[0]\foo
\meaning\foo<-- end of macro\par
\GUIDnx [123456789]\par
\GUIDset[123456789]\foo
\meaning\foo<-- end of macro\par
\end{document}
%No package version
\documentclass{article}
\def\GUIDonedigit {\ifcase\pdfuniformdeviate 16\space\space
0\or 1\or 2\or 3%
\or 4\or 5\or 6\or 7%
\or 8\or 9\or A\or B%
\or C\or D\or E\else F\fi}
\def\GUIDonespecialdigit {\ifcase\pdfuniformdeviate 4\space\space
8\or 9\or A\else B\fi }
% this one is expandable
\newcommand*{\GUID}
{%
% Data 1: (8 Hex Digits)
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 2: (4 Hex Digits)
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 3 : (4 Hex Digits, the first one a 4)
4\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
% Data 4: (16=4+12 Hex Digits, the first one 8, 9 , A or B)
\GUIDonespecialdigit\GUIDonedigit\GUIDonedigit\GUIDonedigit-%
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
\GUIDonedigit\GUIDonedigit\GUIDonedigit\GUIDonedigit
}
\makeatother
% this one is not expandable
\newcommand*{\GUIDnx}[1][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \GUID\endgroup}
% this one is not expandable
\newcommand*{\GUIDset}[2][.]{\begingroup\ifx.#1\else
\pdfsetrandomseed #1\relax\fi \xdef#2{\GUID}\endgroup}
\begin{document}
\ttfamily
\GUID\par
\GUID\par
\GUID\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\edef\foo{\GUID}\meaning\foo<-- end of macro\par
\GUIDnx [0]\par
\GUIDset[0]\foo
\meaning\foo<-- end of macro\par
\GUIDnx [123456789]\par
\GUIDset[123456789]\foo
\meaning\foo<-- end of macro\par
\end{document}
this might not work in all tex engines.
{\catcode`\%=12
\gdef\GUID{\directlua{
tex.print(string.format("%08x-%04x-%04x-%04x-%012x",
math.random(0xffffffff),
math.random(0xffff),
0x4000+math.random(0xfff),
0x8000+math.random(0x3fff),
math.random(0xffffffffffff)
))
}}}
\tt
\edef\foo{\GUID}\foo
\GUID
\bye