16

GUIDs (or Globally Unique Identifier) are usually stored as 128-bit values, and are commonly displayed as 32 hexadecimal digits with groups separated by hyphens, such as:

21EC2020-3AEA-4069-A2DD-08002B30309D

Online resources are available for creating pseudo-random GUIDs, but I'd like to create on during compile time. Ideally I'm interested in a macro \GUID that would produce a GUID (that I can store in a macro), perhaps using the computer time to initialise a pseudo-random number generator. If possible, perhaps an optional argument would allow one to supply a seed that should return a fixed GUID.

I'm specifically interested in Version 4 GUIDs which have the following format:

Hex digits  Description
8           Data1
4           Data2
4           Data3
4           Initial two bytes from Data4
12          Remaining six bytes from Data4

"Version 4 GUIDs simply use a pseudo-random number for filling in all but six of the bits. They have a 4 in the 4-bit version position, and the first two bits of data4 are 1 and 0 (so the first hex digit of data4 is 8, 9, A, or B), for example 38A52BE4-9352-453E-AF97-5C3B448652F0. More specifically, the data3 bit pattern would be [...] 0100xxxxxxxxxxxx [...]."

  • do you also need the 128bits in a macro ? – user4686 Oct 3 '16 at 18:23
  • @jfbu: Not really, as I think the hex representation is more commonly used. – Werner Oct 3 '16 at 18:37
17

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

  • Of course one could use the random number generator provided by pdftex, but using random.tex the code above is portable across engines. – egreg Oct 3 '16 at 9:49
  • I don't get what you mean by "you'll see that the last three values don't change". Your own example seems to contradict this. Would you care to elaborate a little on that part? – a CVn Oct 3 '16 at 11:13
  • 1
    @MichaelKjörling If you specify a seed, the random number generator will always spit out the same value. If you compile several times the example, the first three GUIDs will change every time, the last three will remain fixed. – egreg Oct 3 '16 at 11:58
  • Ah. That makes more sense. I thought you were referring to some part of the GUIDs themselves, not the GUIDs as a whole, hence my confusion. You may want to edit your answer to use the second sentence from your comment above. – a CVn Oct 3 '16 at 12:18
  • the first hex digit of your data4 doesn't seem to be always 8, 9, A, or B. – user4686 Oct 3 '16 at 14:34
11

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

6

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
3

One more way of using PDFTeX random generator primitives.

Macro \GUID is used to generate random GUID, macro \newGUID#1 generates a random GUID and binds it with provided ID, macro \getGUID#1 returns the GUID, binded with provided ID.

Plain pdftex MWE

\def\decdig#1{\expandafter\def\csname#1@@@\endcsname{#1}}
\def\hexdig#1#2{\expandafter\def\csname#1@@@\endcsname{#2}}

\decdig0\decdig1\decdig2\decdig3\decdig4\decdig5\decdig6\decdig7\decdig8\decdig9
\hexdig{10}A\hexdig{11}B\hexdig{12}C\hexdig{13}D\hexdig{14}E\hexdig{15}F

\def\hexdigit#1{\csname#1@@@\endcsname}
\def\X{\hexdigit{\pdfuniformdeviate16}}
\def\XX{\X\X}
\def\XXXX{\XX\XX}

\def\GUID{
    \XXXX\XXXX-\XXXX-4\XX\X-%
    \hexdigit{\the\numexpr8+\pdfuniformdeviate4\relax}\XX\X-\XXXX\XXXX\XXXX}

\def\newGUID#1{\expandafter\edef\csname#1@@@@@\endcsname{\GUID}}
\def\getGUID#1{\csname#1@@@@@\endcsname}

\pdfsetrandomseed1371

Random GUIDs

{
    \tt
    \GUID

    \GUID

    \GUID

    \GUID

    \GUID
}
%
\newGUID{1} \newGUID{1212} \newGUID{special}

\ 

\settabs\+ special\quad&\tt\getGUID{1}&\cr 
\+ Saved GUIDs \cr
\+ ID      & \hfill GUID \hfill  &\cr
\+ 1       & \tt\getGUID{1}      \cr
\+ 1212    & \tt\getGUID{1212}   \cr
\+ special & \tt\getGUID{special}\cr
\+ 1212    & \tt\getGUID{1212}   \cr
\+ 1       & \tt\getGUID{1}      \cr
\+ special & \tt\getGUID{special}\cr


\bye

enter image description here

pdflatex MWE, (essentially the same)

\documentclass{article}

\def\decdig#1{\expandafter\def\csname#1@@@\endcsname{#1}}
\def\hexdig#1#2{\expandafter\def\csname#1@@@\endcsname{#2}}

\decdig0\decdig1\decdig2\decdig3\decdig4\decdig5\decdig6\decdig7\decdig8\decdig9
\hexdig{10}A\hexdig{11}B\hexdig{12}C\hexdig{13}D\hexdig{14}E\hexdig{15}F

\def\hexdigit#1{\csname#1@@@\endcsname}
\def\X{\hexdigit{\pdfuniformdeviate16}}
\def\XX{\X\X}
\def\XXXX{\XX\XX}

\def\GUID{
    \XXXX\XXXX-\XXXX-4\XX\X-%
    \hexdigit{\the\numexpr8+\pdfuniformdeviate4\relax}\XX\X-\XXXX\XXXX\XXXX}

\def\newGUID#1{\expandafter\edef\csname#1@@@@@\endcsname{\GUID}}
\def\getGUID#1{\csname#1@@@@@\endcsname}


\begin{document}

    \pdfsetrandomseed1371

    Random GUIDs

    \texttt{\GUID}

    \texttt{\GUID}

    \texttt{\GUID}

    \texttt{\GUID}

    \texttt{\GUID}

    \ 

    \newGUID{1} \newGUID{1212} \newGUID{special}

    Saved GUIDs 

    \begin{tabular}{cc}
        ID & GUID\\
        1&\texttt{\getGUID{1}} \\
        1212&\texttt{\getGUID{1212}} \\
        special & \texttt{\getGUID{special}}\\
        1212&\texttt{\getGUID{1212}} \\
        1&\texttt{\getGUID{1}} \\
        special & \texttt{\getGUID{special}}
    \end{tabular}


\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.