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.

enter image description here


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}

enter image description here


this might not work in all tex engines.

enter image description here

{\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