% \iffalse meta-comment % !TEX encoding = UTF-8 Unicode % !TEX program = pdfLaTeX %^^A PdfLaTeX-dev % %<*internal> \iffalse % %<*readme> cooking-units ============= Overview -------- While writing a cookery book, units and their format can be quite troublesome. This contains not only the question of how to print those units, but also the question of the conversion of units. A cookery book doesn't contain only SI units, it contains a much more colorful range of units (kg, dag, g, oz, ... for example) and it may cause some confusion if too many different units are used, but if you just write a cookery book for fun you don't think about this ... until someone (my mother in my case) tells you that it's too confusing. This package solves both problems: It formats your input and you have the option to convert the units globally or locally using an easy to use key-value system. Please note that this package is not intended to be used to print units (and numbers) physically correct. It should be used for more light-hearted stuff (like a cookery book for example). Requirements ------------ * expl3 & xparse * translations * xfrac * l3keys2e * fmtcount (optional) This file contains ------------------ * README * cooking-units.pdf * cooking-units.dtx * cooking-units.ins Changes ------- See section 'Change History' in the documentation. Supported Languages ------------------- * German * English * French License ------- This material is subject to the LaTeX Project Public License 1.3c or later. See https://www.latex-project.org/lppl/ for the details of that license. % %<*internal> \fi \def\nameofplainTeX{plain} \ifx\fmtname\nameofplainTeX\else \expandafter\begingroup \fi % %<*install> \input l3docstrip.tex %\keepsilent \askforoverwritefalse \preamble Copyright (C) 2016--2022 by Ben Vitecek (current Maintainer) This file may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version 1.3c of this license or (at your option) any later version. The latest version of this license is in: https://www.latex-project.org/lppl/ and version 1.3c or later is part of all distributions of LaTeX version 2008-05-04 or later. This work has the LPPL maintenance status `maintained'. This work consists of the files README.md, cooking-units.dtx and cooking-units.ins and the derived file cooking-units.sty. \endpreamble %\postamble %\endpostamble \usedir{tex/latex/cooking-units} \generate{ \file{\jobname.sty}{\from{\jobname.dtx}{package}} } % %\endbatchfile %<*internal> \usedir{source/latex/cooking-units} \generate{ \file{\jobname.ins}{\from{\jobname.dtx}{install}} } \nopreamble\nopostamble \usedir{doc/latex/cooking-units} \generate{ \file{README.md}{\from{\jobname.dtx}{readme}} } \ifx\fmtname\nameofplainTeX \expandafter\endbatchfile \else \expandafter\endgroup \fi % %<*driver> \documentclass[ ngerman, french, english, %french, naustrian onlydoc, % check-declarations, ]{l3doc} \usepackage[utf8]{inputenc} \usepackage[main=english]{babel} \usepackage[use-fmtcount-numerals=true]{cooking-units} \usepackage{tikzsymbols} \usepackage{listings} \usepackage{environ} \usepackage{nicefrac} \usepackage{cleveref} \usepackage{mathtools} \usepackage{siunitx}[=v2] \usepackage{totalcount} %\usepackage{microtype} \usepackage{enumitem} \newcounter{CookingUnitsCounter} \newcounter{cunumCounter} \newcounter{cuamCounter} \newcounter{cutextCounter} \newcounter{CutextCounter} \DeclareTotalCounter{CookingUnitsCounter} \DeclareTotalCounter{cunumCounter} \DeclareTotalCounter{cuamCounter} \DeclareTotalCounter{cutextCounter} \DeclareTotalCounter{CutextCounter} \babeltags{myde = ngerman} \DeclareSIUnit{\ounce}{oz} \DeclareSIUnit{\oz}{\ounce} \DeclareSIUnit{\pound}{lb} \DeclareSIUnit{\lb}{\pound} \DeclareSIUnit{\stone}{st} \DeclareSIUnit{\st}{\stone} \DeclareSIUnit{\stick}{st} \DeclareSIUnit{\inch}{in} \cusetup{print-numerals=false} %\EnableCrossrefs \newcommand\US{$^{\mathrm{U.S.}}$} \ExplSyntaxOn \NewDocumentCommand \cookingunitsToDo { m } { \marginpar { \textcolor{red}{#1} } } \NewDocumentCommand \cookingunitsTODO { m } { \cookingunitsToDo {#1} } \NewDocumentCommand \cookingunitsContinue { } { \cookingunitsToDo {Continue} } \NewDocumentCommand \cookingunitsCONTINUE { } { \cookingunitsContinue } \NewDocumentCommand \cookingunitsChecked { } { \cookingunitsToDo{Checked} } \clist_new:N \l_cookingunits_list_of_defined_befehlen_clist \clist_set:Nn \l_cookingunits_list_of_defined_befehlen_clist { cunum, cutext, Cutext, cuam, newcookingunit, declarecookingunit, providecookingunit, cudefinename, cudefinesymbol, cusetup, cudefinekeys, cudefinesinglekey, cuaddkeys, cuaddsinglekeys, cuaddtokeys, culabel, curef, cudefinephrase, declarecookingderivatives , cufrac , cusetoptionfor, cuaddoptionfor, cuclearoptionfor, cudeclareunitgroup, cuaddtounitgroup, cuclearunitgroup , cudefinekeys , , cudefinekeychain , cuaddtokeychain , cudeclareunitgroup , cookingunits_print_fraction:nnn , cookingunits_print_range:nn , ExplSyntaxOff, ExplSyntaxOn } \NewDocumentCommand \mycs { m } { \tl_set_rescan:Nnn \l_tmpa_tl { \char_set_catcode_letter:N \_ % \char_set_catcode_letter:N \: % } {#1} \clist_if_in:NVTF \l_cookingunits_list_of_defined_befehlen_clist \l_tmpa_tl { \cs_if_exist:cTF { \l_tmpa_tl } { \cs{\textcolor{blue}{ \l_tmpa_tl }} } { \tl_show:n {#1}\mycsERRORII } } { \tl_show:n {#1} \mycsERROR } } \newcommand\opt{\texttt} \newcommand\myunit{\texttt} \newcommand\mygroup{\texttt} \newcommand\myaarg[1]{\texttt{<}\meta{#1}\texttt{>}} \newcommand\true{\opt{true}} \newcommand\false{\opt{false}} %: \PrintUnit \NewDocumentCommand \PrintUnit { o m } { \__cookingunits_culang_let:nnN { name } {#2} \l_tmpa_tl \tl_if_eq:NNF \l_tmpa_tl \c__cookingunits_no_translation_str { \l_tmpa_tl } \IfNoValueF {#1} {#1} & \PrintUnitSymbol {#2} } \NewDocumentCommand \PrintUnitSymbol { m } { \texttt {#1} & \tl_use:c { l__cookingunits_default_unit_ #1 _tl } } \NewDocumentCommand \PrintGroup { m } { \mygroup {#1} & \texttt{\seq_use:cn { l__cookingunits_group_ #1 _seq } { \textrm{,~} }} } \NewDocumentCommand \DeactivateForbiddenUnitError { } { \msg_redirect_name:nnn { cooking-units } { curef-forbidden-unit } { warning } } %% Allyourwhitespacearebelongtous (too long) %% 42 %% Miep! %% Ducks \NewDocumentCommand \dothewhitespace { O{ cup~of~tea } } { \textcolor{white} {#1} \\ } \newcommand\eatthewhitespace{} \NewEnviron { cuexamplecode }[1][] { \begin{center} \begin{minipage}{0.4\linewidth} \hbadness=10000 \tl_replace_all:Nnn \BODY { @ } { \dothewhitespace } \tl_replace_all:Nnn \BODY { \% } { \color{red} \% \ignorespaces } \tl_replace_all:Nnn \BODY { \\ } { \\ \color{black} } \tl_replace_all:Nnn \BODY { & } { \ignorespaces } \BODY \end{minipage} #1 \int_zero:N \l_tmpa_int \begin{minipage}{0.5\linewidth} \hbadness=10000 \ttfamily \tl_set_rescan:Nno \l_tmpa_tl { \char_set_catcode_other:N \{ % \char_set_catcode_other:N \} % \char_set_catcode_other:N \_ % \char_set_catcode_other:N \~ % % \char_set_catcode_other:N \# % } { \BODY } \tl_map_inline:Nn \l_tmpa_tl { \token_if_cs:NTF ##1 { \cs_if_eq:NNTF ##1 \\ { \textbackslash\textbackslash \\ } { \textbackslash \textcolor {blue} { \cs_to_str:N ##1 } } }{ \token_if_letter:NTF ##1 {##1} { \str_case:nnF {##1} { { @ } { \\ } { & } { \space } }{ \token_if_active:NTF ##1 {##1} { \token_if_parameter:NTF ##1 { \int_compare:nNnTF { \l_tmpa_int } = { 0 } { \c_hash_str \int_incr:N \l_tmpa_int } { \int_zero:N \l_tmpa_int } } { ##1 {} } } } } } } \end{minipage} \end{center} } \ProvideDocumentEnvironment{cuexamplecode}{}{}{}{} \NewDocumentEnvironment { cuexample } { } { \paragraph{Example:} % \par \bigskip \noindent % \mbox{} % \marginpar{\raggedleft\textbf{Example}}\ % \ignorespaces } { } \NewDocumentEnvironment { cunote } { } { \paragraph{Note:} }{ } \NewEnviron { cuenumnote } { \begin{enumerate}[label={Note \space \arabic*:}] \BODY \end{enumerate} } \NewDocumentEnvironment { cuinterest } { m } { \par \bigskip \clist_set:Nn \l_tmpa_clist {#1} \mbox{}\marginpar{\raggedleft\opt{ \clist_use:Nn \l_tmpa_clist { \\ } }} \ignorespaces } { } %: table \tl_new:N \l_translation_does_not_exist_tl \tl_set:Nn \l_translation_does_not_exist_tl { --- } \tl_new:N \translationdoesnotexisttl \tl_set_eq:NN \translationdoesnotexisttl \l_translation_does_not_exist_tl \tl_new:N \mytablecolumns \tl_set:Nn \mytablecolumns { l l l l l } \clist_new:N \l_list_of_units_clist \clist_new:N \l_list_of_textunits_clist \clist_set:Nn \l_list_of_textunits_clist { decimal-mark , one (m) , one (f) , one (n) , } \clist_set:Nx \l_list_of_units_clist { kg , dag , g , oz , lb , space , C , F , Re , K , space , d , h , min , s , space , m , dm , cm , mm , in , space , l , dl , cl , ml , space , cal , kcal , J , kJ , eV , space , pn , EL , TL , csp , dsp , ssp , Msp , space , \clist_use:Nn \l_list_of_textunits_clist { , } , THEEND } \cs_new:Npn \__table_midrule: { \\ \midrule \cs_gset:Npn \__table_midrule: { \\ } } \cs_new_eq:NN \__table_default_midrule: \__table_midrule: \cs_new:Npn \__cu_use:n #1 { \cs_if_exist_use:cF { __cookingunits_#1 } {\ERRORcommandNOTexist} } \NewDocumentCommand \CreateTableForLanguages { s >{\TrimSpaces} m } { \IfBooleanTF {#1} { \clist_gset_eq:NN \l_tmpa_clist \l_list_of_textunits_clist } { \clist_gset_eq:NN \l_tmpa_clist \l_list_of_units_clist } \cs_gset_eq:NN \__table_midrule: \__table_default_midrule: \CreateTableHeader {#2} \CreateTableRow {#2} } \NewDocumentCommand \CreateTableHeader { m } { \meta{unit-key} & printed \ unit & unit name & (plural) & gender } \NewDocumentCommand \CreateTableRow { m } { \clist_map_inline:Nn \l_tmpa_clist { \str_case:nnF {##1} { { space } { \\ \addlinespace \cs_set:Npn \__table_midrule: {} } { THEEND } { \\ \bottomrule } }{ \__table_midrule: \__cookingunits_doc_print_table_row:nn {#1} {##1} } } } \cs_new:Npn \__cookingunits_doc_print_table_row:nn #1#2 { \texttt{#2} & \clist_if_in:NnTF \l_list_of_textunits_clist {#2} { \l_translation_does_not_exist_tl } { \__cu_use:n { culang_let_for:nnnN } {#1} { unit-symbol } {#2} \l_tmpa_tl \tl_if_in:NnTF \l_tmpa_tl \c__cookingunits_no_translation_str { \l_translation_does_not_exist_tl } { \l_tmpa_tl } } & \__cu_use:n { culang_let_for:nnnN } {#1} { name } {#2} \l_tmpa_tl \tl_if_eq:NNTF \l_tmpa_tl \c__cookingunits_no_translation_str { \l_translation_does_not_exist_tl } { \l_tmpa_tl } & \__cu_use:n { culang_let_for:nnnN } {#1} { name } {#2} \l_tmpa_tl \__cu_use:n { culang_let_for:nnnN } {#1} { name-pl } {#2} \l_tmpb_tl \str_if_eq:NNF \l_tmpa_tl \l_tmpb_tl { \tl_if_eq:NNTF \l_tmpb_tl \c__cookingunits_no_translation_str { \l_translation_does_not_exist_tl } { (\l_tmpb_tl) } } & \__cu_use:n { culang_let_for:nnnN } {#1} { gender } {#2} \l_tmpa_tl \tl_if_eq:NNTF \l_tmpa_tl \c__cookingunits_no_translation_str { \l_translation_does_not_exist_tl } { \l_tmpa_tl } } \NewDocumentCommand \CreateTableForPhrases { >{\TrimSpaces} m } { \seq_clear:N \l_tmpa_seq \__cu_use:n { culang_get_phrase_prop_for:nN } {#1} \l_tmpa_prop \prop_if_exist:NTF \l_tmpa_prop { \tl_if_eq:NNTF \c__cookingunits_no_translation_str \l_tmpa_prop { \ERRORCreateTablePhraseII } { \prop_get:NnN \l_tmpa_prop { seq } \l_tmpa_seq } } { \ERRORCreateTablePhraseI } \seq_gput_right:Nn \l_tmpa_seq { THEEND } \cs_gset_eq:NN \__table_midrule: \__table_default_midrule: \begin{tabular}{ l l l l l } \toprule \meta{Phrase-key} & phrase & (plural) & gender \CreatePhraseTableRow {#1} \end{tabular} } \NewDocumentCommand \CreatePhraseTableRow { m } { \seq_map_inline:Nn \l_tmpa_seq { \str_case:nnF {##1} { { space } { \addlinespace \cs_set:Npn \__table_midrule: {} } { THEEND } { \\ \bottomrule } }{ \__table_midrule: ##1 & \prop_get:NnN \l_tmpa_prop {##1} \l_tmpa_tl \tl_gset:NV \l_tmpa_tl \l_tmpa_tl %% Damit global \l_tmpa_tl & \prop_get:NnN \l_tmpa_prop {##1-pl} \l_tmpb_tl \tl_if_eq:NNF \l_tmpa_tl \l_tmpb_tl { \l_tmpb_tl } & \prop_get:NnN \l_tmpa_prop {##1-gender} \l_tmpa_tl \l_tmpa_tl } } } \newcommand\txtupde{\textsuperscript{de}} \newcommand\txtupsic{\textsuperscript{sicilian}} \newcommand\txtupen{\textsuperscript{en}} \NewDocumentCommand\unititem { m d** O{} O{} } { \item [{ #1 \IfNoValueF {#2} { \use:c { txtup #2 } } \tl_if_empty:nF {#3} {\ (#3) } \tl_if_empty:nF {#4} {\ [#4] } }] } \NewDocumentEnvironment { myquote } { o o } { \list{}{% \topsep 0pt \leftmargin 0pt } \item\relax \itshape }{ \normalfont \IfNoValueF{#1}{(see\ \cite{#1} \IfNoValueF{#2}{\ page \ #2} )} \endlist } \makeatletter %\newenvironment{mydescription} % {\list{}{\labelwidth\z@ \itemindent-\leftmargin % \let\makelabel\descriptionlabel}} % {\endlist} \renewcommand*\descriptionlabel[1]{\hspace\labelsep \normalfont\bfseries #1} \makeatother \ExplSyntaxOff \newcommand\cCutext{\mycs{cutext} and \mycs{Cutext}} \lstset { basicstyle = \ttfamily, basewidth = 0.51em, gobble = 3, keywordstyle = \color{blue}, language = [LaTeX]{TeX}, commentstyle=\color{red!80!black}, moretexcs = { cunum, cutext, Cutext, cuam, newcookingunit, declarecookingunit, providecookingunit, cudefinename, cudefinesymbol, cusetup, cudefinekeys, cudefinesinglekey, cuaddkeys, cuaddsinglekeys, cuaddtokeys, culabel, curef, cudefinephrase, declarecookingderivatives, , cudefinekeychain , cuaddtokeychain , cudeclareunitgroup , cusetoptionfor , usepackage } } \newcookingunit {st} \newcookingunit [\ensuremath{ {} ^ { \circ } }\kern-\scriptspace R{\text{\o}}] {Ro} \newcookingunit {ft} \newcookingunit {km} %\cuaddkeys {lb} % { % {st} { 1/14 } %% 1 lb are 1/14 st % } \cuaddtokeychain { {lb} { 14 } {st} { 1 } } \cuaddtokeys {m} {ft} {0.3048} \cuaddtokeys {m} {km} {1000} \cuaddsinglekeys {C} { {Ro} { #1*21/40 + 7.5 } } \newcommand\eneunit[3]{\sfrac{#1\,#2${}^2$}{#3${}^2$}} %% Can overflow and takes too long, hard code it \declarecookingderivatives{kg,m,s}{#1*#2:#3}{ (#1)*(#2)^2/(#3)^2 } {\eneunit{#1}{#2}{#3}} \declarecookingderivatives{m,s}{#1:#2}{ (#1)/(#2) } {\sfrac{#1}{#2}} \cusetup { commands-add-hook = {\stepcounter{CookingUnitsCounter}} , cunum-add-hook = {\stepcounter{cunumCounter}} , cutext-add-hook = {\stepcounter{cutextCounter}} , Cutext-add-hook = {\stepcounter{CutextCounter}} , cuam-add-hook = {\stepcounter{cuamCounter}} , set-option-for-Ro = { round-precision= 0 } , % add-unit-to-group = { weight = {st} , temperature = {Ro} } } %: \cusetup \cusetup { % weight=stick, % 42 = true , % convert-to-eV=true , % cutext-range-sign = {as}, % erase-all-options, % set-option-for-K= {}, % set-unknown-message = error , % check-temperature = true, % round-precision = 30 , } \EnableCrossrefs \CodelineIndex \RecordChanges \AtEndDocument { \PrintChanges \PrintIndex } \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % % % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % % \iffalse %: changes % \fi % % \changes {2016/06/11} {0.99} {Added the package option to load 'fmtcount'.} % \changes {2016/08/31} {1.00} {Initial version} % \changes {2016/08/31} {1.00a} {Fixed calculation: degree Reamur to eV} % \changes {2016/09/03} {1.01} {Added units 'ssp', 'csp', 'dsp'} % \changes {2016/09/03} {1.01} {English unit: litre (and only litre) uses the curly l \protect\ensuremath{\protect\ell} now } % \changes {2016/09/03} {1.01} {British English: 'pinch' is written in full} % \changes {2016/09/03} {1.01} {Separated Messerspitze and pinch} % \changes {2016/09/05} {1.02} {Replaced \cs{cufrac} by \cs{cuam}} % \changes {2016/09/05} {1.02} {New message: 'obsolete-command'} % \changes {2016/09/09} {1.02a} {Changed name from \cs{@@_parse_fraction_in_input:www} to \cs{@@_parse_mixed_fraction_in_input:www}} % \changes {2016/09/09} {1.02b} {Changed name from \cs{@@_cunum_parse_range} (and derivatives) to \cs{@@_cutext_parse_range}.} % \changes {2016/09/09} {1.02c} {In \cs{@@_cutext_default:nnn} it is only % checked once if a range is inside.} % \changes {2016/09/09} {1.02d} {\cs{@@_calculate_input_and_store_in:nN} optimiert durch neue property-key: single.} % \changes {2016/09/09} {1.02d} {Add 'single' to property list of singlekeys.} % \changes {2016/09/16} {1.02e} {Only use \cs{phantom} if the argument (for \cs{phantom}) is not empty.} % \changes {2016/09/09} {1.02i} {Delete 'single' from property lists of singlekeys cause it is not as safe as I thought.} % \changes {2016/09/09} {1.02f} {Corrected mistake: 'ELektronenvolt' (note uppercase L) to 'Elektronenvolt' in german.} % \changes {2016/09/26} {1.03} {New option (and needed macros): add-temperature-to-check.} % \changes {2016/09/26} {1.03a} {\cs{cuaddsinglekeys} now tests if the unit exists (it didn't before).} % \changes {2016/09/26} {1.03b} {Replaced \cs{prop_clear_new:c} by \cs{prop_clear:c}.} % \changes {2016/09/26} {1.03b} {Recalculated all electron volt values for conversion (as 'kg' was wrong before). Let's hope they are correct this time.} % \changes {2016/09/26} {1.04} {New option: 'round-half'.} % \changes {2016/10/19} {1.04} {Use \cs{keys_set:nn} only if second argument is not empty.} % \changes {2016/10/19} {1.04} {'convert-to-eV' now also as optional argument available.} % \changes {2016/10/19} {1.04} {Option 'load-time-option' now spells 'available' correct.} % \changes {2016/10/19} {1.04} {Update of documentation.} % \changes {2016/10/28} {1.10alpha} {Start implementation of \enquote{Change recipe from $n$ to $m$ persons.}.} % \changes {2016/10/28} {1.10alpha} {\cs{cutext} (and \cs{Cutext}) and \cs{cuam} now parse their input like \cs{cunum}. This is needed as they also need to be changed.} % \changes {2016/10/29} {1.10alpha} {Tiding code: Now every command is separated into a \enquote{calc} function, a \enquote{print numeric value} and a \enquote{print unit} (if there) function. At least, that's the plan.} % \changes {2016/10/30} {1.10} {Fractions should now deal correctly with minus signs.} % \changes {2016/11/07} {1.10} {Finished writing v1.10.} % \changes {2016/11/13} {1.10} {Change amounts for specific number of persons.} % \changes {2016/11/13} {1.10} {\cs{cutext}, \cs{Cutext} and \cs{cuam} check their input, allows conversion of units.} % \changes {2016/11/13} {1.10} {New commands: \cs{culabel} and \cs{curef}.} % \changes {2016/11/13} {1.10} {New commands: \cs{declarecookingunit} and \cs{providecookingunit}.} % \changes {2016/11/13} {1.10} {New options: \opt{cutext-to-cunum}, \opt{cutext-change-unit} and \opt{cutext-space}.} % \changes {2016/11/13} {1.10} {New options: \opt{cuam-version} and \opt{cutext-version}} % \changes {2016/11/13} {1.10} {New options: \opt{recalculate-amount} and \opt{set-number-of-persons}, \opt{label}, \opt{get-label}, \opt{ref}.} % \changes {2017/03/10} {1.10a} {\cs{curef} is now defined by \cs{NewExpandableDocumentCommand} instead of the Declare variant.} % \changes {2017/03/10} {1.11} {Removed \cs{translate} and others from code % and replaced them with wrapper-macros.} % \changes {2017/03/10} {1.11} {Removed things like 'cu-unit' from translate input % and placed them into separate tl's.} % % \changes {2017/10/23} {1.20} {Replaced \pkg{translator} by \pkg{translations}.} % \changes {2017/10/23} {1.20} {Added \enquote{phrases}.} % \changes {2017/10/23} {1.20} {Added unit \enquote{stick} (of butter).} % \changes {2017/10/23} {1.20} {|<| is not allowed as a special sign anymore.} % \changes {2017/10/23} {1.20} {New option: \opt{set-cutext-translation-message}.} % \changes {2017/10/23} {1.20} {New option: \opt{print-numerals}.} % \changes {2017/10/23} {1.20} {New option: \opt{use-phrases}.} % \changes {2017/10/23} {1.20} {New option: \opt{phrase-space}.} % \changes {2017/10/23} {1.20} {New option: \opt{amount-unit-space}.} % \changes {2017/10/23} {1.20} {Now checks for ranges if both values can be printed as numerals (if activated) (bug fix).} % \changes {2017/10/23} {1.20} {Reworked quite a lot of code.} % % \changes {2018/04/20} {1.30} {Allow round precision to be negative.} % \changes {2018/04/20} {1.30} {Fix argument specifiers.} % \changes {2018/04/20} {1.30} {Change large portions of code.} % \changes {2018/04/20} {1.30} {Introduce key-groups (weight, volume, etc.).} % \changes {2018/04/20} {1.30} {Option: \opt{add-natural-unit}.} % \changes {2018/04/20} {1.30} {New Option: \opt{42}.} % \changes {2018/04/20} {1.30} {New options: \opt{expand-both}, \opt{expand-amount}, \opt{expand-unit}.} % \changes {2018/04/20} {1.30} {New options: \opt{set-option-for} \& \opt{add-option-for}.} % \changes {2018/04/20} {1.30} {New option: \opt{erase-all-options-for}} % \changes {2018/04/20} {1.30} {New option: \opt{add-unit-to-group}} % \changes {2018/04/20} {1.30} {New feature: Hooks} % \changes {2018/04/20} {1.30} {New parsing algorithm. Hopefully better % error recovery (if signs for fractions are in wrong order e.g.)} % \changes {2018/04/20} {1.30} {Add \enquote{Division-by-zero} error.} % \changes {2018/04/20} {1.30} {Cooking Units-keys are not allowed to contain % either \enquote{,} or \enquote{/}.} % % \changes {2018/06/05} {1.40} {This version introduces mayor internal changes. % For users not many things change.} % \changes {2018/06/05} {1.40} {Rework parsing code (again). As this is % basically an improved version of the old parsing algorithm, there is % no huge version change.} % \changes {2018/06/05} {1.40} {Improve error-recovery by a lot!} % \changes {2018/06/05} {1.40} {Bugfix: A range in \mycs{Cutext} (and numerals=true) % will print the second word small.} % \changes {2018/06/05} {1.40} {Add \enquote{range-sign} for translations (not usable yet).} % \changes {2018/06/05} {1.40} {Convert \texttt{clist} to \texttt{seq} if possible.} % \changes {2018/06/05} {1.40} {Remove unnecessary variants.} % \changes {2018/06/05} {1.40} {Fix some more argument specifiers.} % \changes {2018/06/05} {1.40} {Renaming of some internal commands.} % \changes {2018/06/05} {1.40} {\opt{set-unknown-message}: Fix default value.} % \changes {2018/06/05} {1.40} {Change (\texttt{amount-not-known}): Change message a bit.} % \changes {2018/06/05} {1.40} {Bugfix (\texttt{label-not-defined}): Add |'#1'|.} % \changes {2018/06/05} {1.40} {Bugfix (\texttt{unit-change}): \opt{convert-to-eV} % can be again used as a local argument.} % \changes {2018/06/05} {1.40} {Change order of boolean testing for |\@@_calc_references_and_store_in:N|.} % \changes {2018/06/05} {1.40} {Bugfix (\texttt{phrases}): Use the phrase from the first amount to check % the second (and don't parse through the second amount).} % % \changes {2018/09/24} {1.45} {Changes prefix from \texttt{cooking\_units} to \texttt{cookingunits}.} % \changes {2018/09/24} {1.45} {New language symbols: \opt{cutext-range-sign}} % \changes {2018/09/24} {1.45} {Improved french (not in general, only for this package)} % \changes {2018/09/24} {1.45} {New section in documentation.} % \changes {2018/09/24} {1.45} {Remove exhaustive expansion from internals (shouldn't change anything for users).} % % \changes {2021/03/21} {2.00} {Overall reworking of internal code.} % \changes {2021/03/21} {2.00} {Much better error handling.} % \changes {2021/03/21} {2.00} {Remove exhaustive expansion from translations. Shouldn't really change anything.} % \changes {2021/03/21} {2.00} {New options: \opt{definition/symbol}, \opt{definition/gender}, \opt{definition/set-option}, \opt{definition/add-to-group}. } % \changes {2021/03/21} {2.00} {Adding keys to unit definition.} % \changes {2021/03/21} {2.00} {Updatet |\declarecookingderivatives|.} % \changes {2021/03/21} {2.00} {New commands: |\cudefinekeychain|, |\cuaddtokeychain|} % \changes {2021/03/21} {2.00} {New joke options: \opt{nothing-special}, \opt{going-bonkers}, \opt{fully-bonkers} and \opt{xD-lol}.} % \changes {2021/03/21} {2.00} {New commands to define keys: \mycs{cudefinekeychain} and \mycs{cuaddtokeychain}.} % \changes {2021/03/21} {2.00} {Using commands as unit-keys now works.} % % \changes {2022/03/26} {2.00a} {Bugfix: Fixing property list retrival error (storing property lists via translations did not work too well).} % \changes {2022/03/26} {2.00a} {Bugfix: |\cuam| prints a unit in case of an error. This also happens with \opt{parse-number=false}.} % \changes {2022/03/26} {2.00a} {Bugfix: Remove \cs{peek_meaning_ignore_spaces:NTF}.} % % \changes {2022/06/06} {2.01} {Correction of french language. Thanks to Alexis Jeandeau} % \changes {2022/06/06} {2.01} {Implement a way to define the plural-finding algorithm.} % \changes {2022/06/06} {2.01} {Add two public commands \cs{cookingunits_print_fraction:nnn} and \cs{cookingunits_print_range:nn}.} % % \changes {2022/11/27} {3.00} {Allow ranges and fractions to be combined!} % \changes {2022/11/27} {3.00} {New input type: products!} % \changes {2022/11/27} {3.00} {Change |cutext-range-unit| to |cutext-range-phrase|.} % % % % \GetFileInfo{\jobname.sty} % %\title{The \pkg{\jobname} package^^A %\thanks{This document corresponds to \textsf{Benedikt Vitecek}~\fileversion, dated~\filedate.}} %\author{Ben Vitecek \\ b.vitecek@gmx.at}% %\date{\filedate}% % % % \maketitle %^^A \TODO{Test für Phrases schreiben!} % % \begin{documentation} % \begin{abstract} % This package enables user to globally format units, to switch % between them and change your recipes to a given % number of persons. % % For not implemented units or differences between Imperial and U.S.\ unit % you may have a look at \cref{sec:other-units}. % % It should be used for light-hearted things like % cookery books (and not e.g. scientific texts; use e.g.\ \pkg{siunitx} for those). % % ^^A Please read through the section \enquote{Important Changes} % \end{abstract} % % \tableofcontents % % \section{Introduction} % % While writing on a cookery book I used -- for some reasons whatsoever -- % three different units for weight: kilogram (kg), gram (g) and % decagram (dag, or older: dkg). Later my mother told me that she % doesn't like it if a cookery book uses more than two different units % (for weight in this case). Happily I hardly used Decagram and % therefore didn't have many problems changing the units. But, well % \dots\ I am using \LaTeX\ and changing those units by hand seemed % not very \LaTeX-like, so I started writing some code to convert % units. I expanded the code, rewrote it in \LaTeX3 (which is much % more pleasant than \LaTeXe) and here it is. % % % % % % \subsection{Supported languages} % \begin{itemize} % \item German % \item English % \item French % \end{itemize} % Want to contribute a new language or make a correction to an existing one? % See \cref{sec:bug-feed} for more details. Wanna just check the % existing translations? See \cref{sec:Translations}. % % \subsection{What's new?} % Since v3.00 fraction and ranges can now be combined! % Furthermore, products are introduced! Check the next section for more info. % % \section {The Commands} % \label{sec:commands} % % % This package offers the following commands for number/unit printing (and % converting): % % \begin{itemize} % \item \mycs{cunum}\myaarg{label}\oarg{options}\marg{amount}\oarg{space}\marg{unit-key} % \item \mycs{cutext}\myaarg{label}\oarg{options}\marg{amount}\marg{unit-key} % \item \mycs{Cutext}\myaarg{label}\oarg{options}\marg{amount}\marg{unit-key} % \item \mycs{cuam}\myaarg{label}\oarg{options} \marg{amount} % \item \mycs{cusetup}\marg{options} % \end{itemize} % % Numbers and units are printed using \mycs{cunum}. The numerical part % can interpret \verb|_| and \texttt{/} as (mixed) fractions, % \verb|--| as a separator for ranges and |x| or |\times| for products; % to convert units use the option % \texttt{\meta{old-unit}=\meta{new-unit}}\footnote{New keys can be % added and defined, see \cref{sec:pred-units} and % \cref{sec:def-units} for further information.}. It furthermore % allows the sign \texttt{?} to be used as a placeholder for not known % amounts and raises a warning to remind you that this amount needs a % check-up\footnote{You can customize this behavior, see % \cref{sec:options}}. \oarg{space} adds a space between the number % and the unit using \lstinline|\phantom|. % % For a list of predefined units have a look at \cref{tab:def-units}. % % \meta{label} is explained in \cref{sec:labeletref}. % % % \begin{cuexamplecode} % \cunum{1}{kg} \\ % \cunum{2.3}{kg} \\ % \cunum{2,3}{kg} \\ % \cunum{2--3}{kg} \\ % \cunum{2.5--3.5}{kg} \\ % \cunum{2.5 x 3.5}{kg} \\ % \cunum{2.5 \times 3.5}{kg} \\ % \cunum{2.5 x 3 \times 4,5}{kg} \\ % \cunum[kg=g]{2.5--3,5}{kg} \\ % \cunum[kg=g]{2.5 x 3,5}{kg} \\ % \cunum[C=F]{200}{C} \\ % \cunum[C=F]{180--200}{C} \\ % \cunum{1/2}{m} \\ % \cunum{1_1/2}{m} \\ % \cunum[m=cm]{1_1/2}{m} \\ % \cunum{1_1/2--2}{m} \\ % \cunum[m=cm]{1--1_1/2}{m} \\ % \cunum[m=cm]{1 x 1_1/2 x 2}{m} \\ % \cunum{?}{l} \\ % \cunum{50}{dag} \\ % \cunum{5}[0]{dag} \\ % \cunum{1.1234}{m} % \end{cuexamplecode} % % Decimal numbers are automatically rounded to 2 digits after the % colon, temperatures (\myunit{C}, \myunit{F}, \myunit{K} and % \myunit{Re}) are automatically rounded to integers.\footnote{You can % -- of course -- change this behavior, see \cref{sec:options}.} % % % \cCutext\ print the number and the written name of the unit. Since % v1.10 it works similar\footnote{One could also say \enquote{exactly % like}.} to \mycs{cunum}: it allows the conversion between units and % interprets the numerical part (again |_| and |/| are used for % (mixed) fractions, |--| for ranges and |x| or |\times| for products). % Furthermore, \cCutext\ allow % the usages of numerals (see \cref{sec:load-time-options} for more information). %^^A if the package %^^A option \opt{use-fmtcount-numerals} (see \cref{sec:load-time-options}) %^^A is used, integers below a specific integer %^^A (by default 13; see \opt{use-numerals-below}) are written out with %^^A \lstinline|\Cutext| capitalizing the first letter (using package %^^A \pkg{fmtcount}). % % \begin{cuexamplecode} % \cutext{1}{l} \\ % \Cutext{1}{l} \\ % \Cutext{1--2}{l} \\ % \Cutext{1 x 2}{l} \\ % \cutext{12}{l} \\ % \Cutext{13}{l} % \end{cuexamplecode} % and using (e.g.)\ package option \opt{use-fmtcount-numerals=true} % \cusetup{print-numerals=true} % \begin{cuexamplecode} % \cutext{1}{l} \\ % \Cutext{1}{l} \\ % \cutext{1--2}{l} \\ % \Cutext{1--2}{l} \\ % \Cutext{1 x 2}{l} \\ % \cutext{12}{l} \\ % \Cutext{13}{l} % \end{cuexamplecode} % \cusetup{print-numerals=false} % You can customize the numeral functions used with \opt{numeral-function} and % \opt{Numeral-function}. % % Furthermore, % since v1.10 \cCutext\ also allow their units to be changed (this behavior % can be altered using \opt{cutext-change-unit}): % \begin{cuexamplecode} % \cusetup{ l=ml } @ % \cutext{1}{l} \\ % \Cutext{1}{l} \\ % \cutext{1--2}{l} \\ % \cutext{1 x 2}{l} \\ % \cutext{12}{l} \\ % \Cutext{13}{l} \\ % \Cutext{?}{l} \\ % \Cutext{1/2}{l} \\ % \end{cuexamplecode} % % % \lstinline|\cuam| works like \mycs{cunum}, but without a unit, so % changing units doesn't affect it. It has the same syntax as \mycs{cunum}. % % \begin{cuexamplecode} % \cuam{3} \\ % \cuam{2--3} \\ % \cuam{2 x 3 x 4} \\ % \cuam{2/3} \\ % \cuam{1_2/3} \\ % \cuam{2/3--1_2/3} \\ % \cuam{2/3 x 1 x 1_2/3} % \end{cuexamplecode} % % Furthermore it allows the concept of \enquote{phrases} (replacing a positive integer % by a word; such as \enquote{12} becoming \enquote{dozen}\footnote{At least I think}) % which can be activated by the option \opt{use-phrases} % (as I don't know any english phrases, I switched the language to german for the following examples) %^^A \begin{myde} % \begin{cuexamplecode} % \selectlanguage{ngerman} @ % \cusetup{use-phrases=true} @ % \cuam{11} \\ % \cuam{12} \\ % \cuam{13} \\ % \cuam{24} \\ % \cuam{12--24} \\ % \cuam{12--13}\\ % \cuam{12x24x36} \\ % \cuam{18} \\ % \cuam{60} % \end{cuexamplecode} %^^A \end{myde} % % % \section{Label \& refs: Changing the amount of the recipe} % \label{sec:labeletref} % % % What if you don't want to change units, but the amounts of the recipe because % you cook not for 4 persons, but for 2 and don't like to do the math? % Simple, use the following commands: % % \begin{itemize} % \item \mycs{culabel}\marg{label}\marg{number of persons} % \item \mycs{curef}\marg{label} % \end{itemize} % % % The first one is the important one: It defines a \meta{label} for a recipe % which is initially for \meta{number of persons}. Afterwards \meta{label} % can be used to tell the commands from \cref{sec:commands} that the given % amounts are for \meta{number of persons}. % Each \meta{label} must be unique and an error is raised if a \meta{label} % is already defined. % % If you would like to print the number of persons this recipe is for, use % \mycs{curef}, which is fully expandable. % % The following example uses \mycs{culabel} to specify that the recipe is % initially intended for 2 persons: % \begin{cuexamplecode} % \culabel{recipe}{2} @ % recipe & for & \curef{recipe} & persons: \\ % \cunum{10--20}{dag} & flour, \\ % \cunum{1/2}{l} & water, \\ % \cutext[ref=recipe]{10}{g} & nuts, \\ % \cuam{2--3} & eggs, \\ % \cunum{180}{C} & (\cunum[C=F]{180}{C}) & open & fire % \end{cuexamplecode} % % In combination with the option \opt{set-number-of-persons} % and \opt{recalculate-amount} you can have this recipe % changed to four persons: % \begin{lstlisting} % \culabel{recipe}{2} % %% adding options: % \cusetup{set-number-of-persons=4,recalculate-amount=true} % \end{lstlisting} % \cusetup{set-number-of-persons=4,recalculate-amount=true} % % \begin{cuexamplecode} % recipe & for & \curef{recipe} & persons: \\ % \cunum{10--20}{dag} & flour, \\ % \cunum{1/2}{l} & water, \\ % \cutext[ref=recipe]{10}{g} & nuts, \\ % \cuam{2--3} & eggs, \\ % \cunum{180}{C} & & (\cunum[C=F]{180}{C}) & open & fire % \end{cuexamplecode} % \cusetup{recalculate-amount=false} % % Note that fractions are automatically evaluated and that only values % with a \meta{label} are changed (\lstinline|\cunum{180}{C}| for example % stays the same which also makes sense as the heat should be the same). % % % % % % % \section{Good to know stuff} % % \subsection{Rounding temperatures} % % % By default temperatures are rounded to integers (using \opt{round-precision=0}). % Since v1.30 it is possible to round amounts to a negative precision. % If you want to round temperatures to the tens see the following % example (\mycs{cusetoptionfor} is described in \cref{sec:opt:unit-spec}). % % \begin{cuexamplecode} % \cunum{182}{C} \\ % \cunum[C=F]{180}{C} \\ % \cunum[C=Re]{180}{C} \\ % \cunum[C=K]{180}{C} \\ % \cusetoptionfor{C,F,K,Re}{ round-precision = -1 } @ % \cunum{182}{C} \\ % \cunum[C=F]{180}{C} \\ % \cunum[C=Re]{180}{C} \\ % \cunum[C=K]{180}{C} \\ % \end{cuexamplecode} % % % \subsection{At which point is the plural used?} % While using \cCutext\ one may wonder which rules are used in order to determine % if the printed unit is singular or plural. If rules for a specific language are not found % the default ruleset is used. There are currently two rule sets: One default % and one for the french language. % % Each set has three separate rules: one for \enquote{normal} numbers, one for % ranges and one for fractions. % % \subsubsection*{French} % % Use singular if: % \begin{description} % \item[number] The absolute value of the number is smaller $2$ % \item[fraction] The absolute value of the evaluated fraction is smaller $2$. % (e.g.\ $1\,\sfrac{1}{2} = 1.5$ uses singular, $2\,\sfrac{3}{4} = 2.75$ uses plural). % \item[range/product] Take the last item and use one of the rules above. % \end{description} % % For sources see \cite{Plural-French-Academia}, \cite{Plural-French-absolute-1} \& % \cite{Plural-French-absolute-2}\footnote{Thanks a lot to Alexis Jeandeau! I never would % have imagined that the french language uses the sinfular for everything smaller $2$.}. % % \subsubsection*{Default (all other languages)} % % Use singular if: % \begin{description} % \item[number] The number is equal to $1$. % \item[fraction] There is no mixed fraction part and the numerator is equal to $1$. % \item[range/product] Take the last item and use one of the rules above. % \end{description} % % \section{Predefined units \& some notes} % \label{sec:pred-units} % % % % In \cref{tab:def-units} and you can find all predefined units which can be % transformed into each other (sorted by group). Other predefined units % (which cannot be used for transformations) are shown in \cref{tab:some-more-units}. % \Cref{tab:strange-units} pretty much exists just for fun. % % % \begin{table}[htb] % \centering % \caption{^^A % This table shows all units which can be transformed into each other, sorted % by group. The columns \enquote{default} show the abbreviations used if % no translation is defined for the given language. The translations used for \cCutext\ % are shown in \cref{sec:Translations}. % Note that \enquote{electron volt} exists just for fun. % } % \smallskip % \begin{tabular}{ lll @{\hspace{1cm}} lll } % \toprule % description & key & default & description & key & default \\ \midrule % \PrintUnit {kg} & \PrintUnit{m} \\ % \PrintUnit {dag} & \PrintUnit{dm} \\ % \PrintUnit {g} & \PrintUnit{cm} \\ % \PrintUnit {oz} & \PrintUnit{mm} \\ % \PrintUnit {lb} & \PrintUnit{in} \\ % \PrintUnit [ (of butter)] {stick} \\ \addlinespace % % \PrintUnit {d} & \PrintUnit {l} \\ % \PrintUnit {h} & \PrintUnit {dl} \\ % \PrintUnit {min} & \PrintUnit {cl} \\ % \PrintUnit {s} & \PrintUnit{ml} \\ \addlinespace % % \PrintUnit{cal} & \PrintUnit{C} \\ % \PrintUnit{kcal} & \PrintUnit{F} \\ % \PrintUnit{J} & \PrintUnit{Re} \\ % \PrintUnit{kJ} & \PrintUnit{K} \\ % \PrintUnit{eV} % % \\ \bottomrule % \end{tabular} % \label{tab:def-units} % \end{table} % % \begin{table}[htb] % \centering % \caption{A (not only) spoonful of (more or less) country and % language dependent units. Please note that sometimes a % translation is nearly impossible as a unit % (e.g. \enquote{saltspoonful}) may not exist in another language % (like german; at least I never heard of it). So please only use % units known to you. For \enquote{tablespoon} and \enquote{teaspoon} % I used the german abbreviations \enquote{EL} and \enquote{TL} (because I forgot to % change them initially).} % \smallskip % \begin{tabular}{ lll } % \toprule % description & key & symbol \\ \midrule % \PrintUnit {pn} \\ \addlinespace % \PrintUnit {EL} \\ % \PrintUnit {TL} \\ % \PrintUnit {dsp} \\ % \PrintUnit {csp} \\ % \PrintUnit {ssp} \\ \addlinespace % \PrintUnit [Messerspitze (point of a knife)] {Msp} \\ % \bottomrule % \end{tabular} % \label{tab:some-more-units} % \end{table} % % \begin{table}[htb] % \centering % \caption{List of (not really) nonsense units (exist just for fun, there will be % no support for those units; unless -- of course -- you really want it).} % \smallskip % \begin{tabular}{ ll } % \toprule % unit-key & symbol \\ \midrule % \PrintUnitSymbol {eVc-2} \\ % \PrintUnitSymbol {hbareV-1} \\ % \PrintUnitSymbol {chbareV-1} \\ % \PrintUnitSymbol {(chbareV-1)3} \\ % \bottomrule % \end{tabular} % \label{tab:strange-units} % \end{table} % % % % \section{Defining units} % \label{sec:def-units} % % % New units can be defined using % \begin{itemize} % \item \mycs{declarecookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \item \mycs{newcookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \item \mycs{providecookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \end{itemize} % % \begin{function}{\declarecookingunit,\newcookingunit,\providecookingunit} % \begin{syntax} % \mycs{declarecookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \mycs{newcookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \mycs{providecookingunit}\oarg{symbol/key-val-list}\marg{unit-key} % \end{syntax} % % These commands define the unit \meta{unit-key}. Note that \meta{unit-key} % can neither contain |/| nor |,|; but it is allowed to be a command since v2.00 % (see examples below). % % If the key is not the same as the printed symbol use the optional argument. % It can either contain the symbol you want printed or a key-value list (see below) % for more advanced adjustments. % % \mycs{newcookingunit} raises an error if the unit is already defined, % \mycs{declarecookingunit} creates or (if given) overwrites \meta{symbol} % and \mycs{providecookingunit} does nothing if the unit is already % defined. % % All units have male gender |m| by default (unless you change it using a key below). % % Some examples: % \begin{lstlisting}[commentstyle=\color{black}] % \declarecookingunit{kg} % \declarecookingunit{g} % \declarecookingunit[Msp.] {Msp} % \declarecookingunit[\ensuremath{{}^{\circ}}\kern-\scriptspace C] {C} % \declarecookingunit{\%} % can use commands now % \end{lstlisting} % % \end{function} % % \begin{cunote} % The definition of the printed degree Celsius is % copied and pasted from (a maybe older version of) \pkg{siunitx}. % \end{cunote} % % \begin{function}{symbol, gender, set-option, add-to-group, natural-unit} % % \begin{syntax} % symbol = \marg{symbol} % gender = \marg{m/f/n} % set-option = \marg{key-val-list} % add-to-group = \marg{group} % natural-unit = \marg{true/false} % \end{syntax} % % Those keys can only be used in the optional argument of % \mycs{declarecookingunit}, \mycs{newcookingunit} or \mycs{providecookingunit}. % They can be used to define some properties of the unit during its initialization. % % \opt{symbol} allows you to set the printed symbol of the unit. A similar effect can be % achieved by just using the optional argument. Use this option if you want to % use other keys during the definition. This symbol is used as a fallback for all % langauges, if no explicit symbol is found for said langauge. % % \opt{gender} sets the gender of the unit (default ist |m|). Allowed is |m|, |f| or |n|. % Note that this sets the default gender for all languages. % % \opt{set-option} allows to add some key-vals to the specific unit which are % activated once the unit is used. See \cpageref{code:set-option}. % % \opt{add-to-group} adds the unit defined to \meta{group}. See \cref{sec:opt:unit-spec} % for more information. % % \opt{natural-unit} is a simple true/false switch. If |true| the unit will be specified to be % a \enquote{natural-unit}. This is more or less a joke option. % \end{function} % % % % \begin{function}{\declarecookingderivatives} % \begin{syntax} % \mycs{declarecookingderivatives} \marg{unit-list} \marg{unit-key} % \ \ \marg{mathematical-relation} \marg{unit-symbol} % \end{syntax} % % \emph{This function is experimental}. Defines new units which are a combination % of the units given in \meta{unit-list} \emph{and} their key-chain. % \meta{unit-key}, \meta{mathematical-relation} and \meta{unit-symbol} % accept |#1| to |#n| as arguments with $n$ being the number of units % given in \meta{unit-list}. $n$ \emph{cannot} be greater than $8$ (and it will % probably compile for quite a while). Also note that this command doesn't work/isn't % tested for single keys. % % Also note that it is quite possible that an \enquote{overflow-error} will % occur if there are too many units. % % \end{function} % % \begin{cuexample} % Your homework is to change the unit of energy \si{\kg\m\squared\per\s\squared} % into \si{\ounce\inch\squared\per\minute\squared}. To check if you are correct % you use \mycs{declarecookingderivatives}: % \begin{lstlisting} % \declarecookingderivatives{kg,m,s}{#1*#2:#3} % { (#1)*(#2)^2/(#3)^2 } {\sfrac{#1\,#2${}^2$}{#3${}^2$}} % \end{lstlisting} % % Using |\cunum[kg*m:s=oz*in:min]{1}{kg*m:s}| shows that % \cunum{1}{kg*m:s} is equal to \cunum[kg*m:s=oz*in:min]{1}{kg*m:s}. %^^A\cunum{196829101.34}{oz*in:min} % %^^A \cunum[m:s=km:h]{1}{m:s} % % \end{cuexample} % % \begin{cunote} % As this is a bit more experimental and can easily lead to overflow-errors, % no actual \LaTeX\ keys are created with \mycs{declarecookingderivatives}. % Internally the keys and possible values are stored in a \emph{huge} property list. % If an unknown key is encountered, it checks if said key can be found in the % property list. % \end{cunote} % % % % % % \section{Defining options to change units} % \label{sec:defining-opt} % % % % Options (to change units) can be newly defined or added to already existing ones % using % \begin{itemize} % \item \mycs{cudefinekeychain} %^^A \item \mycs{cudefinekeys} % \item \mycs{cudefinesinglekey} % \item \mycs{cuaddtokeychain} %^^A \item \mycs{cuaddkeys} % \item \mycs{cuaddsinglekeys} %^^A \item \mycs{cuaddtokeys} % \end{itemize} % % % % \begin{function}{\cudefinekeychain, \cudefinesinglekey } % \begin{syntax} % \mycs{cudefinekeychain} % \ \ \{ % \ \ \ \ \marg{unit-key-1} \marg{value} % \ \ \ \ \marg{unit-key-2} \marg{... unit-key-2 are \meta{value} unit-key-1} % \ \ \ \ \marg{unit-key-3} \marg{... unit-key-3 are \meta{value} unit-key-1} % \ \ \ \ \dots % \ \ \} % % \mycs{cudefinesinglekey}\marg{unit-key-1} % \ \ \{ % \ \ \ \ \marg{unit-key-2} \marg{$1$ unit-key-2 are \dots\ unit-key-1} % \ \ \ \ \marg{unit-key-3} \marg{$1$ unit-key-3 are \dots\ unit-key-1} % \ \ \ \ \dots % \ \ \} % \end{syntax} % If you define new units (see \cref{sec:def-units}) and cannot add % them to already existing keys you may use % \mycs{cudefinekeychain} or \mycs{cudefinesinglekey} respectively to % define new key-chains or single keys. % % \mycs{cudefinekeychain} collects the unit-key's given and defines a key-chain. % This allows you to change every unit into every other unit given in the list. % So \meta{unit-key-1} can take \meta{unit-key-1}, \meta{unit-key-2}, \meta{unit-key-3}, % \dots\ as values; \meta{unit-key-2} can take \meta{unit-key-1}, \meta{unit-key-2}, % \meta{unit-key-3}, \dots\ as values, etc. Please note that \meta{\dots} has to % be a number. % % Sometimes it is not that easy and the conversion of one unit into % another needs are more complicated formula (see for example % temperatures). If that is the case use % \mycs{cudefinesinglekey}. As the name says it defines % \emph{only} a single key \meta{unit-key-1} with the values % \meta{unit-key-1}, \meta{unit-key-2}, etc. The advantage of this % command is that now \meta{\dots} can be a formula and the % numerical input of \mycs{cunum}, etc.\ can be placed explicitly using \texttt{\#1}. % \end{function} % % % % % \begin{cuexample} % This example defines following keys with their respective value: % \begin{itemize} % \item the key \myunit{kg} with the values \myunit{kg}, \myunit{dag}, % \myunit{g} and \myunit{oz} % \item the key \myunit{dag} with the values \myunit{kg}, % \myunit{dag}, \myunit{g} and \myunit{oz} % \item the key \myunit{g} with the values \myunit{kg}, \myunit{dag}, % \myunit{g} and \myunit{oz} % \item the key \myunit{oz} with the values \myunit{kg}, \myunit{dag}, % \myunit{g} and \myunit{oz} % \item \dots % \end{itemize} % \begin{align*} % \SI{1}{\kg}&= \SI{1}{\kg} & % \SI{1}{\kg}&= \SI{100}{\deka\g} & % \SI{1}{\kg}&= \SI{1000}{\g} \\ % \SI{1}{\kg}&= \SI{35.27399}{\ounce} & % \SI{1}{\kg}&= \SI{2.204 622 6}{\pound} % \end{align*} % \begin{lstlisting} % \cudefinekeychain % { % {kg} { 1 } % {dag}{ 100 } %% 1 kg are 100 dag % {g} { 1000 } %% 1 kg are 1000 g % {oz} { 35.27399 } %% 1 kg are 35.27399 oz % {lb} { 2.204 622 6 } %% 1 kg are 2.204 622 6 lb % } % % \cudefinekeychain % { % {d} { 1 } % {h} { 24 } %% 1 day are 24 hours % {min}{ 1440 } %% 1 day are 1440 minutes % {s} { 86400 } %% 1 day are 86400 seconds % } % \end{lstlisting} % \end{cuexample} % % \begin{cunote} % The value of the first item can be something different from $1$. % So something like this is also possible: % \begin{lstlisting} % \cudefinekeychain % { % {kg} { 0.4535924 } % {dag}{ 45.35924 } % {g} { 453,5924 } % {oz} { 16 } % {lb} { 1 } % } % \end{lstlisting} % \end{cunote} % % \begin{cuexample} % To convert degree Fahrenheit to degree Celsius, kelvin and degree R\'eamur one % needs the formulas\footnote{See Wikipedia.} % \begin{align*} % T_C &= ( T_F - 32 ) \cdot \frac{5}{9} \\ % T_K &= ( T_F - 459.67 ) \cdot \frac{5}{9} \\ % T_{Re} &= ( T_F - 32 ) \cdot \frac{4}{9} % \end{align*} % with $T_F$ being the input temperature in degree Fahrenheit and % $T_C$ being the same temperature in degree Celsius, etc. Using % \lstinline|\cudefinesinglekey| the key \myunit{F} with values % \myunit{C}, \myunit{K} and \myunit{Re} is defined by: % % \begin{lstlisting} % \cudefinesinglekey {F} % { % {C} { ( #1 - 32 ) * 5/9 } %% see formulas above % {K} { ( #1 + 459.67 ) * 5/9 } % {Re} { ( #1 - 32 ) * 4/9 } % } % \end{lstlisting} % % This defines the key \myunit{F} with the values \myunit{F}, \myunit{C}, % \myunit{K} and \myunit{Re}. % % \end{cuexample} % % % % \begin{function}{\cuaddtokeychain,\cuaddsinglekeys} % \begin{syntax} % \mycs{cuaddtokeychain} % \ \ \{ % \ \ \ \ \marg{unit-key-1} \marg{value} % \ \ \ \ \marg{unit-key-2} \marg{... unit-key-2 are \meta{value} unit-key-1} % \ \ \ \ \marg{unit-key-3} \marg{... unit-key-3 are \meta{value} unit-key-1} % \ \ \ \ \dots % \ \ \} % % \mycs{cuaddsinglekeys}\marg{unit-key-1} % \ \ \{ % \ \ \ \ \marg{unit-key-2} \marg{$1$ unit-key-2 are \dots\ unit-key-1} % \ \ \ \ \marg{unit-key-3} \marg{$1$ unit-key-3 are \dots\ unit-key-1} % \ \ \ \ \dots % \ \ \} % \end{syntax} % % \mycs{cuaddtokeychain} first parses through its unit-list and searches for % a base unit key which is already in a key-chain % (aka.\ was defined by \mycs{cudefinekeychain}). The other units, not yet % part of a key-chain, are added to the same key-chain as the base unit. % So the newly added units are available as a key and a value for the other % units in the same key-chain. Note that \meta{...} must be a number. % % % If the conversion is more complicated use % \lstinline|\cuaddsinglekeys|. It adds \meta{unit-key-2}, etc.\ as % values to \meta{unit-key-1}. The numerical input can be placed % using \myunit{\#1} (see \mycs{cudefinesinglekey}). This % command neither defines new keys nor does it add values to % keys other than \meta{unit-key-1}. % % % \end{function} % % % % % \begin{cuexample} % Suppose you are British (I am sorry, I can't think of another % reason to use those units) and you want to implement 'stone' (yes, % I was surprised myself that such a unit exists, but it even % appears in a Sherlock Holmes story). You exactly know that % \SI{1}{\st} equals \SI{14}{\lb}, well \dots\ now you have two % choices. \lstinline|\cuaddkeys| or \lstinline|\cuaddtokeys| (use % the one best fitting). This example uses the first, the next the % latter one. % \begin{lstlisting} % \newcookingunit{st} %% defining new unit 'stone' % \cuaddtokeychain % { % {lb} { 14 } %% unit already in a key-chain. % {st} { 1 } %% new unit. 1st = 14lb % } % \end{lstlisting} % \begin{cuexamplecode} % \cunum[lb=st]{1}{lb} \\ % \cunum[st=lb]{1}{st} \\ % \cunum[st=g]{1}{st} \\ % \cunum[st=kg]{1}{st} \\ % \cunum[kg=st]{1}{kg} \\ % \cunum[st=kg]{16}{st} % \end{cuexamplecode} % \end{cuexample} % % \begin{cunote} % Of course using % \begin{lstlisting} % \cuaddtokeychain % { % {st} { 1/14 } %% 1lb = 1/14st % {lb} { 1 } %% unit already in a key-chain. % } % \end{lstlisting} % is also possible % \end{cunote} % % \begin{cuexample} % Now you want to add degree R{\o}mer and convert Celsius to degree % R{\o}mer: % \begin{equation*} % T_{R\text\o} = T_C * \frac{21}{40} + 7.5 % \end{equation*} % % \begin{lstlisting} % %% defining new unit 'degree R{\o}mer' % \newcookingunit [\ensuremath{ {} ^ { \circ } }\kern-\scriptspace R{\o}] {Ro} % \cuaddsinglekeys {C} %% adds value 'Ro' to key 'C'. % { % {Ro} { #1 * 21/40 + 7.5 } % } % \cusetoptionfor{Ro}{ round-precision = 0 } %% round to integer automatically % \end{lstlisting} % \begin{cuexamplecode} % \cunum{10}{C} \\ % \cunum[C=Ro]{10}{C} % \end{cuexamplecode} % \end{cuexample} % % % % % % % % % % % % % % % % \subsection{Obsolete Commands} % % \begin{function}{cudefinekeys} % \begin{syntax} % \mycs{cudefinekeys}\marg{unit-key-1} % \ \ \{ % \ \ \ \ \marg{unit-key-2} \marg{$1$ unit-key-1 are \dots\ unit-key-2} % \ \ \ \ \marg{unit-key-3} \marg{$1$ unit-key-1 are \dots\ unit-key-3} % \ \ \ \ \marg{unit-key-4} \marg{$1$ unit-key-1 are \dots\ unit-key-4} % \ \ \ \ \dots % \ \ \} % \end{syntax} % % \emph{This command is going to be obsolete at one point. It is advised to use % \mycs{cudefinekeychain} instead.} % % \mycs{cudefinekeys} takes \marg{unit-key-1} as a % \enquote{basis}, defines a key with the name \meta{unit-key-1} and % adds the values \meta{unit-key-1}, \meta{unit-key-2}, % \meta{unit-key-3}, etc. Furthermore this command also defines the % keys \meta{unit-key-2}, \meta{unit-key-3}, etc. with the same % values as \meta{unit-key-1}. Please note that \meta{\dots} has to % be a number. % \end{function} % % % % \begin{function}{\cuaddkeys,\cuaddtokeys} % \begin{syntax} % \mycs{cuaddkeys}\marg{unit-key-1} % \ \ \{ % \ \ \ \ \marg{unit-key-2} \marg{$1$ unit-key-1 are \dots\ unit-key-2} % \ \ \ \ \marg{unit-key-3} \marg{$1$ unit-key-1 are \dots\ unit-key-3} % \ \ \ \ \marg{unit-key-4} \marg{$1$ unit-key-1 are \dots\ unit-key-4} % \ \ \ \ \dots % \ \ \} % % \mycs{cuaddtokeys} \marg{unit-key-1} \marg{unit-key-2} \marg{$1$ unit-key-2 are \dots\ unit-key-1} % \end{syntax} % % \emph{Those commands are going to be obsolete at one point. It is advised to use % \mycs{cuaddtokeychain} instead.} % % % \lstinline|\cuaddkeys| takes the already defined key % \marg{unit-key-1} as a \enquote{basis}, and adds % \meta{unit-key-2}, \meta{unit-key-3}, etc. to its % values. Furthermore it adds those new values to other keys linked % to \meta{unit-key-1} and defines the new keys \meta{unit-key-2}, % etc.\ with the same values as \meta{unit-key-1}. % % Works similar to \mycs{cuaddkeys} regarding the definition of keys. % \end{function} % % % % % % % % \section{Language support} % \label{sec:language} % % % Unit names and symbols depend on the language. To change the % name and symbol for given language you can use \lstinline|\cudefinename|; % to only change symbols use \lstinline|\cudefinesymbol|. % % \begin{function}{decimal-mark,cutext-range-phrase, one(m),one(f),one(n)} % Those are special keys (as they cannot be used as units). Not only % are printed units language depending, but as is the decimal mark % (|.| or |,|) and the text which substitutes the range-sign. % To set the decimal mark use % \opt{decimal-mark} (see examples below), to set the range-sign for \cCutext\ % use \opt{cutext-range-phrase}. % % Note that \opt{cutext-range-phrase} is \enquote{overwritten} by the \emph{option} % \opt{cutext-range-sign}. If the \emph{option} is set, then the language symbol % will be ignored. % % Furthermore if you are using numerals % you may also use the keys \opt{one(m)}, \opt{one(f)} and % \opt{one(n)}. Integers below a certain % value (see option \opt{use-numerals-below}) are written-out. The % problem is that the written-out \enquote{1} depends on the % gender of the word following (e.g. \enquote{ein Baum} (m), % \enquote{eine Pflanze} (f) and \enquote{ein Auto} (n)). Use those keys % to set the specific gender of \enquote{1} (see also examples below). % \end{function} % % \begin{function}{\cudefinename} % \begin{syntax} % \mycs{cudefinename}\marg{Language} % \ \ \{ % \ \ \ \ \marg{unit-key-1} \oarg{symbol-1} \marg{singular-1} \oarg{plural-1} \myaarg{gender} % \ \ \ \ \marg{unit-key-2} \oarg{symbol-2} \marg{singular-2} \oarg{plural-2} \myaarg{gender} % \ \ \ \ \dots % \ \ \} % \end{syntax} % % This command defines the names (and optionally the symbol) of the % units printed in \lstinline|\cutext| and \lstinline|\Cutext| % (and \lstinline|\cunum| regarding the symbol) for the specific % \meta{Language}. For details regarding \meta{language} see the % \pkg{translations} documentation. % % If the plural form of the name differs from the singular form use % \oarg{plural} to specify the plural form, else it will be equal to its singular % form. The singular form % is only used if the number in \lstinline|\cutext| and % \lstinline|\Cutext| is equal to $1$. % % \meta{gender} can be \texttt{m} (maskulin), \texttt{f} (feminin) or % \texttt{n} (neutrum). If not given, \texttt{m} is used as default. % \end{function} % % \begin{lstlisting} % \cudefinename {English} % { % {kg} {kilogramme} % {oz} {ounce} % {h} {hour} [hours] % {C} {degree\space Celsius} [degrees\space Celsius] % {decimal-marker} {.} % {cutext-range-phrase} {~to~} % {one(m)} {one} % {one(f)} {one} % {one(n)} {one} % } % \end{lstlisting} % \begin{lstlisting} % \cudefinename {German} % { % {kg} {Kilogramm} % {oz} {Unze} % {d} {Tag} [Tage] % {h} {Stunde} [Stunden] % {C} {Grad\space Celsius} % {decimal-marker} {,} % {cutext-range-phrase} {~bis~} % {one(m)} {ein} % {one(f)} {eine} % {one(n)} {ein} % } % \end{lstlisting} % % % \begin{function}{\cudefinesymbol} % % \begin{syntax} % \mycs{cudefinesymbol}\marg{Language} % \ \ \{ % \ \ \ \ \marg{unit-key-1} \marg{symbol-1} % \ \ \ \ \marg{unit-key-2} \marg{symbol-2} % \ \ \ \ \dots % \ \ \} % \end{syntax} % % This command defines the symbols of the units printed in % \lstinline|\cunum| for the specific \meta{Language}. It works % similar as \lstinline|\cudefinename|, but only the symbols (and no % names) can be set. For details regarding \meta{Language} see % the \pkg{translations} documentation. % % \end{function} % % % \begin{lstlisting} % \cudefinesymbol {English} % { % {decimal-mark} {.} % {cutext-range-phrase} {~to~} % {one(m)} {one} % {one(f)} {one} % {one(n)} {one} % } % \cudefinesymbol {German} % { % {decimal-mark} {,} % {cutext-range-phrase} {~bis~} % {one(m)} {ein} % {one(f)} {eine} % {one(n)} {ein} % } % \cudefinesymbol {French} % { % {l} {L} % {dl} {dL} % {cl} {cL} % {ml} {mL} % {cutext-range-phrase} {~\`{a}~} % {decimal-mark} {,} % {one(m)} {un} % {one(f)} {une} % {one(n)} {un} % } % \end{lstlisting} % % \begin{cuexample} % Imagine that instead of the abbreviation \enquote{dag} for \enquote{decagramme} % you want to use \enquote{ducks} (because \dots\ I don't know). You can easily % do this via % \begin{lstlisting} % \cudefinesymbol {English} % { % {dag} {ducks} % } % \end{lstlisting} % \cudefinesymbol {English} % { % {dag} {ducks} % } % As you can see it may be a bit suboptimal as there is no plural version allowed. % You do it anyway and end up with: % \begin{cuexamplecode} % \cunum{12}{dag} & weed\\ % \cunum{3}[0]{dag} & nuts\\ % \cunum{10}{dag} & duckmeat % \end{cuexamplecode} % % % \end{cuexample} % % % % % % % \subsection{Phrases} % \label{sec:phrases} % % Each language has synonyms for certain (integer) numbers. This package % supports those phrases and they can be implemented with the following % command to be used by \mycs{cuam}: % % \begin{function}{\cudefinephrase} % \begin{syntax} % \mycs{cudefinephrase}\marg{Language} % \ \ \{ % \ \marg{integer-1} \marg{phrase-1} \oarg{phrase-1-plural} \myaarg{gender-1} % \ \marg{integer-2} * \marg{phrase-2} \oarg{phrase-2-plural} \myaarg{gender-2} % \ \dots % \ \ \} % \end{syntax} % % This command pairs for a given \marg{Language} (see package \pkg{translations}) % the number \marg{integer-1} % with \marg{phrase-1} (\& \meta{phrase-1-plural} and \meta{gender-1}). % Afterwards the package can check if an amount % given in \mycs{cuam} is either this number or a \emph{multiple} of it. % % If the behavior of checking for a multiple is not wanted, you can use the optional % star \texttt{*}. % % \meta{gender} can be \opt{m}, \opt{f} or \opt{n}. It is \opt{m} by default. % % Afterwards the numbers are ordered from highest to lowest so that % the phrase with the highest number is used (if used at all). % % Furthermore, it chooses star (|*|) phrases over non-star phrases. % \end{function} % \begin{cuexample} % The following example creates some phrases for the language % \enquote{German}: % % \cudefinephrase {German} % { % { 60 } {Schock} % { 6 }* {halbes\ Dutzend} % } % % % \begin{lstlisting} % \cudefinephrase {German} % { % { 12 } {Dutzend} %% implemented by default % { 60 } {Schock} % { 6 }* {halbes\ Dutzend} % } % \end{lstlisting} % Let's just use them (german language activated!): %^^A \begin{myde} % \begin{cuexamplecode} % \selectlanguage{ngerman} @ % \cusetup{use-phrases=true} @ % \cuam{12} \\ % \cuam{24} \\ % \cuam{25} \\ % \cuam{60} \\ % \cuam{120} \\ % \cuam{121} \\ % \cuam{6} \\ % \cuam{18} % \end{cuexamplecode} %^^A \end{myde} % % As you can see, \enquote{Schock} ($60$) is preferred over \enquote{Dutzend} ($12$) % as it linked to the higher number. Furthermore, for $6$ the phrase \enquote{halbes % Dutzend} (half a dozen) is used, but because it is a star version it is \emph{not} % used for $18$. % % \end{cuexample} % % % \section{Options} % \label{sec:options} % % % Options in \pkg{cooking-units} can mostly be set globally using % \lstinline|\cusetup| or locally using the optional argument of the % respective command (but \emph{not} as a package option). The only % exception is the option given in \cref{sec:load-time-options} which % needs to be used as a package option. % % \begin{function}{\cusetup} % \begin{syntax} % \mycs{cusetup}\marg{options} % \end{syntax} % Options can be set using \mycs{cusetup}\Arg{options}. % \end{function} % % % \begin{function}{\cusetoptionfor,\cuaddoptionfor,\cuclearoptionfor} % \label{code:set-option} % \begin{syntax} % \mycs{cusetoptionfor}\marg{unit-list}\marg{options} % \mycs{cuaddoptionfor}\marg{unit-list}\marg{options} % \mycs{cuclearoptionfor}\marg{unit-list} % \end{syntax} % \pkg{cooking-units} allows you to attach options to units. % Those % options are activated if (and only if) the specific unit is used \emph{or} % if another unit is converted % into it. Those options allow you to e.g.\ round temperatures to % integers automatically. Furthermore, those added options are % overwritten by local options. % % \mycs{cusetoptionfor} \emph{sets} \meta{options} to each unit in \meta{unit-list} % overwriting the old ones. % % \mycs{cuaddoptionfor} \emph{add}s \meta{options} to each unit in \meta{unit-list}. % % \mycs{cuclearoptionfor} clears all options given to each unit in \meta{unit-list}. % \end{function} % \begin{cuexample} % Temperatures \myunit{C}, \myunit{F}, \myunit{K} and \myunit{Re} are % by default rounded to integers. % \begin{cuexamplecode} % \cunum{75.23}{C} \\ % \cunum{75.23}{F} \\ % \cunum{75.23}{K} \\ % \cunum{75.23}{Re} \\ % \cusetoptionfor{C,F,K,Re}{round-precision=-1} @ % \cunum{75.23}{C} \\ % \cunum{75.23}{F} \\ % \cunum{75.23}{K} \\ % \cunum{75.23}{Re} \\ % \cuclearoptionfor{C,F,K,Re} @ % \cunum{75.23}{C} \\ % \cunum{75.23}{F} \\ % \cunum{75.23}{K} \\ % \cunum{75.23}{Re} % \end{cuexamplecode} % \end{cuexample} % % \subsection{Load time options} % \label{sec:load-time-options} % \begin{function}{use-fmtcount-numerals} % \begin{syntax} % \cs{usepackage}[use-fmtcount-numerals=\meta{true/false}]\{cooking-units\} % \end{syntax} % If set to \true\ loads package \pkg{fmtcount} and uses % \cs{numberstringnum} for \mycs{cutext} % and \cs{Numberstringnum} for \mycs{Cutext} to write-out numbers below % \opt{use-numerals-below} ($13$ by default), integers above are % printed as numbers. % You can decide to not print any numerals by setting \opt{print-numerals} % to \false. % % Note: You don't need to use this function to use numerals. Using % \opt{print-numerals} and setting \opt{numeral-function} and \opt{Numeral-function} % also works. % \end{function} % % \cusetup{print-numerals=true} % \begin{cuexamplecode} % \cutext{1}{kg} \\ % \Cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \Cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \Cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \cutext{13}{kg} \\ % \Cutext{14}{kg} % \end{cuexamplecode} % \cusetup{print-numerals=false} % % \begin{cunote} % \opt{use-fmtcount-numerals} is a package option as it needs to load % \pkg{fmtcount} which is not loaded by default. % \end{cunote} % % \begin{cunote} % Please note the keys \opt{one(m)}, \opt{one(f)} % and \opt{one(n)} to change the printed \enquote{one} (as % \enquote{one} is in many languages dependent on the gender of the % following word. E.g in German: Masculine: ein Baum, % Feminin: eine Pflanze, Neutrum: ein Auto). % \end{cunote} % % \begin{cunote} % You can always change the functions used to print numerals with % \opt{numeral-function} and \opt{Numeral-function}. % \end{cunote} % % \subsection{Normal options} % % Options in this subsection can only be set as local options or using % \lstinline|\cusetup|, but \emph{not} as load time options. % % % \subsubsection{Unit Specific options} % \label{sec:opt:unit-spec} % % \begin{function}{} % \begin{syntax} % \meta{unit-key-1} = \meta{unit-key-2} % \end{syntax} % Change \meta{unit-key-1} to \meta{unit-key-2} (see % \cref{sec:defining-opt} to define new options). % \end{function} % % \begin{function}{} % \begin{syntax} % \meta{group} = \meta{unit-key} % \end{syntax} % Changes each unit contained in \meta{group} to \meta{unit-key} (\meta{unit-key} % must be part of \meta{group}). % % \begin{center} % \begin{tabular}{ll} % \toprule % \meta{group} & default \meta{unit-key}s \\ \midrule % \PrintGroup{weight} \\ % \PrintGroup{length} \\ % \PrintGroup{volume} \\ % \PrintGroup{temperature} \\ % \PrintGroup{energy} \\ % \PrintGroup{time} \\ \bottomrule % \end{tabular} % \end{center} % \end{function} % % \begin{cuexamplecode} % \cusetup{weight=g} @ % \cunum{1}{kg} \\ % \cunum{1}{dag} \\ % \cunum{1}{g} \\ % \cunum{1}{oz} \\ % \cunum{1}{lb} \\ % \cunum{1}{stick} \\ % \end{cuexamplecode} % % You can define new groups using \mycs{cudeclareunitgroup}: % \begin{function} {\cudeclareunitgroup} % \begin{syntax} % \mycs{cudeclareunitgroup} \marg{group-name} \marg{unit-list} % \end{syntax} % Defines the group \meta{group-name} containing the list \meta{unit-list}. % This allows the usage of \texttt{\meta{group-name}=\meta{unit-key}} to % change all units in the group \meta{group-name} to \meta{unit-key} % (which has to be part of \meta{unit-list}). % \end{function} % % \begin{cuexample} % Define the group \enquote{weight}: % \begin{lstlisting} % \cudeclareunitgroup {weight} { kg , dag, g, oz, lb, stick } % \end{lstlisting} % Now \lstinline|\cusetup{weight=dag}| can be used to change all units % contained in |weight| to |dag|. % \end{cuexample} % % % \begin{function}{\cuaddtounitgroup} % \begin{syntax} % \mycs{cuaddtounitgroup}\marg{group}\marg{unit-list} % \end{syntax} % Adds \meta{unit-list} to an already existing \meta{group} (both need to exist). % \end{function} % \begin{cuexample} % Adding \myunit{st} to the group \mygroup{weight} % \begin{cuexamplecode} % \cuaddtounitgroup {weight} {st} @ % \cusetup{weight = g } @ % \cunum{1}{kg} \\ % \cunum{1}{dag} \\ % \cunum{1}{g} \\ % \cunum{1}{oz} \\ % \cunum{1}{lb} \\ % \cunum{1}{stick} \\ % \cunum{1}{st} % \end{cuexamplecode} % \end{cuexample} % % % \begin{function}{add-unit-to-group} % \begin{syntax} % add-unit-to-group = % \ \{ % \ \meta{group1} = \marg{unit-key-list}, % \ \meta{group2} = \marg{unit-key-list}, % \ \dots % \ \} % \end{syntax} % \emph{This option is going to be obsolete at one point.} % Adds each \meta{unit-key} in \meta{unit-keys-list} to \meta{group}. % The key-val equivalent of \mycs{cuaddtounitgroup}. % \end{function} %^^A \begin{cuexample} %^^A The same as above: %^^A This example adds the unit \myunit{st} to the group \mygroup{weight} %^^A and \myunit{Ro} to \mygroup{temperature}. %^^A \begin{lstlisting} %^^A \cusetup %^^A { %^^A add-unit-to-group = { weight = {st} , temperature = {Ro} } %^^A } % ^^A\end{lstlisting} %^^A %^^A \cusetup{ add-unit-to-group = { weight = {st} } } %^^A %^^A \begin{cuexamplecode} %^^A \cusetup{weight=g} @ %^^A \cunum{1}{kg} \\ %^^A \cunum{1}{dag} \\ %^^A \cunum{1}{g} \\ %^^A \cunum{1}{oz} \\ %^^A \cunum{1}{lb} \\ %^^A \cunum{1}{stick} \\ %^^A \cunum{1}{st} %^^A \end{cuexamplecode} %^^A \end{cuexample} % % % % \begin{function}{set-option-for-,add-option-for-} % \begin{syntax} % set-option-for-\meta{unit-key} = \marg{ key1 = value1, \dots\ } % add-option-for-\meta{unit-key} = \marg{ key1 = value1, \dots\ } % \end{syntax} % \emph{This option is going to be obsolete at one point.} % Sets and adds \meta{key1=value1,\dots} to a specific % \meta{unit-key}, % \opt{erase-all-options} (see below) is used to erase all options for all % \meta{unit-key}s. % % The less flexible key-value version of \mycs{cusetoptionfor} and % \mycs{cuaddoptionfor}. % % %^^A You may want to attach some options to a special %^^A \meta{unit-key}. Those options are automatically activated if (and %^^A only if) the specific \meta{unit-key} is used (or changed into this %^^A unit). Setting options overwrites old options. Adding options, well %^^A \dots\ adds the options to the old ones. % %^^A You can \enquote{delete} the options by setting %^^A an empty value for a specific \meta{unit-key} (or use %^^A \opt{erase-all-options} (or \opt{erase-all-options-for} ) %^^A (see below) to erase all options for all \meta{unit-key}s) % % \end{function} % %^^A \begin{cuexample} %^^A The following rounds the values to integers for \myunit{F}, \myunit{C}, %^^A \myunit{K} and \myunit{Re}: %^^A \begin{lstlisting} %^^A \cusetup %^^A { %^^A set-option-for-F = { round-precision = 0 } , %^^A set-option-for-C = { round-precision = 0 } , %^^A set-option-for-K = { round-precision = 0 } , %^^A set-option-for-Re = { round-precision = 0 } %^^A } %^^A \end{lstlisting} %^^A although note that it would be easier to simply write %^^A \begin{lstlisting} %^^A \cusetoptionfor {F,C,K,Re} { round-precision = 0 } %^^A \end{lstlisting} %^^A \end{cuexample} % % % \begin{function}{set-option-for, add-option-for} % \begin{syntax} % set-option-for = % \ \{ % \ \meta{unit-key1} = \marg{keys=vals}, % \ \meta{unit-key2} = \marg{keys=vals}, % \ \dots % \ \} % add-option-for = % \ \{ % \ \meta{unit-key1} = \marg{keys=vals}, % \ \meta{unit-key2} = \marg{keys=vals}, % \ \dots % \ \} % \end{syntax} % \emph{This option is going to be obsolete at one point.} % Sets/adds each \meta{keys=vals} to the specific \meta{unit-key}. Works pretty much % the same way their \opt{set-option-for-\meta{unit-key}} and % \opt{add-option-for-\meta{unit-key}} counterparts. % % The less flexible versions of the commands \mycs{cusetoptionfor} and % \mycs{cuaddoptionfor}. % \end{function} % %^^A \begin{cuexample} %^^A The following example does the same as the example above: %^^A \begin{lstlisting} %^^A \cusetup %^^A { %^^A set-option-for = %^^A { %^^A F = { round-precision = 0 } , %^^A C = { round-precision = 0 } , %^^A K = { round-precision = 0 } , %^^A Re = { round-precision = 0 } %^^A } %^^A } %^^A \end{lstlisting} %^^A \end{cuexample} % % \begin{function}{erase-all-options, erase-all-options-for} % \begin{syntax} % erase-all-options % erase-all-options-for = \marg{unit-key1, unit-key2, \dots} % \end{syntax} % \emph{This option is going to be obsolete at one point.} % Erase options added to units. \opt{erase-all-options} erases all options % for \emph{all} \meta{unit-key}s. % %^^A If you only want to erase options for specific \meta{unit-key}s, you may %^^A use \opt{erase-all-options-for}. % % \opt{erase-all-options-for} is used to remove added options from % the specified \meta{unit-key}s (key-value version of \mycs{cuclearoptionfor}). % % \end{function} % %^^A \begin{cuexample} %^^A The following code erases all attached options from \myunit{C}, \myunit{F}, %^^A \myunit{K} and \myunit{Re}: %^^A %^^A \begin{lstlisting} %^^A \cusetup{ erase-all-options-for = {C, F, K, Re} } %^^A \end{lstlisting} %^^A It's the same as %^^A \begin{lstlisting} %^^A \cuclearoptionfor {C, F, K, Re} %^^A \end{lstlisting} %^^A \end{cuexample} % % % % \subsubsection{Command behavior} % % \begin{function}{cutext-to-cunum} % \begin{syntax} % cutext-to-cunum = \meta{true/false} % \end{syntax} % Want to get rid of all \cCutext? Set this option to \opt{true} and all % \cCutext\ are changed into \mycs{cunum}. % \end{function} % \begin{cuexamplecode} % \cutext{1}{kg} \\ % \Cutext{2}{kg} \\ % \cutext{1/2}{kg} \\ % \cutext{?}{kg} \\ % \cutext[kg=g]{1--2}{kg} \\ % \cusetup{cutext-to-cunum = true } @ % \cutext{1}{kg} \\ % \Cutext{2}{kg} \\ % \cutext{1/2}{kg} \\ % \cutext{?}{kg} \\ % \cutext[kg=g]{1--2}{kg} % \end{cuexamplecode} % % % \begin{function}{cutext-change-unit} % \begin{syntax} % cutext-change-unit = \meta{true/false} % \end{syntax} % Set this option to \opt{false} if you do \emph{not} want the units of \cCutext\ % to be changed. Set to \true\ by default % \end{function} % \begin{cuexamplecode} % \cutext[kg=g]{1}{kg} \\ % \cutext[kg=g]{1/2}{kg} \\ % \cutext[kg=g]{1--2}{kg} \\ % \cusetup{cutext-change-unit = false } @ % \cutext[kg=g]{1}{kg} \\ % \cutext[kg=g]{1/2}{kg} \\ % \cutext[kg=g]{1--2}{kg} % \end{cuexamplecode} % % \begin{function}{cuam-version,cutext-version} % \begin{syntax} % cuam-version = \meta{old/new} % cutext-version = \meta{old/new} % \end{syntax} % Since v1.10 this package also parses and checks the input of % \cCutext\ and \mycs{cuam}. If you want to restore the old % behavior, set this option to \opt{old}, but note that then you can % neither change the amounts for a given number of persons nor % change the unit of \cCutext. Both of them are set to \opt{new} by % default. % \end{function} % % % % % \subsubsection{Hooks} % % \begin{function}{commands-add-hook,cunum-add-hook,cutext-add-hook,Cutext-add-hook,cuam-add-hook} % \begin{syntax} % commands-add-hook = \marg{code} % cunum-add-hook = \marg{code} % cutext-add-hook = \marg{code} % Cutext-add-hook = \marg{code} % cuam-add-hook = \marg{code} % \end{syntax} % Adds \meta{code} to the respective command (or in case of the first key: % to \emph{all} commands). The hook is executed % \emph{after} setting the keys, but \emph{before} parsing and processing the input. % % Please be carful with spaces, they will be printed. % \end{function} % % \begin{cuexample} % You would like to count how often all commands of this package are used. % Simply add: % \begin{lstlisting} % \newcounter{CookingUnitsCounter} %% or however you like it % \cusetup{commands-add-hook={\stepcounter{CookingUnitsCounter}}} % %% beware of spaces inside the add-hook keys. % \end{lstlisting} % to your preamble. The following table lists how often each command % is used in this documentation (with help of \pkg{totalcount}): % % \begin{center} % \begin{tabular}{lr} % \toprule % command & times \\ \midrule % \mycs{cunum} & \totalcunumCounters \\ % \mycs{cutext} & \totalcutextCounters \\ % \mycs{Cutext} & \totalCutextCounters \\ % \mycs{cuam} & \totalcuamCounters \\ % \cmidrule{2-2} % total & \totalCookingUnitsCounters \\ % \bottomrule % \end{tabular} % \end{center} % \end{cuexample} % % \subsubsection{Input and Outputs} % % % \begin{function}{expand-both , expand-amount, expand-unit} % \begin{syntax} % expand-both = \meta{n/o/f/x} % expand-amount = \meta{n/o/f/x} % expand-unit = \meta{n/o/f/x} % \end{syntax} % By default the commands \mycs{cunum}, \cCutext{} and \mycs{cunum} % do \emph{not} expand their input. You can change the expansion behavior of % \meta{amount} and/or \meta{unit-key} using the options specified % above. The meaning of the available values are the same as specified % in the \LaTeX3\ document \enquote{interface3}. % % It is set to \texttt{n} (no expansion) by default. % % \end{function} % % % % % \begin{function}{set-special-sign,add-special-sign} % \begin{syntax} % set-special-sign = \marg{character(s)} % add-special-sign = \marg{character(s)} % \end{syntax} % Allows \meta{character(s)} to be used in the first mandatory argument of % \mycs{cunum}, \mycs{cuam}, \cCutext{} without raising an error (you can customize this % behavior, see \opt{set-unknown-message}). By default it is set to |?|. % Please note that the sign |<| is not allowed as a special sign. % \end{function} % % \begin{cuexamplecode} % \cunum{?}{kg} \\ % \cunum[g=kg]{10?--20?}{kg} \\ % \cusetup{add-special-sign={xX} } @ % \cunum{x}{kg} \\ % \cunum{X--?}{kg} \\ % \cusetup{set-special-sign={} } @ % \cunum{1}{kg} \\ % \cunum{1--2}{kg} % \end{cuexamplecode} % % \begin{function}{set-unknown-message} % \begin{syntax} % set-unknown-message = \meta{error/warning/none} % \end{syntax} % Using a special sign (\texttt{?} by default) causes a warning to % be raised. Set this option to \opt{error} if you want an error (as % an extra emphasis), \opt{warning} if you want a warning (default) % and \opt{none} if you don't want to know anything about it. % \end{function} % % \begin{function}{set-cutext-translation-message} % \begin{syntax} % set-cutext-translation-message = \meta{error/warning/none} % \end{syntax} % If a translation for \cCutext\ is not available for the language, % the commands are replaced % by \mycs{cunum}. Currently -- if this is happening -- a warning is shown, % you may change the behavior of the message (error, warning or not showing at all) % using this option. % \end{function} % % \begin{function}{print-numerals} % \begin{syntax} % print-numerals = \meta{true/false} % \end{syntax} % % Prints numerals for integers smaller than \opt{use-numerals-below} if % set to \true. If set to \false\ no numerals are printed. % % If you use the package option \opt{use-fmtcount-numerals} this option % is automatically set to \true. % % If you want to use another package, just set this option to \true\ and % use \opt{numeral-function} and \opt{Numeral-function}). % %^^A If the package option \opt{use-fmtcount-numerals} is set to \true\ you can %^^A deactivate the printing of numerals by setting \opt{print-numerals} to %^^A \false\ and activate them by setting it to \true. % % \end{function} % \begin{cuexample} % (Using the package option \opt{use-fmtcount-numerals}: % \cusetup{ print-numerals=true } % \begin{cuexamplecode} % \cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \cusetup{ print-numerals=false } @ % \cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \end{cuexamplecode} % \cusetup{ print-numerals=false } % \end{cuexample} % % \begin{function}{use-numerals-below} % \begin{syntax} % use-numerals-below = \meta{integer} % \end{syntax} % If \opt{print-numerals} is \true, prints the numerals in % \mycs{cutext} and \mycs{Cutext} for integers smaller than % \meta{integer}. \meta{integer} is by default 13. % You can deactivate the printing of numerals by \opt{print-numerals=false}. % \end{function} % \cusetup{ print-numerals=true } % \begin{cuexamplecode} % \cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \cusetup{ use-numerals-below=10 } @ % \cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \cusetup{ use-numerals-below=0 } @ % \cutext{1}{kg} \\ % \cutext{2}{kg} \\ % \cutext{12}{kg} \\ % \cutext{13}{kg} \\ % \cusetup{ use-numerals-below=12001 } @ % \cutext[kg=g]{1}{kg} \\ % \cutext[kg=g]{2}{kg} \\ % \cutext[kg=g]{12}{kg} \\ % \cutext[kg=g]{13}{kg} \\ % \end{cuexamplecode} % \cusetup{ print-numerals=false } % % \begin{function}{numeral-function, Numeral-function } % \begin{syntax} % numeral-function = \meta{function} % Numeral-function = \meta{function} % \end{syntax} % Sets the functions used for printing numerals. % \opt{numeral-function} is used for lowercase, \opt{Numeral-function} % for capitalized cases. % \end{function} % \begin{cuexample} % Using the commands from \pkg{fmtcount} you can set the % numeral function equal to % \begin{lstlisting} % \cusetup{ % numeral-function = \numberstringnum , % Numeral-function = \Numberstringnum % } % \end{lstlisting} % (this happens if you use the package option \opt{use-fmtcount-numerals}) % \end{cuexample} % % % % \begin{function}{parse-number} % \begin{syntax} % parse-number = \meta{true/false} % \end{syntax} % If set to \false\ prints the number of \lstinline!\cunum!, % \lstinline!\cutext!, \lstinline!\Cutext! and \mycs{cuam} as they % are (after some \dots\ well \dots\ parsing due to % \enquote{\lstinline|_|}). Is set to \true\ by default. % \end{function} % % \begin{cuexamplecode} % \cusetup{ parse-number=false } @ % \cunum[kg=g]{1}{kg} \\ % \cunum{1--2}{kg} \\ % \cunum{1----------2}{kg} \\ % \cunum{1.2}{kg} \\ % \cunum[kg=g]{1,2}{kg} \\ % \cunum{1/2}{kg} \\ % \cunum{1_2/3}{kg} \\ % \cunum{1/2_3}{kg} \\ % \cunum{some weird stuff}{kg} \\ % \cutext{1}{kg} \\ % \cutext{100}{kg} \\ % \cutext{gjfak}{kg} \\ % \cutext[kg=g]{12}{kg} \\ % \cuam{1----------2} \\ % \cuam{1,2} \\ % \cuam{1_1/2} \\ % \cuam{kwflk} \\ % \end{cuexamplecode} % % % % \begin{function}{range-sign, cunum-range-sign, cutext-range-sign} % \begin{syntax} % range-sign = \marg{string} % cunum-range-sign = \marg{string} % cutext-range-sign = \marg{string} % \end{syntax} % \opt{cunum-range-sign} sets the \emph{printed} range sign used in \mycs{cunum} % (and \mycs{cuam}) to \meta{string}, \opt{cutext-range-sign} sets the printed % range sign used in \cCutext{} to \meta{string}. % Using \opt{range-sign} sets the % range signs for both \mycs{cunum} (and \mycs{cuam}) and % \mycs{cutext}/\mycs{Cutext} to \meta{string}. % % The default for \meta{string} is \texttt{-{}-} (for both). % \end{function} % % Since version 1.45 there also exists the language symbol \opt{cutext-range-sign} % (see \cref{sec:language}). If the \emph{option} \opt{cutext-range-sign} is set % the language symbol will be ignored. % % \begin{cuexamplecode} % \cunum{1--2}{kg} \\ % \cuam{1--2} \\ % \cutext{1--2}{kg} \\ % \Cutext{1--2}{kg} % \end{cuexamplecode} % \begin{cuexamplecode} % \cusetup{cunum-range-sign={~to~}} @ % \cunum{1--2}{kg} \\ % \cuam{1--2} \\ % \cutext{1--2}{kg} \\ % \Cutext{1--2}{kg} % \end{cuexamplecode} % \begin{cuexamplecode} % \cusetup{cutext-range-sign={--}} @ % \cunum{1--2}{kg} \\ % \cuam{1--2} \\ % \cutext{1--2}{kg} \\ % \Cutext{1--2}{kg} % \end{cuexamplecode} % \begin{cuexamplecode} % \cusetup{range-sign={-to-}} @ % \cunum{1--2}{kg} \\ % \cuam{1--2} \\ % \cutext{1--2}{kg} \\ % \Cutext{1--2}{kg} % \end{cuexamplecode} % % \begin{function} {use-phrases} % \begin{syntax} % use-phrases = \meta{true/false} % \end{syntax} % Setting this option to \true\ replaces certain integers (see \cref{sec:phrases} for % more information) with their phrase counterpart. This option is set to \false\ by default. % \end{function} % % \begin{cuexample} %^^A \begin{myde} % For the German language: % \begin{cuexamplecode} % \selectlanguage{ngerman} @ % \cuam{12} \\ % \cuam{12--24} \\ % \cuam{36} \\ % \cusetup{use-phrases=true} @ % \cuam{12} \\ % \cuam{12--24} \\ % \cuam{36} \\ % \cusetup{use-phrases=true, print-numerals=true} @ % \cuam{12} \\ % \cuam{12--24} \\ % \cuam{36} \\ % \end{cuexamplecode} %^^A \end{myde} % \end{cuexample} % % % \subsubsection{Rounding options} % % \begin{function}{round-precision} % \begin{syntax} % round-precision = \meta{integer} % \end{syntax} % Rounds the amount automatically to \meta{integer} digits after the % colon. Note that units like \myunit{C}, \myunit{F}, \myunit{K} and % \myunit{Re} are still rounded to integers due to \mycs{cusetoptionfor}. % \end{function} % % % \begin{cuexamplecode} % \cusetup{round-precision= 5} @ % \cunum{1.23456789}{kg} \\ % \cunum[g=kg]{12.587}{g} \\ % \cunum{194}{kg} \\ % \cunum[C=F]{200--210}{C} \\ % \cunum[K=C]{0.0012}{K} \\ % \cusetup{round-precision= 1} @ % \cunum{1.23456789}{kg} \\ % \cunum{12.58}{kg} \\ % \cunum[g=kg]{194}{g} \\ % \cunum[C=F]{200--210}{C} \\ % \cunum[K=C]{0.0012}{K} % \end{cuexamplecode} % % \begin{cunote} % Negative numbers are also allowed. % \begin{cuexamplecode} % \cusetoptionfor{C,F}{round-precision=-1} @ % \cunum{-271,2}{C} \\ % \cunum[K=C]{0.0012}{K} \\ % \cunum{185}{C} \\ % \cunum[C=F]{180--200}{C} \\ % \end{cuexamplecode} % \end{cunote} % % \begin{function}{round-to-int} % \begin{syntax} % round-to-int = \meta{true/false} % \end{syntax} % \emph{This option is deprecated.} Rounds the amount to an integer if set \true. % Use \opt{round-precision=0} instead. % \end{function} % % %^^A \begin{cuexamplecode} %^^A \cusetup{round-to-int=true} @ %^^A \cunum{1.23456789}{kg} \\ %^^A \cunum{12.58}{kg} \\ %^^A \cunum[g=kg]{194--294}{g} \\ %^^A \cunum[kg=g]{1.23456789}{kg} %^^A \end{cuexamplecode} % % \begin{function}{round-half} % \begin{syntax} % round-half = \meta{default/commercial} % \end{syntax} % This option is only important for half-way numbers % (e.g. $0.005$). By setting it to \opt{default} the value will be % rounded to the nearest even number. Setting it to % \opt{commercial} rounds the value away from zero. % % It is set to \opt{default} by \dots\ default. % % \begin{cunote} % \opt{default} actually refers to the fact that it is the default % rounding algorithm used by \cs{fp\_eval:n \{ round( ) \}} % without a third argument. % \end{cunote} % % \end{function} % % % \begin{cuexamplecode} % \cusetup{round-half=default} @ % \cunum{0.005}{kg} \\ % \cunum{-0.005}{kg} \\ % \cunum{1.245}{kg} \\ % \cusetup{round-half=commercial} @ % \cunum{0.005}{kg} \\ % \cunum{-0.005}{kg} \\ % \cunum{1.245}{kg} @ % \end{cuexamplecode} % % % % \subsubsection{Fractions} % % \begin{function}{eval-fraction} % \begin{syntax} % eval-fraction = \meta{true/false} % \end{syntax} % This option takes \true\ or \false\ as values. % If set to \true\ all fractions are evaluated. Please note that divisions % through zero are not allowed. % \end{function} % % \begin{cuexamplecode} % \cusetup{eval-fraction=true} @ % \cunum{1/3}{kg} \\ % \cunum{1/2}{kg} \\ % \cunum[kg=g]{1/2}{kg} \\ % \cunum{1_1/2}{kg} \\ % \cunum[kg=g]{1_1/2}{kg} \\ % \cunum[kg=g]{-1_1/2}{kg} \\ % \cunum[kg=g]{1_2/?}{kg} \\ % \end{cuexamplecode} % % % % \begin{function}{convert-fraction} % \begin{syntax} % convert-fraction = \meta{true/false} % \end{syntax} % By default units of fractions are not converted into another unit. % Setting this option to \true\ % allows fractions to be evaluated when a change of units is requested % (and \emph{only} if a change of unit is requested). % \end{function} % % \begin{cuexamplecode} % \cusetup{convert-fraction=true} @ % \cunum{1/3}{kg} \\ % \cunum[kg=g]{1/3}{kg} \\ % \cunum{1_1/2}{kg} \\ % \cunum[kg=g]{1_1/2}{kg} \\ % \cunum[kg=g]{1_?/3}{kg} \\ % \end{cuexamplecode} % % % % % \begin{function}{fraction-command} % \begin{syntax} % fraction-command = \cs{command} % \end{syntax} % Sets the command used for printing fractions equal to \cs{command}. % \cs{command} has to take two arguments. By default % it is equal to |\sfrac| from \pkg{xfrac}. % % Please note that the amount is \emph{not} printed inside a math % environment by default. % \end{function} % % \begin{cuexamplecode} % \newcommand\myfrac[2]{#1/#2} @ % \cusetup{fraction-command=\myfrac} @ % \cuam{1/8} \\ % \cunum{1/2}{kg} \\ % \cunum{4/5}{C} \\ % \cunum{1_2/3}{kg} \\ % \cusetup{fraction-command=\nicefrac} @ % \cuam{1/8} \\ % \cunum{1/2}{kg} \\ % \cunum{4/5}{C} \\ % \cunum{1_2/3}{kg} % \end{cuexamplecode} % % \begin{function}{fraction-inline} % \begin{syntax} % fraction-inline = \marg{input containing \#1 and \#2} % \end{syntax} % Similar to \opt{fraction-command} only that you don't have to % define a command to alter the output of the fraction. % \end{function} % % \begin{cuexamplecode} % \cusetup{fraction-inline={#1/#2} } @ % \cuam{1/8} \\ % \cunum{1/2}{kg} \\ % \cunum{4/5}{C} \\ % \cunum{1_2/3}{kg} \\ % \cusetup{fraction-inline={\nicefrac{#2}{#1}} } @ % \cuam{1/8} \\ % \cunum{1/2}{kg} \\ % \cunum{4/5}{C} \\ % \cunum{1_2/3}{kg} % \end{cuexamplecode} % % \subsubsection{Spaces} % % \begin{function}{mixed-fraction-space} % \begin{syntax} % mixed-fraction-space = \meta{length} % \end{syntax} % Sets the length between the fraction and the number in a mixed-fraction, % default is \texttt{0.1em} (because I said so; if someone has some literature % or sources to look up the space, please let me know). % \end{function} % \begin{cuexamplecode} % \cuam{1_2/3} \\ % \cunum{1_2/3}{kg} \\ % \cunum{10_2/3}{kg} \\ % \cusetup{mixed-fraction-space=1em } @ % \cuam{1_2/3} \\ % \cunum{1_2/3}{kg} \\ % \cunum{10_2/3}{kg} \\ % \cusetup{mixed-fraction-space=0em } @ % \cuam{1_2/3} \\ % \cunum{1_2/3}{kg} \\ % \cunum{10_2/3}{kg} % \end{cuexamplecode} % % \begin{function}{cutext-space} % \begin{syntax} % cutext-space = \marg{string} % \end{syntax} % \meta{string} is inserted between the numeral part and the unit part % when using \cCutext. By default it is set an unbreakable space |~|. % \end{function} % \begin{cuexamplecode} % \cutext{1}{kg}\\ % \Cutext{10}{kg}\\ % \cusetup { cutext-space = \space } @ % \cutext{1}{kg}\\ % \Cutext{10}{kg}\\ % \cusetup { cutext-space = {} } @ % \cutext{1}{kg}\\ % \Cutext{10}{kg}\\ % \cusetup { cutext-space = {qwe} } @ % \cutext{1}{kg}\\ % \Cutext{10}{kg}\\ % \end{cuexamplecode} % % % \begin{function}{phrase-space} % \begin{syntax} % phrase-space = \marg{string} % \end{syntax} % \meta{string} is inserted between the numeral part and the phrase part % while using \mycs{cuam}. By default it is set to the unbreakable space |~|. Use this option % if you want to e.g.\ insert a normal space. % \end{function} %^^A \begin{myde} % \cusetup{use-phrases=true} % (Switching to german) % \begin{cuexamplecode} % \selectlanguage{ngerman} @ % \cuam{12}\\ % \cuam{144}\\ % \cusetup { phrase-space = \space } @ % \cuam{12}\\ % \cuam{144}\\ % \cusetup { phrase-space = {} } @ % \cuam{12}\\ % \cuam{144}\\ % \cusetup { phrase-space = {qwe} } @ % \cuam{12}\\ % \cuam{144}\\ % \end{cuexamplecode} % \cusetup{use-phrases=false} %^^A \end{myde} % % \begin{function}{amount-unit-space} % \begin{syntax} % amount-unit-space = \marg{string} % \end{syntax} % Change the spacing for \mycs{cunum} between the printed amount(s) and % the unit. The default value is |\thinspace|. % \end{function} % % \begin{cuexamplecode} % \cunum{1}{kg}\\ % \cunum{1/2}{kg}\\ % \cunum{1--2}{kg}\\ % \cusetup { amount-unit-space = {\hspace{1em}} } @ % \cunum{1}{kg}\\ % \cunum{1/2}{kg}\\ % \cunum{1--2}{kg}\\ % \cusetup { amount-unit-space = {} } @ % \cunum{1}{kg}\\ % \cunum{1/2}{kg}\\ % \cunum{1--2}{kg}\\ % \cusetup { amount-unit-space = {qwe} } @ % \cunum{1}{kg}\\ % \cunum{1/2}{kg}\\ % \cunum{1--2}{kg}\\ % \end{cuexamplecode} % % \subsubsection{label \& refs} % % \begin{function}{recalculate-amount} % \begin{syntax} % recalculate-amount = \meta{true/false} % \end{syntax} % Set this option to \opt{true} if you want to change your recipes to the given % number of people set by \opt{set-number-of-persons}. Note that only those % values who have a label are changed. % \end{function} % % \begin{function}{set-number-of-persons} % \begin{syntax} % set-number-of-persons = \meta{integer} % \end{syntax} % With this option you can determine the number of people your recipes % are for. Note that this option only has an effect on those who have a \meta{label} % given. It is set to \opt{4} by default. Please also note the use of % \opt{recalculate-amount}. % \end{function} % \begin{cuexamplecode} % \culabel{anotherrecipe}{2} @ % \curef{anotherrecipe}~persons \\ % \cunum{1}{kg}\\ % \cuam{1}\\ % \Cutext[ref=anotherrecipe]{10}{kg}\\ % & % \cusetup{ recalculate-amount = true } @ % \curef{anotherrecipe}~persons \\ % \cunum{1}{kg}\\ % \cuam{1}\\ % \Cutext[ref=anotherrecipe]{10}{kg}\\ % & % \cusetup { set-number-of-persons = 3 } @ % \curef{anotherrecipe}~persons \\ % \cunum{1}{kg} \\ % \cuam{1} \\ % \Cutext[ref=anotherrecipe]{10}{kg}\\ % & % \cusetup { set-number-of-persons = 2 } @ % \curef{anotherrecipe}~persons \\ % \cunum{1}{kg}\\ % \cuam{1}\\ % \cutext{10}{kg}\\ % & % \cusetup { set-number-of-persons = 1 } @ % \curef{anotherrecipe}~persons \\ % \cunum{1}{kg}\\ % \cuam{1}\\ % \Cutext[ref=anotherrecipe]{10}{kg}\\ % \end{cuexamplecode} % % % \begin{function}{label} % \begin{syntax} % label = \{\meta{string}*\meta{integer}\} % \end{syntax} % The key-value version of \mycs{culabel}. It defines the label \meta{string} % which is originally for \meta{integer} people. Please note that the \texttt{*} % is mandatory as it separates the string from the integer. Each % label is defined globally and must be unique. % \end{function} % \begin{cuexamplecode} % \cusetup{label=Toast*1} @ % \curef{Toast}~person \\ % \cuam{2} \\ % \cunum{2}{dag} \\ % \cusetup { recalculate-amount = true } @ % \curef{Toast}~persons \\ % \cuam{2} \\ % \cunum{2}{dag} % \end{cuexamplecode} % % % \begin{function}{get-label} % \begin{syntax} % get-label = \marg{label} % \end{syntax} % The key-value version of \mycs{curef}. Note that this key doesn't save the value % inside a macro but rather prints it directly into the document. % \end{function} % \begin{cuexamplecode} % \culabel{Schinken}{3} @ % \cusetup{get-label=Schinken} \\ % \curef{Schinken} \\ % \cusetup { recalculate-amount = true } @ % \cusetup{get-label=Schinken} \\ % \curef{Schinken} % \end{cuexamplecode} % % \begin{cunote} % \mycs{curef} \emph{is} expendable. % \end{cunote} % % % \begin{function}{ref} % \begin{syntax} % ref = \marg{label} % \end{syntax} % Instead of using the first optional arguments of the commands in \cref{sec:commands} % you may use this option. It requires a valid value and throws an error if \meta{label} % is not defined. % \end{function} % \begin{cuexamplecode} % \culabel{Kaese}{3} @ % \cunum [m=dm]{1}{m} \\ % \cunum[ref=Kaese,m=dm]{1}{m} \\ % \cusetup { recalculate-amount = true } @ % \cunum[m=dm]{1}{m} \\ % \cunum[ref=Kaese,m=dm]{1}{m} % \end{cuexamplecode} % % % % \begin{function}{curef-add-forbidden-unit, curef-remove-forbidden-unit,curef-clear-forbidden-units} % \begin{syntax} % curef-add-forbidden-unit = \marg{unit list} % curef-remove-forbidden-unit = \marg{unit list} % curef-clear-forbidden-units = \meta{true/false} % \end{syntax} %^^A How much of a specify ingredient is used generally depends on the number of folks the meal %^^A is for. Change the number of folks and the needed weight of an ingredient will most probably %^^A change, \emph{but} there are units which do not change automatically if you have $2$ instead %^^A of $4$ guests. Units which measure the temperature are for example one of them. So changing %^^A them using the label \& ref system would be accidental and should in the best case create an error. %^^A Use the given options to define units which will throw an error if they are about to be changed %^^A by the label \& ref system. Of course, you can also remove units or clear the whole list. % % There are units which do not depend on the number of folks you are cooking for, units measuring % the temperature are an example. Changing those units with the label \& ref % system would be accidental and in the best case throw an error. With the following options % you can add units to the \enquote{forbidden unit list}, remove them and clear the whole list % entirely. % % By default the list contains \texttt{C}, \texttt{F}, \texttt{K} and \texttt{Re}. % \end{function} % \begingroup % \DeactivateForbiddenUnitError % \begin{cuexamplecode} % \culabel{check}{2} @ % \cusetup{ recalculate-amount=true } @ % \cunum{1}{m} \\ % \cunum{1}{kg} \\ % \cunum[ref=check]{1}{C} \\ % \cusetup { curef-add-forbidden-unit = { m, kg } } @ % \cunum[m]{1}{m} \\ % \cunum[m]{1}{kg} \\ % \cunum[ref=check]{1}{C} \\ % \cusetup { curef-remove-forbidden-unit = { C } } @ % \cunum[m]{1}{m} \\ % \cunum[m]{1}{kg} \\ % \cunum[ref=check]{1}{C} \\ % \cusetup { curef-clear-forbidden-units = true } @ % \cunum[m]{1}{m} \\ % \cunum[m]{1}{kg} \\ % \cunum[ref=check]{1}{C} % \end{cuexamplecode} % \endgroup % % % % % % % % % % % % % % % % \subsection{Weird options} % % \begin{function}{check-temperature} % \begin{syntax} % check-temperature = \meta{true/false} % \end{syntax} % Checks if the used temperature is below absolute zero. % Currently \myunit{C}, \myunit{F}, \myunit{K} and \myunit{Re} are supported. % While \lstinline|\cunum{0}{K}| is ok, \lstinline!\cunum{-1}{K}! raises an error, % same for the others. Is set to \false\ by default. To add new units % see \opt{add-temperature-to-check}. % \end{function} % % \begin{function}{add-temperature-to-check} % \begin{syntax} % add-temperature-to-check = % \ \ \{ % \ \ \ \ \meta{unit-key-1} = \meta{minimum-value-1} , % \ \ \ \ \meta{unit-key-2} = \meta{minimum-value-2} , % \ \ \ \ \dots % \ \ \} % \end{syntax} % This option adds \meta{unit-key-1} and so on to the list of units to be checked % if \opt{check-temperature} is active. The argument can be a comma-separated % list of \meta{unit-key} = \meta{minimum-value}. This sets the allowed minimum % value of \meta{unit-key} to \meta{minimum-value}. % % \end{function} % % \begin{cuexample} % This package implements the allowed minimum values for the % temperatures \myunit{C}, \myunit{F}, \myunit{K} and \myunit{Re} to be checked if % \opt{check-temperature} is active using: % \begin{lstlisting} % \cusetup % { % add-temperature-to-check = % { % K = 0, % C = -273.15 , % F = -459.67 , % Re = -218.52 % } % } % \end{lstlisting} % % If you want to add a new value, for example degree R{\o}mer % (which has be defined in another example) you can write: % \begin{lstlisting} % \cusetup % { % add-temperature-to-check = { Ro = -135.90375 } % } % \end{lstlisting} % \end{cuexample} % % % % % \begin{function}{convert-to-eV} % \begin{syntax} % convert-to-eV = \meta{true/false} % \end{syntax} % Converts (nearly) every unit in \cref{tab:def-units} % to electron volt or the respective derivative (if possible). Note that this % option is: % a) experimental and probably will forever be and (b) just a joke, you are not % supposed to use this units in a cookery book (and as you see this package % doesn't support the arrangement of such huge numbers). Also you may % want to check the values if you really want to use them, just to be sure % (I've checked them several times and hope they are finally correct, but mistakes % happen). % % \end{function} % % \begin{cuexamplecode}[\hfill] % \cusetup{ convert-to-eV=true } @ % \cunum{1}{kg} \\ % \cunum{1}{l} \\ % \cunum{1}{J} \\ % \cunum{1}{m} \\ % \cunum{1}{C} \\ % \cunum{1}{s} % \end{cuexamplecode} % % % \begin{function}{add-natural-unit} % \begin{syntax} % add-natural-unit = \meta{unit-key} % \end{syntax} % This option adds \meta{unit-key} to the list of units \opt{convert-to-eV} % uses to determine how a unit is transformed if set to \true. % \end{function} % % % % \begin{function}{42} % \begin{syntax} % 42 = \meta{true/false} % \end{syntax} % Take a good guess. % \end{function} % % \begin{cuexamplecode}[\hfill] % \cusetup{ 42=true } @ % \cunum{1}{kg} \\ % \cunum[kg=g]{1}{kg} \\ % \cunum{1.5}{J} \\ % \cunum{180}{C} \\ % \cunum{15/4}{s} \\ % \cunum{1--2}{min} \\ % \cunum{1x2x3}{min} \\ % \cunum{?}{l} % \end{cuexamplecode} % % % \begin{function}{nothing-special, going-bonkers, fully-bonkers, xD-lol} % \begin{syntax} % nothing-special = \meta{true/false} % going-bonkers = \meta{true/false} % fully-bonkers = \meta{true/false} % xD-lol = \meta{true/false} % \end{syntax} % Options that do \dots\ stuff. The four stages of madness in option for. % % \begin{description} % \item[nothing-special] is your default. The package behaves as intended. % % \item[going-bonkers] is a bit more strange. It converts an unit into another random unit % (if it can) and does so throughout the document. So if |unit-A| is converted % into |unit-B|, it is going to be converted this way the entire document through. % For an unit to be converted it must have a key, see \cref{sec:defining-opt}. % % % \item[fully-bonkers] converts one unit into another random unit (if it can) and does so for % each unit it encounters. So |unit-A| might be converted into |unit-B| the first time, % but |unit-C| the second. Each conversion picks a random unit for the conversion % (but the conversion itself makes sense, e.g.\ |kg| into |g|, but not into |cm|). % % \item[xD-lol] is pure insanity. A unit is transformed into another, if it makes sense % or not, and its value is replaced by a random number. % \end{description} % \end{function} % % % % % % % \section{Public Commands} % % This section describes some public functions, Their main usage is the printing of % stuff. They are primtivies used between an \mycs{ExplSyntaxOn} and % \mycs{ExplSyntaxOff}; for usage in a document you may do the following: % \begin{lstlisting} % \ExplSyntaxOn % \NewDocumentCommand \cuprintfrac { O{} m m } % { % \cookingunits_print_fraction:nnn {#1} {#2} {#3} % } % \NewDocumentCommand \cuprintrange { m m } % { % \cookingunits_print_range:nn {#1} {#2} % } % \ExplSyntaxOff % \end{lstlisting} % % % \begin{function}{\cookingunits_print_fraction:nnn} % \begin{syntax} % \mycs{cookingunits_print_fraction:nnn} \marg{mixed-part}\marg{numerator}\marg{denominator} % \end{syntax} % \emph{Note:} There is no parsing and processing done in this command, it just % prints the input. % % Uses the internal fraction printing command to print a fraction. \Arg{mixed-part} % can be empty if no mixed part is needed. % \end{function} % % % \begin{function}{\cookingunits_print_range:nn} % \begin{syntax} % \mycs{cookingunits_print_range:nn} \marg{left-part}\marg{right-part} % \end{syntax} % \emph{Note:} There is no parsing and processing done in this command, ist just % prints the input. % % Uses the internal range printing command to print a range (using the same range sign as % \mycs{cunum} would use) % \end{function} % % %^^A \section{Notes} % %^^A There is a unit called \enquote{Batman}. % % % % % % % % % % % % % % \section{Bugs \& Feedback} % \label{sec:bug-feed} % % Bug reports are always welcome. If you are sending a bug report % please include a minimal working example showing the bug and a short % description. If you use mail please add \pkg{cooking-units} % to the e-mail header. GMX has the habit of putting e-mails into the % spam account and adding \pkg{cooking-units} to the header makes % it easier to recognize those e-mails. It can also take longer of GitHub, % but I hope I figured out how to get a mail if a new issue is created (by not me). % % Feedback and requests (commands, units, etc.) are also welcome. Please % also add (if possible) an example of the desired output into the % minimal example (and -- if by mail -- add \pkg{cooking-units} to the % header). % % Furthermore, as you can see I am not able to speak too many % languages (german and english to be precise) so if you are % able to speak a language not yet implemented and would like to help % you can send me the translations known to you. A list of all units % (and their current translations) is given in \cref{sec:Translations}. % % % % % %\clearpage % % \section{Bens Einheitensammelsurium (Bens unit Almanac)} % \label{sec:unit-almanac} % % Units are a fascinating mess. There are so many different ones which are different and % the few ones which are the same (in name at least) are \emph{also} different, depending on % geographical position, time period and probably pure spite. We can be glad that SI-units exist. % % %^^A Units are a fascinating mess. There are so many different ones that one can be glad %^^A that SI-units exist in this world. This section offers some units which didn't manage %^^A to appear in \cref{tab:def-units} and \cref{tab:some-more-units}.^^A\footnote{This somehow %^^A ^^A reminds me of the story of \enquote{Tower of Babel} in the bible. This tower is -- according to the %^^A ^^A story -- the reason (or the symptom) of why people speak different languages. %^^A ^^A I suppose what didn't make it into the story was that as even more severe punishment, %^^A ^^A different units were created. Just my take at lest.}. % % So for those units which didn't make it into \cref{tab:def-units} and \cref{tab:some-more-units}, % this section exists. Please note that this list is intended to be a just-for-fun list and not a % compilation of every unit in existence with its exact value ordered by geographical and chronological % position. I am sadly neither a historian nor very good in regards to languages. It would sound like % fun, but ultimately, I wouldn't have the time. % Therefore I am only taking units into account which I either found in literature (stone, canna, etc.), % are well known (foot) or have some other experience with them (ell) (exception: Batman). The reason I % am not including units which I found in the internet is that I would like to % see those units in their \enquote{natural environment}. % % % \begin{description} % % \unititem{unit}*in given language*[translation][abbreviation] Description, containing a % quote or not. \emph{Please note that most of the units are country dependent! % So the translation may not have the same amount as the word it is translated to.} % % \unititem{Batman} % So \dots\ You wanna be Batman? Be like Bruce Wayne? Having a secret identity? % Then congratulations! You \emph{are} Batman! How much Batman depends on the location, % but Wikipedia is your friend in this matter. % % % \unititem{Rotolo}*sic*[Rottel\txtupde] Around \SI{0.850}{\kg} % \begin{myquote}[Gattopardo][51] % Auf den Fußboden lagen vier ungereifte Käse zu je \emph{zwölf Rottel}, % jeder ungefähr zehn Kilo schwer. % \end{myquote} % % \unititem{Canna}*sic*[Rute\txtupde, rod\txtupen] About \SI{2}{\m} bzw.\ about $6$ foot. % \begin{myquote}[Gattopardo][25] % \enquote{Unsinn, Stella, Unsinn; was soll mir zustoßen? Sie kennen mich alle: % Männer, die \emph{eine Rute} lange sind, gibt es wenige in Palermo.} % \end{myquote} % % \unititem{Stone}[][st] \cunum[st=kg]{1}{st}. According to a fellow student % this unit is still used in % Great Britain. I've also recently found it in a video game; in the german translation % of said video game to be precise. Why is the german translation using stone % and not kilogram (at least in braces)? % % \begin{myquote}[Sh:3][988] % As we had expected, the telegramm was soon followed by its sender, and the card of Mr.\ Cyril Overton, Trinity College, Cambridge, announced the arrival of an enormous young man, \emph{sixteen stone} \emph{[\cunum[st=kg]{16}{st}]} of solid bone and muscle, who spanned the doorway with his broad shoulders \emph{[\dots\!]} % \end{myquote} (Story \enquote{The missing Three Quarters}) % % \unititem{Foot}[][ft] Equals exactly \cunum[ft=m,round-precision=10]{1}{ft} % or \cunum[ft=in]{1}{ft}. % % A bit of a strange unit (for me at least). Where I am from, people % tend to have different feet sizes. Also present in the german translation of the % video game that uses \enquote{Stone}. % % \unititem{degree Rèamur}[][\ensuremath{{}^{\circ}}\kern-\scriptspace R\'{e}] Like degree Celsius, but instead of having the % water boiling at \SI{100}{\degree} (Celsius), water boils at \SI{80}{\degree}. % Water thankfully still freezes at \SI{0}{\degree}. Don't think that this unit % is used anymore. I think I learned about in physics. % % \unititem{Ell} Just read the Wikipedia article. % % Fun Fact: At the Stephansdom % in Vienna left of the main entrance are two metal bars. One is the % \enquote{Tuchelle} (drapery ell, circa \SI{78}{\cm}), the other the % \enquote{Leinenelle} (linen ell, around \SI{89.6}{\cm}). % % \unititem{cup} I think the idea of having a \enquote{cup} and it not being % equal to \cunum{250}{ml} is a bit strange, for me at least. What other sizes can a % cup have? I can imagine \cunum{500}{ml}, but are there other sizes? % ^^A Any person who writes \enquote{cup} and % ^^A \emph{doesn't} mean % ^^A a cup equal to \SI{250}{\ml} should be %^^A^^A -- in my humble opinion\footnote{Which, now that I admitted %^^A^^A my humbleness, is automatically %^^A^^A more important than yours.} -- %^^A excommunicated by default (or incommunicated, %^^A if you are an atheist). % % \unititem{stick} A unit I've made fun of because it is quite regional and doesn't make % any sense for foreigners. Then I realized that I am using the unit \enquote{Packerl} % in my cookery book which is also quite locally\footnote{And maybe doesn't even exist % outside my family} and -- even worse -- the weight changes depending % the content (See \emph{Packerl}). % % \unititem{Packerl}*de*[small bag] I'm a bit split on this unit as I don't actually % know if it exists. The reason I have the unit \emph{Packerl} for my cookery % book is that in Austria you can buy baking powder, (dry) Germ, Natrium, etc. % in small bags (similar to \emph{stick}). The problem: Depending on the content, % the weight of \emph{Packerl} differs. Not only that, but it can also differ % between different producers (but not more than \SI{2}{\g} bzw.\ \cunum[g=oz]{2}{g}). % Here is a table: % % \begin{tabular}{l @{\space} l @{\space} l r l} % % 1 Packerl & Backpulver & (baking powder) & \cunum{16}{g} & (\cunum[g=oz]{16}{g}) \\ % & Natrium & & \cunum{14}{g} & (\cunum[g=oz]{14}{g}) \\ % & Vanillin(-zucker) & (vanillin(-sugar)) & \cunum{8}{g} & (\cunum[g=oz]{8}{g}) \\ % & Germ* & & \cunum{7}{g} & (\cunum[g=oz]{7}{g}) \\ % \end{tabular} % % {\footnotesize *Tockengerm (dry Germ) to be precise} % % For what kind of thing do I need \emph{Natrium} for? % \end{description} % % % % % % % % \appendix % % \section{Translations} % \label{sec:Translations} % This section contains the list of available translations. Each table % shows the available translations regarding the unit symbol, the unit name % (printed if \mycs{cutext} or \mycs{Cutext} is used) and % the plural form (if different from the singular form). A second table % shows the translations used for phrases (if given). % % If a translation is not available a \enquote{\translationdoesnotexisttl} is % shown. % % \small % % %\clearpage % \subsection{English} % \begin{tabular}{ l l l l l } % \toprule % \CreateTableForLanguages{ English } % \end{tabular} % % %\clearpage % \subsection{american} % % \begin{tabular}{ l l l l l } % \toprule % \CreateTableForLanguages{ american } % \end{tabular} % %\clearpage % \subsection{German} % \begin{tabular}{ l l l l l } % \toprule % \CreateTableForLanguages{ German } % \end{tabular} % % \bigskip % % \CreateTableForPhrases { German } % % \bigskip % \begin{minipage}{\textwidth} % Some further phrases, just to write them down (they are not implemented, % as they are barely used). % % \begin{tabular} { l l @{\space} l l l l } % \toprule % \meta{number} & name & \multicolumn{2}{c}{Note} & (plural) & gender \\ \midrule % 60 & Schock & (5 Dutzend, & $12*5$) & & n \\ % 144 & Gros & (12 Dutzend, & $12*12$) && n \\ % 1728 & Gro{\ss}gros &(12 Gro{\ss}, & $12*144$) && n \\ % \bottomrule % \end{tabular} % \end{minipage} % % Note that Gro{\ss}gros has other (probably more common) synonyms. % %\clearpage % \subsection{French} % \begin{tabular}{ l l l l l } % \toprule % \CreateTableForLanguages{ French } % \end{tabular} % % If the spoons should be extra full: % \begin{itemize} % \item cuill\`{e}re \`{a} soupe rase % \item cuill\`{e}re \`{a} caf\'{e} rase % \end{itemize} % %\clearpage % % % % % % \normalsize % % % \section{US, Imperial and Other units} % \label{sec:other-units} % % As source \cite{WM1985} has been used for imperial units, while \cite{Nist2006} % and \cite{Nist2008} were used for U.S.\ units. I hope someone will find this % bringing together useful. % %\begin{center} %\begin{tabular}{ l @{ } l } %\toprule % 1 yard &= \SI{0.9144}{\m} (exact) \\ % 1 yard &= 3 foot \\ % 1 yard &= 36 Inch \\ \midrule % 1 Inch &= \SI{0.0254}{\m} (also exact) \\ % \bottomrule %\end{tabular} %\end{center} % %\begin{center} %\begin{tabular}{r@{ }l r@{ }l} %\toprule % \multicolumn{4}{c}{ 1 liter = \SI{1}{\cubic\deci\meter} } \\ \midrule % 1 gallon &= \SI{4.54609}{liter} (exact) & 1 U.S.\ gallon &= 231\,Inch$^3$ $=$ % $231\times \num{0,016387064}$ liter \\ % 1 gallon &= 4 Quart & 1 U.S.\ gallon &= 4 Quart\US\\ % 1 gallon &= 8 Pint & 1 U.S.\ gallon &= 8 Pint\US\\ % 1 gallon &= 32 Gill & 1 U.S.\ gallon &= 32 Gill\US\\ % 1 gallon &= 160\,fl.\,oz & 1 U.S.\ gallon &= 128\,fl.\,oz\US \\\midrule % 1 \,fl.\,oz&= \num{0.0284130625} liter & 1\,fl.\,oz\US & = \num{0.0295735295625} liter % \\ \bottomrule %\end{tabular}\\ \footnotesize\raggedright %\begin{cuenumnote} % \item I think the American fl.\,oz\US\ is more common. Maybe. Most bottles % have something like 10\,fl.\,oz, which they say is equal to \SI{30}{\ml}. This would % work really well with fl.\,oz\US. % \item Sometimes \enquote{fl.\,oz} is written without the dot. I am also not sure what % kind of spacing has to be between \enquote{fl.} and \enquote{oz} (currently % using \cs{thinspace}). % \item This maybe sounds stupid, but could we introduce something like % \enquote{flouz}, \enquote{floiz} and \enquote{floez}? \enquote{flouz} would be % \enquote{fl.\,oz\US}, \enquote{floiz} would be \enquote{Imperial fl.\,oz} and % \enquote{floez} would simply be equal to \SI{30}{\ml}? %\end{cuenumnote} %\end{center} % % % For \enquote{stick} see \cite{StickSource}. %\begin{center} %\begin{tabular} { l @{ } l } % \toprule % 1\,lb &= \SI{0.453 592 37}{\kg} (exact) \\ % 1\,lb &= 16\,oz \\ % 1\,lb &= \sfrac{1}{14}\,st \\ % 1\,lb &= \sfrac{175}{12}\,ounce~troy \\ % 1\,lb &= 4 stick \\ % \bottomrule %\end{tabular} %\end{center} % % % %\begin{center} %\begin{tabular} { r @{ } l @{ }l r @{ } l } % \toprule % 1 cup &$\approx$ \num{0.25} litre &= \SI{250}{\ml} &1 cup\US &= 8\, fl.\,oz\US \\ % 1 tablespoon &$\approx$ \num{0.015} litre&= \SI{15}{\ml} & 1 tablespoon\US &= \sfrac{1}{2}\, fl.\,oz\US \\ % 1 teaspoon &$\approx$ \num{0.005} litre &= \SI{5}{\ml} & 1 teaspoon\US&= \sfrac{1}{6}\, fl.\,oz\US \\ % \bottomrule %\end{tabular} \footnotesize \\ \raggedright %\begin{cuenumnote} % \item I tested the approximation for tablespoon with water % ($\SI{1}{\mg} \approx \SI{1}{\mg}$) and the approximation looks good enough. % It of course depends on how full you fill your spoon. %\end{cuenumnote} %\end{center} % % % If you ever encounter in a german cookery book the word \enquote{Packerl}, % check out its entry in \cref{sec:unit-almanac}. % % % % % % % % % % % % % % % % % % % % % % % % % % % % %\end{documentation} % % % % % % % % % % % \begin{implementation} % % \section{Implementation} % % \subsection{Beginning} % % \begin{macrocode} %<@@=cookingunits> % \end{macrocode} % % \begin{macrocode} %<*package> % \end{macrocode} % % % % Na dann, auf gehts! % % \begin{macrocode} \@ifpackageloaded {xparse} { } { \RequirePackage {xparse} } \@ifpackageloaded {expl3} { } { \RequirePackage {expl3} } % \end{macrocode} % %: Package % \begin{macrocode} \ProvidesExplPackage {cooking-units} {2022/12/10} {3.00} {Ein Paket fuer Kocheinheiten} % \end{macrocode} % % Checking if \pkg{expl3} is up-to-date, otherwise abort the loading of the package. % \begin{macrocode} \@ifpackagelater { expl3 } { 2022-09-28 } { } { \PackageError { cooking-units } { Support~package~expl3~too~old } { You~need~to~update~your~installation~of~the~bundles~'l3kernel'~and~ 'l3packages'.\MessageBreak Loading~cooking-units~will~abort! } \tex_endinput:D } % \end{macrocode} % Loading some needed packages. % \begin{macrocode} \@ifpackageloaded { translations } { } { \RequirePackage { translations } } \@ifpackageloaded { xfrac } { } { \RequirePackage { xfrac } } \@ifpackageloaded { l3keys2e } { } { \RequirePackage { l3keys2e } } % \end{macrocode} % % Checking if \pkg{translations} is up-to-date, otherwise abort the loading of the package. % \begin{macrocode} \@ifpackagelater { translations } { 2022/02/05 } { } { \PackageError { cooking-units } { Support~package~translations~too~old } { You~need~to~update~your~installation~of~the~package~'translations'.\MessageBreak Loading~cooking-units~will~abort! } \tex_endinput:D } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Option-deprecated } { The \ option \ '#1' \ is \ deprecated \ and \ should \ be \ replaced \ by \ '#2'. }{ Please \ replace \ the \ option. \msg_see_documentation_text:n { cooking-units } } \msg_new:nnnn { cooking-units } { key-unknown } { The~key~'#1'~is~unknown~and~is~being~ignored. } { The~module~'#2'~does~not~have~a~key~called~'#1'.\\ Check~that~you~have~spelled~the~key~name~correctly. } % \end{macrocode} % \begin{macrocode} \bool_new:N \g_@@_opt_numeral_bool %\bool_new:N \g_@@_opt_debug_bool \keys_define:nn { cooking-units } { use-numerals .code:n = { \msg_warning:nnnn { cooking-units } { Option-deprecated } { use-numerals } { use-fmtcount-numerals } \keys_set:nn { cooking-units } { use-fmtcount-numerals = #1 } } , use-numerals .default:n = { false }, use-fmtcount-numerals .bool_gset:N = \g_@@_opt_numeral_bool , use-fmtcount-numerals .default:n = { false }, % debug .bool_set:N = \g_@@_opt_debug_bool , % debug .default:n = { true } , unknown .code:n = { \msg_error:nnxx { cooking-units } { key-unknown } { \l_keys_path_str } { cooking-units } } } % \end{macrocode} % Now process the package options \dots % \begin{macrocode} \ProcessKeysOptions { cooking-units } \bool_if:NT \g_@@_opt_numeral_bool { \@ifpackageloaded { fmtcount } { } { \RequirePackage { fmtcount } } } % \end{macrocode} % \dots\ and redefine the package option such that it cannot be used elsewhere. % \begin{macrocode} \keys_define:nn { cooking-units } { use-numerals .code:n = { \msg_warning:nnnn { cooking-units } { Option-deprecated } { use-numerals } { use-fmtcount-numerals } \msg_error:nnn { cooking-units } { load-time-option } { use-fmtcount-numerals } } , use-fmtcount-numerals .code:n = { \msg_error:nnn { cooking-units } { load-time-option } { use-fmtcount-numerals } } , } % \end{macrocode} % % % % \subsection {Defining Variables} % % \begin{macro} { cs variants } % Some variations of commands we will need later. % \begin{macrocode} \cs_generate_variant:Nn \msg_redirect_name:nnn { nnV } \cs_generate_variant:Nn \tl_replace_all:Nnn { NVn } \cs_generate_variant:Nn \tl_replace_once:Nnn { NVn } \cs_generate_variant:Nn \tl_if_in:nnTF { nV } \cs_generate_variant:Nn \tl_if_in:NnTF { NVTF } \cs_generate_variant:Nn \tl_if_in:NnT { NVT } \cs_generate_variant:Nn \fp_compare:nNnT { vNnT } \cs_generate_variant:Nn \prop_get:NnN { cVc, Nnc } \cs_generate_variant:Nn \prop_get:NnNF { Nnc } \cs_generate_variant:Nn \tl_set_rescan:Nnn { NnV } \cs_generate_variant:Nn \msg_error:nnnn { nnnV } \cs_generate_variant:Nn \msg_error:nnn { nnV } \cs_generate_variant:Nn \msg_warning:nnn { nnV } \cs_generate_variant:Nn \prop_put:Nnn { cnx } \cs_generate_variant:Nn \seq_remove_all:Nn { NV } \cs_generate_variant:Nn \prop_gput:Nnn { Nnv, Nxv } \cs_generate_variant:Nn \prop_get:NnN { cnc } \cs_generate_variant:Nn \prg_new_conditional:Npnn { c } \cs_generate_variant:Nn \text_uppercase:n { x } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_frac:nn} % This command is used to print fractions and can be changed % accordingly. % \begin{macrocode} \cs_new_eq:NN \@@_frac:nn \sfrac % \end{macrocode} % \end{macro} % % \begin{macro}{\l_@@_unit_argument_spec_tl , \l_@@_unit_argument_spec_tl } % Allows for potential parsing of amount and or unit. % \begin{macrocode} \tl_new:N \l_@@_unit_argument_spec_tl \tl_new:N \l_@@_expand_amount_tl \tl_set:Nn \l_@@_unit_argument_spec_tl { n } \tl_set:Nn \l_@@_expand_amount_tl { n } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_set_amount:Nn , \@@_set_unit:Nn , \@@_to_unit:n } % Definitions to set the amount and the unit. Made so that it can be changed globally. % By default, the amount is just taken as is while the unit is changed into a string. % \begin{macrocode} \cs_new_eq:NN \@@_set_amount:Nn \tl_set:Nn \cs_new_eq:NN \@@_set_unit:Nn \str_set:Nn \cs_new_eq:NN \@@_to_unit:n \tl_to_str:n % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_print_numeral:n , \@@_print_Numeral:n} % Commands to print numerals. Either use default or set yourself. % \begin{macrocode} \cs_new:Npn \@@_print_numeral:n #1 { \msg_error:nn {cooking-units} { define-numeral-function } #1 } \cs_new:Npn \@@_print_Numeral:n #1 { \msg_error:nn {cooking-units} { define-Numeral-function } #1 } \bool_if:NT \g_@@_opt_numeral_bool { \cs_set_eq:NN \@@_print_numeral:n \numberstringnum \cs_set_eq:NN \@@_print_Numeral:n \Numberstringnum } \cs_generate_variant:Nn \@@_print_numeral:n { V } \cs_generate_variant:Nn \@@_print_Numeral:n { V } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \l_@@_change_unit_prop } % Conversions of units are stored within this property list. % If someone requests that \myunit{kg} should be changed into \myunit{g}, % \myunit{kg} is stored as a key with the value \myunit{g}. If someone % then uses the unit \myunit{kg} the value \myunit{g} is restored and the unit % is changed accordingly. % \begin{macrocode} \prop_new:N \l_@@_change_unit_prop % \end{macrocode} % \end{macro} % % % % \begin{macro} { \g_@@_master_changer_prop } % Similar to |\l_@@_change_unit_prop|, but doesn't store the requested % unit change, but the values needed for conversion. Either a numerical % value is stored or a formula (using |#1| to insert the original value). % % Put and get values via \cs{\@@_prop_gput_master:nnn} % and \cs{@@_prop_get_master:nnNTF}. % \begin{macrocode} \prop_new:N \g_@@_master_changer_prop % \end{macrocode} % \end{macro} % % \begin{macro} { \g_@@_convert_bonkers_remember_prop , \g_@@_convert_to_eV_remember_prop } % If the unit is changed in a \enquote{not normal} way, store % some things in one of the two lists. They are just used as shortcuts % to remember which unit is changed into which. They are one level above % |\g_@@_master_changer_prop|. % \begin{macrocode} \prop_new:N \g_@@_convert_to_eV_remember_prop \prop_new:N \g_@@_convert_bonkers_remember_prop % \end{macrocode} % \end{macro} % % \begin{macro} % { % , \l_@@_number_tmpa_tl % , \l_@@_number_tmpb_tl % , \l_@@_mixed_fraction_tl % , \l_@@_original_amount_tl % , \l_@@_original_unit_tl % , \l_@@_option_unit_tl % , \l_@@_language_tl % , \l_@@_cunum_range_sign_tl % , \l_@@_cutext_range_sign_tl % , \l_@@_value_unit_space_tl % , \l_@@_input_digits_tl % , \l_@@_input_decimal_mark_tl % , \l_@@_input_value_signs_tl % , \l_@@_input_allowed_special_signs_tl % , \g_@@_input_str_hash_one_tl % , \l_@@_input_range_sign_tl % , \l_@@_input_times_sign_tl % , \l_@@_cutext_space_tl % , \l_@@_cutext_numeral_space_tl % , \l_@@_cuphrase_space_tl % , \l_@@_translation_tmpa_tl % , \l_@@_cutext_last_value_tl % , \l_@@_phantom_tl % , \l_@@_phrase_text_tl % , \l_@@_unit_letter_not_allowed_tl % , \l_@@_input_fraction_sign_tl % , \l_@@_input_mixed_fraction_sign_tl % , \l_@@_list_of_units_tl % , \l_@@_list_of_values_tl % , \l_@@_allwed_integer_math_operators_tl % , \l_@@_curr_group_tl % , \l_@@_curr_unit_tl % , \l_@@_cmd_hook_tl % , \l_@@_cunum_hook_tl % , \l_@@_cutext_hook_tl % , \l_@@_Cutext_hook_tl % , \l_@@_cuam_hook_tl % , \l_@@_new_key_base_tl % , \l_@@_decided_unit_tl % , \l_@@_stored_units_tl % , \l_@@_stored_formula_tl % , \l_@@_base_tl % } % Quite a lot of tl's. % \begin{macrocode} \tl_new:N \l_@@_number_tmpa_tl \tl_new:N \l_@@_number_tmpb_tl \tl_new:N \l_@@_mixed_fraction_tl \tl_new:N \l_@@_original_amount_tl \tl_new:N \l_@@_original_unit_tl \tl_new:N \l_@@_option_unit_tl \tl_new:N \l_@@_language_tl \tl_new:N \l_@@_cunum_range_sign_tl \tl_new:N \l_@@_cutext_range_sign_tl \tl_new:N \l_@@_value_unit_space_tl \tl_new:N \l_@@_input_digits_tl \tl_new:N \l_@@_input_decimal_mark_tl \tl_new:N \l_@@_input_value_signs_tl \tl_new:N \l_@@_input_allowed_special_signs_tl \tl_new:N \g_@@_input_str_hash_one_tl \tl_new:N \l_@@_input_range_sign_tl \tl_new:N \l_@@_input_times_sign_tl \tl_new:N \l_@@_cutext_space_tl \tl_new:N \l_@@_cutext_numeral_space_tl \tl_new:N \l_@@_cuphrase_space_tl \tl_new:N \l_@@_translation_tmpa_tl \tl_new:N \l_@@_cutext_last_value_tl \tl_new:N \l_@@_phantom_tl \tl_new:N \l_@@_phrase_text_tl \tl_new:N \l_@@_unit_letter_not_allowed_tl \tl_new:N \l_@@_input_fraction_sign_tl \tl_new:N \l_@@_input_mixed_fraction_sign_tl \tl_new:N \l_@@_list_of_units_tl \tl_new:N \l_@@_list_of_values_tl \tl_new:N \l_@@_allwed_integer_math_operators_tl \tl_new:N \l_@@_curr_group_tl \tl_new:N \l_@@_curr_unit_tl \tl_new:N \l_@@_new_key_base_tl \tl_new:N \l_@@_decided_unit_tl \tl_new:N \l_@@_stored_units_tl \tl_new:N \l_@@_stored_formula_tl \tl_new:N \l_@@_base_tl \tl_new:N \l_@@_times_space_tl \tl_new:N \l_@@_times_sign_tl % \end{macrocode} % \begin{macrocode} \tl_new:N \l_@@_cmd_hook_tl \tl_new:N \l_@@_cunum_hook_tl \tl_new:N \l_@@_cutext_hook_tl \tl_new:N \l_@@_Cutext_hook_tl \tl_new:N \l_@@_cuam_hook_tl % \end{macrocode} % \end{macro} % % % Setting some token lists to their default value. % \cs{c_@@_input_str_hash_one_tl} is used for defining single keys. % (As you will see, I didn't have a better idea) % \begin{macrocode} \tl_set:Nn \l_@@_input_digits_tl { 0123456789 } \tl_set:Nn \l_@@_input_range_sign_tl { -- } \tl_set:Nn \l_@@_input_times_sign_tl { \times x X } \tl_set:Nn \l_@@_input_fraction_sign_tl { / } \tl_set:Nn \l_@@_input_mixed_fraction_sign_tl { _ } \tl_set:Nn \l_@@_input_decimal_mark_tl { . , } \tl_set:Nn \l_@@_input_value_signs_tl { + - } \tl_set:Nn \l_@@_input_allowed_special_signs_tl { ? } \tl_gset_rescan:Nnn \g_@@_input_str_hash_one_tl { \char_set_catcode_letter:N \# } {#1} \tl_set:Nn \l_@@_allwed_integer_math_operators_tl { +-()*/ } \tl_set:Nn \l_@@_times_sign_tl { \ensuremath { \times } } % \end{macrocode} % \begin{macrocode} \tl_set:Nn \l_@@_cunum_range_sign_tl { -- } \tl_set:Nn \l_@@_cutext_range_sign_tl { } \tl_set:Nn \l_@@_value_unit_space_tl { \thinspace } \tl_set:Nn \l_@@_times_space_tl { \, } % \: \tl_set:Nn \l_@@_cutext_space_tl { ~ } \tl_set:Nn \l_@@_cutext_numeral_space_tl { \space } \tl_set:Nn \l_@@_cuphrase_space_tl { \space } \tl_set:Nn \l_@@_unit_letter_not_allowed_tl { , / } % \end{macrocode} % % \begin{macro}{\c_@@_minus_tl} % Flat out stolen from \pkg{siunitx}. % \begin{macrocode} \AtBeginDocument { \cs_if_free:cT { T@TS1 } { \DeclareFontEncoding { TS1 } { } { } \DeclareFontSubstitution { TS1 } { cmr } { m } { n } } } % \end{macrocode} % \begin{macrocode} \DeclareTextSymbolDefault \c_@@_minus_tl { TS1 } \DeclareTextSymbol \c_@@_minus_tl { TS1 } { 61 } \AtBeginDocument { \@ifpackageloaded { fontspec } { \@ifpackageloaded { eulervm } { } { \int_const:Nn \c_@@_minus_int { 8722 } \tl_set:Nn \c_@@_minus_tl { \tex_char:D \c_@@_minus_int } } } { } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \l_@@_mixed_frac_dim } % The dimension between the fraction and the mixed fraction part is % stored within this macro. There is no real reason why I have % chosen this distance to be 0.1em, I just thought that it looks % best. But if someone has some ideas of how large this distancee % should be I am happy to listen. % \begin{macrocode} \dim_new:N \l_@@_mixed_frac_dim \dim_set:Nn \l_@@_mixed_frac_dim { 0.1 em } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \l_@@_round_precision_int } % Stores the round-precision inside. Can also take negative values. % \begin{macrocode} \int_new:N \l_@@_round_precision_int \int_set:Nn \l_@@_round_precision_int { 2 } % \end{macrocode} % \end{macro} % % \begin{macro}{ \l_@@_print_numerals_below_int } % Used if option \opt{use-numerals} is active. Uses numerals for % integers smaller than this number. I learned this number % at school. % \begin{macrocode} \int_new:N \l_@@_print_numerals_below_int \int_set:Nn \l_@@_print_numerals_below_int { 13 } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \l_@@_local_number_of_persons_int } % Each recipe defined by \cs{culabel} defines a counter to % store the number of persons the recipe is for. For calculation % the value is retrieved and stored inside this temporal integer variable. % \begin{macrocode} \int_new:N \l_@@_local_number_of_persons_int % \end{macrocode} % \end{macro} % % \begin{macro}{ \l_@@_wanted_number_of_persons_int } % Not only the number of persons are recipe is for is needed % for calculation, but also the number of persons you want % the recipe to be. This information is stored here. % \begin{macrocode} \int_new:N \l_@@_wanted_number_of_persons_int \int_set:Nn \l_@@_wanted_number_of_persons_int { 4 } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \l_@@_phrase_number_tl , \l_@@_phrase_gender_tl } % Stores the current \enquote{phrase number} (and gender). % For \enquote{Dutzend} this would % be \enquote{12}, etc. The second stores the gender (m, f or n) % \begin{macrocode} \int_new:N \l_@@_phrase_number_tl \int_new:N \l_@@_phrase_gender_tl % \end{macrocode} % \end{macro} % % \begin{macro}{ \l_@@_list_of_defined_keys_clist , \g_@@_list_of_defined_units_clist } % Sequence of defined units and keys. Units are defined % globally as they create new commands, keys do not do that % (I think). Could be my mistake. % \begin{macrocode} \seq_new:N \l_@@_list_of_defined_keys_seq \seq_new:N \g_@@_list_of_defined_units_seq % \end{macrocode} %\end{macro} % % % \begin{macro} { \g_@@_allowed_special_keys_seq } % \changes {2016/06/12} {New: \cs{g_@@_allowed_unit_phrases_tl}} % Used for the keys 'one(m)', 'one(f)', etc. Those are special keys % which cannot be used as units, but are processed by the commands in % the language section as such. % \begin{macrocode} \seq_new:N \g_@@_allowed_special_keys_seq % \end{macrocode} % \end{macro} % % \begin{macro} { \l_@@_phrase_numbers_seq } % Inside this list the numbers for which a phrase is defined % is stored in. As this is language specific, the list is stored % inside a language-sensitive command and retrieved when needed. % \begin{macrocode} \seq_new:N \l_@@_phrase_numbers_seq % \end{macrocode} % \end{macro} % % % \begin{macro}{ % , \g_@@_stored_derived_unit_symbol_seq % , \g_@@_stored_derived_formula_seq % , \g_@@_stored_derived_unit_print_seq % } % Variables used in \mycs{declarecookingderivatives}. % \begin{macrocode} \seq_new:N \g_@@_stored_derived_unit_symbol_seq \seq_new:N \g_@@_stored_derived_formula_seq \seq_new:N \g_@@_stored_derived_unit_print_seq % \end{macrocode} % \end{macro} % % % \begin{macro}{\l_@@_nr_of_units_int, \l_@@_depth_int} % Also some variables used in \mycs{declarecookingderivatives}. (And for one check during % definitions of keys). % \begin{macrocode} \int_new:N \l_@@_nr_of_units_int \int_new:N \l_@@_depth_int % \end{macrocode} % \end{macro} % % \begin{macro}{\g_@@_natural_units_seq } % Remembers which unit foes to which natural unit. % \begin{macrocode} \seq_new:N \g_@@_natural_units_seq % \end{macrocode} % \end{macro} % % \begin{macro} { \l_@@_temperatures_to_check_seq } % \changes {1.02h} {New.} % Stores units which should be tested if \opt{check-temperature} equals true. % \begin{macrocode} \seq_new:N \l_@@_temperatures_to_check_seq % \end{macrocode} % \end{macro} % % \begin{macro} { \l_@@_curef_do_not_change_seq } % This contains the units which shouldn't be changed. % \begin{macrocode} \seq_new:N \l_@@_curef_do_not_change_seq % \end{macrocode} % \end{macro} % % \begin{macro} { \l_@@_phrase_prop } % Stores the number and he respective phrase. For example % if \enquote{12} has the phrase \enquote{Dutzend}, this % key-value pair is stored inside. % \begin{macrocode} \prop_new:N \l_@@_phrase_prop % \end{macrocode} % \end{macro} % % \begin{macro} { \l_@@_derived_units_prop } % Stores \emph{via} key-value all defined derived units as keys % and the formula to calculate the derived unit as value.. % \begin{macrocode} \prop_new:N \l_@@_derived_units_prop % \end{macrocode} % \end{macro} % % % % \begin{macro} % { % , \l_@@_pckopt_eval_fractions_bool % , \l_@@_pckopt_convert_fractions_bool % , \l_@@_pckopt_draft_bool % , \l_@@_pckopt_round_to_int_bool % , \l_@@_pckopt_check_temperature_bool % , \l_@@_pckopt_convert_to_eV_bool % , \l_@@_pckopt_cuam_old_bool % , \l_@@_pckopt_recalculate_amount_bool % , \l_@@_pckopt_cutext_to_cunum_bool % , \l_@@_pckopt_cutext_old_bool % , \l_@@_pckopt_cutext_keep_unit_bool % , \l_@@_pckopt_round_commercial_bool % , \l_@@_pckopt_use_phrases_bool % , \l_@@_pckopt_round_precision_negative_bool % , \l_@@_pckopt_fourty_two_bool % , \l_@@_pckopt_bonkers_bool % , \l_@@_minus_bool % , \l_@@_round_number_bool % , \l_@@_single_key_bool % , \l_@@_cutext_uppercase_word_bool % , \l_@@_reference_given_bool % , \l_@@_lokal_recalc_for_people_bool % , \l_@@_lokal_phrase_bool % , \l_@@_lokal_change_unit_bool % , \l_@@_pckopt_print_numeral_bool % , \l_@@_lokal_numeral_bool % , \l_@@_phrase_negative_bool % , \l_@@_cutext_use_single_bool % , \l_@@_symbol_was_set_by_key_bool % , \l_@@_contains_valid_key_bool % , \l_@@_check_decimal_bool % , \l_@@_unit_error_bool % } % Some booleans we need later. % \begin{macrocode} \bool_new:N \l_@@_pckopt_eval_fractions_bool \bool_new:N \l_@@_pckopt_convert_fractions_bool \bool_new:N \l_@@_pckopt_draft_bool \bool_new:N \l_@@_pckopt_round_to_int_bool \bool_new:N \l_@@_pckopt_check_temperature_bool \bool_new:N \l_@@_pckopt_convert_to_eV_bool \bool_new:N \l_@@_pckopt_cuam_old_bool \bool_new:N \l_@@_pckopt_recalculate_amount_bool \bool_new:N \l_@@_pckopt_cutext_to_cunum_bool \bool_new:N \l_@@_pckopt_cutext_old_bool \bool_new:N \l_@@_pckopt_cutext_keep_unit_bool \bool_new:N \l_@@_pckopt_round_commercial_bool \bool_new:N \l_@@_pckopt_use_phrases_bool \bool_new:N \l_@@_pckopt_round_precision_negative_bool \bool_new:N \l_@@_pckopt_fourty_two_bool \bool_new:N \l_@@_pckopt_bonkers_bool \bool_new:N \l_@@_minus_bool \bool_new:N \l_@@_round_number_bool \bool_new:N \l_@@_single_key_bool \bool_new:N \l_@@_cutext_uppercase_word_bool \bool_new:N \l_@@_reference_given_bool \bool_new:N \l_@@_lokal_recalc_for_people_bool \bool_new:N \l_@@_lokal_phrase_bool \bool_new:N \l_@@_lokal_change_unit_bool \bool_new:N \l_@@_pckopt_print_numeral_bool \bool_new:N \l_@@_lokal_numeral_bool \bool_new:N \l_@@_phrase_negative_bool \bool_new:N \l_@@_cutext_use_single_bool \bool_new:N \l_@@_symbol_was_set_by_key_bool \bool_new:N \l_@@_contains_valid_key_bool \bool_new:N \l_@@_check_decimal_bool \bool_new:N \l_@@_unit_error_bool \bool_new:N \l_@@_rf_contains_fraction_bool \bool_new:N \l_@@_rf_contains_range_bool \bool_new:N \l_@@_rf_contains_times_bool \bool_new:N \l_@@_rf_check_fraction_bool \bool_new:N \l_@@_lokal_eval_fractions_bool \seq_new:N \l_@@_split_input_seq \seq_new:N \l_@@_parsed_seq \seq_new:N \l_@@_numbers_to_check_seq \int_new:N \l_@@_largest_common_divisor_int \seq_new:N \l_@@_reduced_numbers_seq \bool_new:N \l_@@_seperator_after_fraction_bool \seq_new:N \l_@@_prepared_printing_seq \int_new:N \l_@@_nr_of_items \seq_new:N \l_@@_processed_seq \tl_new:N \l_@@_precompiled_options_tl % \end{macrocode} % \end{macro} % % % % Setting some of them to \true\ or another boolean respectively. % \begin{macrocode} \bool_set_false:N \l_@@_pckopt_cutext_keep_unit_bool \bool_set_eq:NN \l_@@_pckopt_print_numeral_bool \g_@@_opt_numeral_bool % \end{macrocode} % % % \begin{macro}{ \q_@@_range } % Replacing the sign \enquote{-{}-} with \lstinline|\q_@@_range| for testing. % \begin{macrocode} \quark_new:N \q_@@_range % \end{macrocode} %\end{macro} % % \begin{macro}{ \q_@@_no_translation_str } % Checking value if a translation is avaiable. % \begin{macrocode} \quark_new:N \q_@@_no_translation_str \str_const:Nn \c_@@_no_translation_str { no-translation-sady-found-@@ } %\cs_new:Npn \q_@@_no_translation_str { asdasd\ewhkerhjkwejhk } % \end{macrocode} %\end{macro} % % % \begin{macro}{ % , \l_@@_scrap_tl % , \l_@@_tmpa_tl % , \l_@@_tmpb_tl % , \l_@@_tmp_unit_tl % , \l_@@_tmpa_fp % , \l_@@_tmpa_clist % , \l_@@_tmpa_prop % , \l_@@_tmpb_prop % , \l_@@_tmpa_seq % , \l_@@_tmpb_seq % , \l_@@_tmpa_int % , \l_@@_tmpb_int % , \l_@@_tmpa_bool % , \l_@@_tmpb_bool % } % Some temporal stores which are used throughout the code. % \begin{macrocode} \tl_new:N \l_@@_scrap_tl \tl_new:N \l_@@_tmpa_tl \tl_new:N \l_@@_tmpb_tl \tl_new:N \l_@@_tmp_unit_tl \fp_new:N \l_@@_tmpa_fp \clist_new:N \l_@@_tmpa_clist \prop_new:N \l_@@_tmpa_prop \prop_new:N \l_@@_tmpb_prop \seq_new:N \l_@@_tmpa_seq \seq_new:N \l_@@_tmpb_seq \int_new:N \l_@@_tmpa_int \int_new:N \l_@@_tmpb_int \bool_new:N \l_@@_tmpa_bool \bool_new:N \l_@@_tmpb_bool % \end{macrocode} %\end{macro} % % % % \subsection {Keys} % % % Let's define some keys. % \begin{macrocode} \keys_define:nn { cooking-units } { % \end{macrocode} % % % \begin{macro}{ add-unit-to-group } % \begin{macrocode} add-unit-to-group .code:n = { \keyval_parse:NNn \@@_add_to_unit_group_error:n \@@_add_to_unit_group:nn {#1} } , add-unit-to-group .default:n = {} , % \end{macrocode} % \end{macro} % %\begin{macro}{ set-option-for } % Setting options. % \begin{macrocode} set-option-for .code:n = { \keyval_parse:NNn \@@_set_options_for_error:n \@@_set_options_for:nn {#1} }, % \end{macrocode} % \end{macro} % %\begin{macro}{ add-option-for } % Setting options. % \begin{macrocode} add-option-for .code:n = { \keyval_parse:NNn \@@_add_options_for_error:n \@@_add_options_for:nn {#1} }, % \end{macrocode} % \end{macro} % % \begin{macro}{ erase-all-options } % Erasing all preset options. Again not the best approach \dots\ maybe I need a lower % level \enquote{clear} option. % \begin{macrocode} erase-all-options .code:n = { \clist_set_from_seq:NN \l_@@_tmpa_clist \g_@@_list_of_defined_units_seq \@@_clear_options_for:V \l_@@_tmpa_clist }, erase-all-options .value_forbidden:n = { true } , % \end{macrocode} % \end{macro} % %\begin{macro}{ erase-all-options-for } % Erasing options. As this simply clears the unit options the argument |#1| % is simply a clist, as such no keyval parsing is needed. % \begin{macrocode} erase-all-options-for .code:n = { \@@_clear_options_for:n {#1} }, erase-all-options-for .value_required:n = { true } , % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{ cutext-to-cunum } % Don't want any \mycs{cutext} (or \mycs{Cutext}) in your % document? Use this option! % \begin{macrocode} cutext-to-cunum .bool_set:N = \l_@@_pckopt_cutext_to_cunum_bool , cutext-to-cunum .default:n = { false } , cutext-to-cunum .groups:n = { change-command } , % \end{macrocode} % \end{macro} % % \begin{macro}{ cutext-change-unit } % Do not wanna change units in \mycs{cutext}? Use this option. % \begin{macrocode} cutext-change-unit .bool_set_inverse:N = \l_@@_pckopt_cutext_keep_unit_bool , cutext-change-unit .default:n = { true } , cutext-change-unit .groups:n = { change-unit } , % \end{macrocode} % \end{macro} % % \begin{macro}{ cuam-version , cutext-version} % Some keys with horrible option names. Reverts % the respective command back to its older state (pre v1.10). % \begin{macrocode} cuam-version .choices:nn = { new , old } { \int_case:nn { \l_keys_choice_int } { { 1 } { \bool_set_false:N \l_@@_pckopt_cuam_old_bool } { 2 } { \bool_set_true:N \l_@@_pckopt_cuam_old_bool } } } , cuam-version .default:n = { new } , cuam-version .groups:n = { parse } , cutext-version .choices:nn = { new , old } { \int_case:nn { \l_keys_choice_int } { { 1 } { \bool_set_false:N \l_@@_pckopt_cutext_old_bool } { 2 } { \bool_set_true:N \l_@@_pckopt_cutext_old_bool } } } , cutext-version .default:n = { new } , cutext-version .groups:n = { parse } , % \end{macrocode} % \end{macro} % % \begin{macro}{ expand-both , expand-amount, expand-unit } % \begin{macrocode} expand-both .choices:nn = { n , o , f , x } { \tl_set_eq:NN \l_@@_unit_argument_spec_tl \l_keys_choice_tl \tl_set_eq:NN \l_@@_expand_amount_tl \l_keys_choice_tl } , expand-both .default:n = { n } , expand-both .groups:n = { parse } , % \end{macrocode} % \begin{macrocode} expand-amount .choices:nn = { n , o , f , x } { \tl_set_eq:NN \l_@@_expand_amount_tl \l_keys_choice_tl } , expand-amount .default:n = { n } , expand-amount .groups:n = { parse } , % \end{macrocode} % \begin{macrocode} expand-unit .choices:nn = { n , o , f , x } { \tl_set_eq:NN \l_@@_unit_argument_spec_tl \l_keys_choice_tl } , expand-unit .default:n = { n } , expand-unit .groups:n = { parse } , % \end{macrocode} % \end{macro} % % % % % %\begin{macro}{ set-special-sign } % Setting allowd special signs. Note that |<| is not allowed. % \begin{macrocode} set-special-sign .code:n = { \tl_map_inline:nn {#1} { \str_if_eq:nnTF {##1} { < } { \msg_error:nn { cooking-units } { <-not-allowed-as-special-sign } \tl_map_break:n { \use_none:nn } } } \use:n { \str_if_in:nnTF {#1} { x } { \tl_remove_all:Nn \l_@@_input_times_sign_tl { x } \tl_remove_all:Nn \l_@@_input_times_sign_tl { X } }{ \str_if_in:nnT {#1} { X } { \tl_remove_all:Nn \l_@@_input_times_sign_tl { x } \tl_remove_all:Nn \l_@@_input_times_sign_tl { X } } } \tl_set:Nn \l_@@_input_allowed_special_signs_tl {#1} } } , set-special-sign .groups:n = { parse } , % \end{macrocode} %\end{macro} %\begin{macro}{ add-special-sign } % Adding a (some) special sign(s) which is (are) allowed in the input. % \begin{macrocode} add-special-sign .code:n = { \tl_map_inline:nn {#1} { \str_if_eq:nnTF {##1} { < } { \msg_error:nn { cooking-units } { <-not-allowed-as-special-sign } }{ \bool_lazy_or:nnT { \str_if_eq_p:nn {##1} { x } } { \str_if_eq_p:nn {##1} { X } } { \tl_remove_all:Nn \l_@@_input_times_sign_tl { x } \tl_remove_all:Nn \l_@@_input_times_sign_tl { X } } \tl_put_right:Nn \l_@@_input_allowed_special_signs_tl {##1} } } } , add-special-sign .default:n = { } , add-special-sign .groups:n = { parse } , % \end{macrocode} %\end{macro} % % % \begin{macro}{ set-unknown-message } % Sets the message for a special-sign to error, warning or none. % \begin{macrocode} set-unknown-message .choices:nn = { error , warning , none } { \msg_redirect_name:nnV { cooking-units } { amount-not-known } \l_keys_choice_tl } , set-unknown-message .default:n = { warning } , % \end{macrocode} %\end{macro} % % \begin{macro}{ set-cutext-translation-message } % Sets the message for a special-sign to error, warning or none. % \begin{macrocode} set-cutext-translation-message .choices:nn = { error , warning , none } { \msg_redirect_name:nnV { cooking-units } { cutext-no-translation-available } \l_keys_choice_tl } , set-cutext-translation-message .default:n = { warning } , % \end{macrocode} %\end{macro} % % % \begin{macro}{ print-numerals } % A not very good name, but I couldn't think of a better name. % \begin{macrocode} print-numerals .bool_set:N = \l_@@_pckopt_print_numeral_bool , print-numerals .default:n = { true } , print-numerals .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % % \begin{macro}{ use-numerals-below } % Use numerals if the integer is below the integer set by this option. % \begin{macrocode} use-numerals-below .int_set:N = \l_@@_print_numerals_below_int , use-numerals-below .default:n = { 13 } , use-numerals-below .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % %\begin{macro}{ parse-number } % Parse the input? If no the input is printed as is (after some safety parsing). % \begin{macrocode} parse-number .bool_set_inverse:N = \l_@@_pckopt_draft_bool , parse-number .default:n= { true } , parse-number .groups:n = { parse } , % \end{macrocode} %\end{macro} % %\begin{macro}{ range-sign } % Setting the printed range sign and make a difference between cunum % and c(C)utext. % \begin{macrocode} range-sign .meta:n = { cunum-range-sign = {#1} , cutext-range-sign = {#1} } , range-sign .default:n = { -- } , cunum-range-sign .tl_set:N = \l_@@_cunum_range_sign_tl , cunum-range-sign .default:n = { -- } , cutext-range-sign .tl_set:N = \l_@@_cutext_range_sign_tl , cutext-range-sign .default:n = { -- } , range-sign .groups:n = { unit-hook } , cunum-range-sign .groups:n = { unit-hook } , cutext-range-sign .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % % \begin{macro}{ use-phrases } % Do not wanna use phrases in \mycs{cuam}? Use this option! % \begin{macrocode} use-phrases .bool_set:N = \l_@@_pckopt_use_phrases_bool , use-phrases .default:n = { true } , use-phrases .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % % % % % % %\begin{macro}{ round-precision } % Setting the round-precision. Setting those two at once to % not calculate it every time. % \begin{macrocode} round-precision .code:n = { \int_set:Nn \l_@@_round_precision_int {#1} \int_compare:nNnTF {#1} < { 0 } { \bool_set_true:N } { \bool_set_false:N } \l_@@_pckopt_round_precision_negative_bool } , round-precision .default:n = { 2 } , round-precision .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % %\begin{macro}{ round-to-int } % Rounding the results to an integer. % \begin{macrocode} round-to-int .bool_set:N = \l_@@_pckopt_round_to_int_bool , round-to-int .choices:nn = { true , false } { \msg_error:nnnn { cooking-units } { Option-deprecated } { round-to-int } { round-precision = 0 } \int_compare:nNnT { \l_keys_choice_int } = { 1 } { \int_set:Nn \l_@@_round_precision_int { 0 } } \use:c { bool_set_ \l_keys_choice_tl :N } \l_@@_pckopt_round_to_int_bool } , round-to-int .default:n = { false } , round-to-int .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % % \begin{macro}{ round-half } % Choosing between \enquote{normal} rounding to even % and commercial rounding and sets the boolean accordingly. % \begin{macrocode} % TODO: uodate doch und so round-half .choices:nn = { default , commercial , up } { \int_case:nn { \l_keys_choice_int } { { 1 } { \bool_set_false:N \l_@@_pckopt_round_commercial_bool } { 2 } { \bool_set_true:N \l_@@_pckopt_round_commercial_bool } { 3 } { \bool_set_true:N \l_@@_pckopt_round_commercial_bool } } }, round-half .default:n = { default }, round-half .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % %\begin{macro}{ eval-fraction } % If set to true the fractions are evaluated. % \begin{macrocode} eval-fraction .bool_set:N = \l_@@_pckopt_eval_fractions_bool , eval-fraction .default:n = { false } , % \end{macrocode} %\end{macro} % %\begin{macro}{ convert-fraction } % If set to true the fractions are evaluated \emph{if} the unit is transformed! % \begin{macrocode} convert-fraction .bool_set:N = \l_@@_pckopt_convert_fractions_bool , convert-fraction .default:n = { true } , % \end{macrocode} %\end{macro} % % %\begin{macro}{ fraction-command } % Setting the fraction command % \begin{macrocode} fraction-command .code:n = { \cs_set_eq:NN \@@_frac:nn #1 } , fraction-command .default:n = { \sfrac } , % \end{macrocode} %\end{macro} % %\begin{macro}{ fraction-inline } % Setting the code inline. % \begin{macrocode} fraction-inline .code:n = { \cs_set:Npn \@@_frac:nn ##1##2 {#1} } , fraction-inline .default:n = { \sfrac {#1} {#2} } , % \end{macrocode} %\end{macro} % % % % % % %\begin{macro}{ mixed-fraction-space } % Setting the space between the mixed fraction part and the fraction. % \begin{macrocode} mixed-fraction-space .dim_set:N = \l_@@_mixed_frac_dim , mixed-fraction-space .default:n = { 0.1 em } , mixed-fraction-space .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % % \begin{macro}{ cutext-space } % The space used in \mycs{cutext} between the \emph{number} % and unit. % \begin{macrocode} cutext-space .tl_set:N = \l_@@_cutext_space_tl , cutext-space .default:n = { ~ } , cutext-space .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % % \begin{macro}{ cutext-space } % The space used in \mycs{cutext} between the \emph{numeral} % and unit. % \begin{macrocode} cutext-numeral-space .tl_set:N = \l_@@_cutext_numeral_space_tl , cutext-numeral-space .default:n = { \space } , cutext-numeral-space .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % % \begin{macro}{ phrase-space } % Same as before, but for phrases % \begin{macrocode} phrase-space .tl_set:N = \l_@@_cuphrase_space_tl , phrase-space .default:n = { ~ } , phrase-space .groups:n = { unit-hook } , % \end{macrocode} % \end{macro} % %\begin{macro}{ amount-unit-space } % Setting the space between the value and the printed unit. % \begin{macrocode} amount-unit-space .tl_set:N = \l_@@_value_unit_space_tl , amount-unit-space .default:n = { \thinspace } , amount-unit-space .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % %\begin{macro}{ amount-unit-space } % Setting the space between the value and the printed unit. % \begin{macrocode} times-space .tl_set:N = \l_@@_times_space_tl , times-space .default:n = { \: } , times-space .groups:n = { unit-hook } , % \end{macrocode} %\end{macro} % % % % % % \begin{macro}{ recalculate-amount } % Setting the number of persons your recipes should be for % is not enough; it is also needed to tell the package to % recalculate the amounts. % \begin{macrocode} recalculate-amount .bool_set:N = \l_@@_pckopt_recalculate_amount_bool , recalculate-amount .default:n = { false } , % \end{macrocode} % \end{macro} % % \begin{macro}{ set-number-of-persons } % Setting the number of persons the recipe should be for. % \begin{macrocode} set-number-of-persons .int_set:N = \l_@@_wanted_number_of_persons_int , set-number-of-persons .default:n = { 4 } , % \end{macrocode} % \end{macro} % % \begin{macro}{ label } % Defines a label. Is \mycs{culabel} in key form. % \begin{macrocode} label .code:n = \@@_label_and_persons:n {#1} , label .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{ get-label } % \mycs{curef} as a key. % \begin{macrocode} get-label .code:n = \@@_curef:n {#1} , get-label .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{ ref } % The |<>| option for keys. % \begin{macrocode} ref .code:n = \@@_reference_label_and_persons:n {#1} , ref .value_required:n = { true } , % \end{macrocode} % \end{macro} % % % % % % %\begin{macro}{ check-temperature } % Weird option. Checking the temperature, if the temperature is below the absolute zero temperature % it raises an error. % \begin{macrocode} check-temperature .bool_set:N = \l_@@_pckopt_check_temperature_bool , check-temperature .default:n = { true } , % \end{macrocode} % %\begin{macro}{ add-temperature-to-check } % Adds a temperature to check for \texttt{check-temperature}. % It uses the \cs{keyval\_parse:NNn} command as this % macro is used to parse keys (which is what I need). % \begin{macrocode} add-temperature-to-check .code:n = { \keyval_parse:NNn \@@_temperature_to_check_print_error:n \@@_temperatures_to_check_define:nn {#1} } , temperature-to-check .value_required:n = { true } , % \end{macrocode} % \end{macro} % %\begin{macro}{ convert-to-eV } % Another weird option, converts pretty much any unit defined by this % package to electron volt or the respective derivative. % As this is a unit transformation, it needs to be inside the % group. % \begin{macrocode} convert-to-eV .bool_set:N = \l_@@_pckopt_convert_to_eV_bool , convert-to-eV .default:n = { true } , % convert-to-eV .groups:n = { change-unit } , % \end{macrocode} %\end{macro} % % \begin{macro}{ add-natural-unit } % \begin{macrocode} add-natural-unit .code:n = { \seq_set_from_clist:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_error_if_unit_not_defined:nNF {##1} \l_@@_curr_unit_tl { \seq_if_in:NVF \g_@@_natural_units_seq \l_@@_curr_unit_tl { \seq_gput_right:NV \g_@@_natural_units_seq \l_@@_curr_unit_tl } } } } , add-natural-unit .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{ 42 } % \begin{macrocode} 42 .choice: , 42 / true .code:n = { \bool_set_true:N \l_@@_pckopt_fourty_two_bool } , 42 / false .code:n = { \msg_fatal:nnn { cooking-units } { 42-is-...-wrong? } } , 42 .default:n = { true } , 42 .groups:n = { parse } , % \end{macrocode} % \end{macro} % % % % % % % % % % % % % % % % % % % % \begin{macro}{ input-range-sign } % Don't wanna use \verb|--| as a range sperator in \mycs{cunum}? % Use this option. % \begin{macrocode} input-range-sign .tl_set:N = \l_@@_input_range_sign_tl , input-range-sign .default:n = { -- } , input-range-sign .groups:n = { parse } , % \end{macrocode} %\end{macro} % % \begin{macro}{ numeral-function , Numeral-function } % \begin{macrocode} numeral-function .code:n = { \cs_set_eq:NN \@@_print_numeral:n #1 } , Numeral-function .code:n = { \cs_set_eq:NN \@@_print_Numeral:n #1 } , % \end{macrocode} % \end{macro} % % % % % \begin{macro}{ % commands-add-hook , % cunum-add-hook , % cutext-add-hook , Cutext-add-hook , % cuam-add-hook , % } % \begin{macrocode} commands-add-hook .meta:n = { cunum-add-hook = {#1} , cutext-add-hook = {#1} , Cutext-add-hook = {#1} , cuam-add-hook = {#1} , } , cunum-add-hook .code:n = { \tl_put_right:Nn \l_@@_cunum_hook_tl {#1} } , cutext-add-hook .code:n = { \tl_put_right:Nn \l_@@_cutext_hook_tl {#1} } , Cutext-add-hook .code:n = { \tl_put_right:Nn \l_@@_Cutext_hook_tl {#1} } , cuam-add-hook .code:n = { \tl_put_right:Nn \l_@@_cuam_hook_tl {#1} } , cunum-add-hook .groups:n = { parse } , cutext-add-hook .groups:n = { parse } , Cutext-add-hook .groups:n = { parse } , cuam-add-hook .groups:n = { parse } , commands-add-hook .groups:n = { parse } , % \end{macrocode} % \end{macro} % % % % % % % % % % % % % % % % % % \begin{macro}{ % curef-add-forbidden-unit, % curef-remove-forbidden-unit, % curef-clear-forbidden-units % } % \begin{macrocode} curef-add-forbidden-unit .code:n = { \@@_add_forbidden_unit:n {#1} } , curef-add-forbidden-unit .default:n = { } , curef-remove-forbidden-unit .code:n = { \@@_remove_forbidden_unit:n {#1} } , curef-remove-forbidden-unit .default:n = { } , % curef-clear-forbidden-units .choice: , curef-clear-forbidden-units/true .code:n = { \@@_clear_forbidden_unit: } , curef-clear-forbidden-units/false .code:n = { } , curef-clear-forbidden-units .value_required:n = true, % \end{macrocode} % \end{macro} % % % % % \begin{macro}{symbol} % To be implemented. Key used during definition of new units. % Defines the printed symbol of the unit (both do the same). % \begin{macrocode} definition / symbol .code:n = { \bool_set_true:N \l_@@_contains_valid_key_bool \bool_set_true:N \l_@@_symbol_was_set_by_key_bool \tl_set:cn { l_@@_default_unit_ \l_@@_original_unit_tl _tl } {#1} }, definition / symbol .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{gender} % To be implemented. Key used during definition of new units. % Defines the gender of the unit. % \begin{macrocode} definition / gender .code:n = { \bool_set_true:N \l_@@_contains_valid_key_bool \@@_if_gender_error:nTF {#1} { \@@_culang_def_base:nVn { gender } \l_@@_original_unit_tl { m } } { \@@_culang_def_base:nVn { gender } \l_@@_original_unit_tl {#1} } }, definition / gender .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{set-option} % To be implemented. Key used during definition of new units. % Adds options to a specific key during definition of unit. % \begin{macrocode} definition / set-option .code:n = { \bool_set_true:N \l_@@_contains_valid_key_bool \@@_set_options_for:Vn \l_@@_original_unit_tl {#1} }, definition / set-option .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{add-to-group} % To be implemented. Key used during definition of new units. % Adds unit to given group. % \begin{macrocode} definition / add-to-group .code:n = { \bool_set_true:N \l_@@_contains_valid_key_bool \@@_add_to_unit_group:nV {#1} \l_@@_original_unit_tl }, definition / add-to-group .value_required:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{natural-unit} % To be implemented. Key used during definition of new units. % Defines this unit as a natural unit. % \begin{macrocode} definition / natural-unit .code:n = { \bool_set_true:N \l_@@_contains_valid_key_bool \seq_if_in:NVF \g_@@_natural_units_seq \l_@@_original_unit_tl { \seq_gput_right:NV \g_@@_natural_units_seq \l_@@_original_unit_tl } }, definition / natural-unit .default:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{unit-names} % To be implemented. Key used during definition of new units. % Defines written out unit name for \cCutext for given language. % Also suspended. Would need to give language and it would be too % complicated (I think) % \begin{macrocode} % definition / unit-names .code:n = { }, % \end{macrocode} % \end{macro} % % % You get here during the definition of a new unit if you use the optional % argument. As symbols cannot contain |=| the argument |#1| is going % to be empty. I also assume that none of the above keys are used as an % unit-symbol. If |#1| is non-empty (aka.\ the key has a value) throw an error. % \begin{macrocode} definition / unknown .code:n = { \tl_if_empty:nF {#1} { \msg_error:nnxx { cooking-units } { definition-key-mistyped? } \l_keys_key_str {#1} } }, % \end{macrocode} % % % % \begin{macro}{nothing-special} % Stage 0 of being bonkers. Do nothing special in this one. Reset everything. % \begin{macrocode} nothing-special .code:n = { \prop_gclear:N \g_@@_master_changer_prop \prop_gclear:N \g_@@_convert_bonkers_remember_prop \bool_set_false:N \l_@@_pckopt_bonkers_bool \tl_set_eq:NN \l_@@_bonkers_state_tl \c_@@_bonkers_state_normal_tl }, nothing-special .value_forbidden:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{going-bonkers} % Stage 1 of being bonkers. % \begin{macrocode} going-bonkers .code:n = { \prop_gclear:N \g_@@_convert_bonkers_remember_prop \bool_set_true:N \l_@@_pckopt_bonkers_bool \tl_set_eq:NN \l_@@_bonkers_state_tl \c_@@_bonkers_state_going_tl }, going-bonkers .value_forbidden:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{fully-bonkers} % Stage 2 of being bonkers. % \begin{macrocode} fully-bonkers .code:n = { \prop_gclear:N \g_@@_master_changer_prop \prop_gclear:N \g_@@_convert_bonkers_remember_prop \bool_set_true:N \l_@@_pckopt_bonkers_bool \tl_set_eq:NN \l_@@_bonkers_state_tl \c_@@_bonkers_state_fully_tl }, fully-bonkers .value_forbidden:n = { true } , % \end{macrocode} % \end{macro} % % \begin{macro}{xD-lol} % Stage 3 of being bonkers. % \begin{macrocode} xD-lol .code:n = { \prop_gclear:N \g_@@_master_changer_prop \prop_gclear:N \g_@@_convert_bonkers_remember_prop \bool_set_true:N \l_@@_pckopt_bonkers_bool \tl_set_eq:NN \l_@@_bonkers_state_tl \c_@@_bonkers_state_xD_tl }, xD-lol .value_forbidden:n = { true } , % \end{macrocode} % \end{macro} % % % % % % % % % % % Ending the definition of keys. % \begin{macrocode} } %\keys_set:nn { cooking-units } {#2} % \end{macrocode} % \end{macro} % % % % % % \subsection {Messages} % % Defining messages. % % \begin{macro} {Messages} % I do not allow fractions and ranges in the same input. Maybe I will % change this. % \begin{macrocode} %\msg_new:nnnn { cooking-units } { fraction-not-allowed-with-range } % { % You \ cannot \ use \ '/' \ ( and \ '_' )\ in \ combination \ % with \ '\l_@@_input_range_sign_tl' \ in \ '#1'. % }{ % You \ cannot \ use \ fractions \ with \ a \ range. % \msg_see_documentation_text:n { cooking-units } % } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { range-too-many-items } { A \ range \ is \ only \ allowed \ to \ have \ a \ maximum \ of \ two \ items. \ You \ have \ #1 \ items \ in \ '#2'. }{ You \ cannot \ use \ more \ than \ two \ '--' . \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { range-and-times-not-allowed } { It \ is \ not \ allowed \ to \ mix \ ranges \ ('--') \ and \ times \ in \ '#1'. }{ Do \ not \ use \ times \ and \ ranges \ in \ the \ same \ command. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Do not allow a \_ without a /. % \begin{macrocode} \msg_new:nnnn { cooking-units } { missing-slash } { You \ cannot \ use \ '_' \ without \ '/' \ in \ '#1'. }{ You \ cannot \ have \ a \ mixed \ fraction \ ('_') \ without \ a \ normal \ fraction \ ('/'). \msg_see_documentation_text:n { cooking-units } } \msg_new:nnnn { cooking-units } { fraction-wrong-order } { You \ cannot \ use \ '/' \ before \ '_' \ in \ '#1'. }{ You \ can \ only \ use \ '_' \ before \ '/'. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % Error message if unit is not known to this package. % \begin{macrocode} \msg_new:nnnn { cooking-units } { unknown-unit } { The \ unit \ '#1' \ is \ not \ defined. \ Use \ \newcookingunit (or \ one \ of \ the \ others) \ to \ define \ new \ units. }{ Define \ units \ before \ using \ or \ check \ if \ the \ unit-key \ is \ written \ correctly. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % Error if unit is already defined. % \begin{macrocode} \msg_new:nnnn { cooking-units } { unit-already-defined } { The\ unit \ '#1' \ is \ already \ defined. } { The \ unit-key \ is \ already \ defined. \ Please \ use \ a \ different \ key \ for \ a \ new \ unit. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { character-not-allowed } { Unit-keys \ are \ not \ allowed \ to \ either \ contain \ ',' \ or \ '/' \ in \ '#1'. }{ The \ unit-key \ is \ not \ allowed \ to \ contain \ ',' \ or \ '/'. \ Due \ to \ internal \ reasons. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % Missing argument in \lstinline|\cudefinesymbols| (et all). % \begin{macrocode} \msg_new:nnnn { cooking-units } { missing-argument } { There \ is \ a \ missing \ argument. } { You \ probably \ have \ forgotten \ a \ curly-brace \ pair. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { symbol-and-special-keys-error } { You \ cannot \ use \ special-key \ '#1' \ with \ symbol \ '#2' \ definitions. } { You \ cannot \ redefine \ a \ symbol \ of \ a \ special-key. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % % If fractions are evaluated division by zero is not allowed. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Division-by-zero } { Division\ by \ zero \ is \ not \ allowed \ in \ '#1'. } { See \ a \ math \ book \ of \ your \ choice \ or \ for \ example \ Wikipedia. } % \end{macrocode} % Showing the not allowed token in the input. Hope this helps. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Token-not-allowed } { The \ token \ '#1' \ is \ not \ allowed \ in \ '#2'. } { The \ command \ accepts \ only \ a \ fixed \ number \ of \ tokens. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % A second decimal sign is not allowed (No na net). % \begin{macrocode} \msg_new:nnnn { cooking-units } { Second-decimal-sign-not-allowed } { A \ second \ decimal \ sign \ is \ not \ allowed \ in \ '#1'. } { Perhaps \ you \ didn't \ type \ it \ correctly. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Second-fraction-sign-not-allowed } { A \ second \ fraction \ sign \ is \ not \ allowed \ in \ '#1'. } { Perhaps \ you \ didn't \ type \ it \ correctly. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Second-mixed-fraction-sign-not-allowed } { A \ second \ mixed \ fraction \ sign \ is \ not \ allowed \ in \ '#1'. } { Perhaps \ you \ didn't \ type \ it \ correctly. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % Error message for an undefined key. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Key-not-defined } { The \ key \ '#1' \ is \ not \ defined. \ Use \ \cudefinekeys or \ \cudefinesinglekey to \ define \ keys. } { This \ key \ is \ not \ defined, \ perhaps \ you \ misspelled \ it. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % Error message for an undefined key. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Key-chain-unit-not-found } { It \ appears \ that \ \token_to_str:N \cuaddtokeychain\ was \ not \ able \ to \ find a \ key-chain-unit. Define \ key-chains \ first \ using \token_to_str:N \cudefinekeychain. } { No \ key-chain-unit\ was \ found, \ perhaps \ you \ misspelled \ it? \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % If the temperature is too low print this error message. % Now prints all units for which a zero-point is defined. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Temperature-too-low } { The \ temperature \ '#1' \ is \ too \ low.\ It \ cannot \ be \ below \ the \ absolute \ zero - point \ of \ '#2'. \ Note \ that \ the \ temperatures \ '\@@_seq_use_msg:N \l_@@_temperatures_to_check_seq' \ are \ rounded \ to \ integers \ by \ default. \\\\ You \ can \ disable \ the \ option \ 'check-temperature' \ to \ disable \ this \ error. } { See \ for \ example \ Wikipedia. } % \end{macrocode} % If for an unit-key the value is wrong the following error message is shown. % \begin{macrocode} \msg_new:nnnn { cooking-units } { key-choice-unknown } { The \ key \ '#1' \ only \ accepts \ only \ '#3' \ as \ a \ set \ of \ choices \ and \ '#2' \ is \ non \ of \ these. }{ The \ key \ accepts \ only \ a \ fixed \ set \ of \ choices. \ You \ can \ add \ new \ choices \ via \ \token_to_str:N \cuaddkeys, ~ \token_to_str:N \cuaddsinglekeys ~ and \ \token_to_str:N \cuaddtokeys. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % If for an unit-key the value is wrong the following error message is shown. % \begin{macrocode} \msg_new:nnnn { cooking-units } { key-no-knows-value } { It \ appears \ that \ the \ key \ '#1' \ does \ not \ accept \ the \ value '#2'. \ Are \ you \ sure \ it \ is \ in \ the \ same \ key-chain? }{ The \ key \ accepts \ only \ a \ fixed \ set \ of \ choices. \ \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % Adding a message for unit-groups. % \begin{macrocode} \msg_new:nnnn { cooking-units } { group-choice-not-allowed } { The \ group \ key \ '#1' \ accepts \ only \ '#3' \ as \ a \ set \ of \ choices \ and \ '#2' \ is \ non \ of \ these. }{ The \ key \ accepts\ only \ a \ fixed \ set \ of \ choices. \ You \ can \ add \ new \ choices \ via \ option \ 'add-unit-to-group'. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % %\changes {v0.98a} {2016/06/11} {New option: 'value-not-known'.} %\changes {v0.99} {2016/06/12} {Renamed option: 'amount-not-known'.} % An info message for unknown messages. % \begin{macrocode} \msg_new:nnnn { cooking-units } { amount-not-known } { The \ amount \ '#1' \ is \ not \ known \ at \ line \ \msg_line_number: . }{ You \ used\ a\ special\ sign\ indicating\ that\ the\ true\ amount\ of\ the\ specific\ ingredient\ is\ (was) \ not\ known\ to\ you. This\ message\ reminds\ you\ about\ that\ fact. \msg_see_documentation_text:n {cooking-units} } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { group-key-unknown } { The \ group \ key \ '#1' \ is \ not \ defined. \ Perhaps \ you \ misspelled \ it? }{ The \ group \ key \ is \ not \ defined. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % If a load time option is not used as a package option, this % message is shown. % \begin{macrocode} \msg_new:nnnn { cooking-units } { load-time-option } { The \ option \ '#1' \ is \ only \ available \ as \ a \ load-time-option. \ Please \ set \ it \ as \ a \ package \ option. }{ You \ cannot \ set \ this \ option \ using \ \cusetup ~ but \ only \ as \ a \ package \ option. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Messages for obsolete commands. % \begin{macrocode} \msg_new:nnnn { cooking-units } { obsolete-command } { Command \ #1 is \ obsolete. \ Please \ use \ #2 instead. }{ Don't \ use \ this \ old \ command \ ... \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { No-Value-given } { Please \ input \ a \ value \ for \ the \ key-value \ list \ in \ '#1' }{ Each \ element \ of \ the \ list \ needs \ a \ value. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Adding-single-to-multi-key-or-vice-verca } { You \ are \ trying \ to \ add \ a \ single-key \ to \ a \ multi-key \ or \ vice-versa. }{ This \ is \ not \ allowed! \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Value-not-allowed } { The \ key-list \ in \ '#1' \ does \ not \ take \ a \ value. }{ This \ key-list \ does \ not \ take \ a \ value. \ The \ given \ value \ will \ be \ ignored. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Error message if a zero-point temperature is already defined. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Minimum-already-defined } { A \ minimum \ for \ '#1' \ has \ already \ been \ defined. }{ You \ cannot \ redefine \ it. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Using the key version of \mycs{culabel} one needs to give the number of % people the recipe is for after a \enquote{*}. % \begin{macrocode} \msg_new:nnnn { cooking-units } { Number-of-persons-missing } { Please \ add \ the \ number \ of \ persons \ this \ recipe \ is \ for \ in \ '#1'. \ Note \ that\ the \ number \ must \ be \ given \ after \ a \ '*'. }{ Write \ 'Schweinsbraten*4' \ to \ create \ the \ label \ 'Schweinsbraten' \ which \ is \ initially \ for \ 4 \ persons. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % The number of persons, must be an integer \dots\ % \begin{macrocode} \msg_new:nnnn { cooking-units } { Number-of-persons-is-not-an-integer } { The \ number \ of \ persons \ the \ recipe \ is \ for \ must \ be \ an \ integer. \ '#1' \ is \ not \ allowed. }{ The \ number \ '#1' \ is \ not \ allowed. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Each label defined by \mycs{culabel} needs to be new. % \begin{macrocode} \msg_new:nnnn { cooking-units } { label-already-defined } { The \ label \ '#1' \ has \ already \ been \ defined. }{ Each \ label \ must \ be \ unique. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % Message if a label is not defined. % \begin{macrocode} \msg_new:nnnn { cooking-units } { label-not-defined } { The \ label \ '#1' \ is \ not \ defined. \ Please \ note \ that \ a \ label \ has \ to \ defined \ before \ it \ is \ referenced. }{ Define \ the \ label \ before \ using \ it. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % If an unit is already defined and redefined by an \mycs{declarecookingunit}. % \begin{macrocode} \msg_new:nnnn { cooking-units } { redefine-unit } { The \ unit \ '#1' \ is \ redefined \ by \ \declarecookingunit ~ at \ \msg_line_context: . }{ \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % A \enquote{phrase} must be an integer. % \begin{macrocode} \msg_new:nnnn { cooking-units } { phrase-unit-not-an-integer } { A \ phrase \ must \ be \ an \ integer, \ '#1' \ is \ not \ allowed. }{ You \ can \ only \ use \ integers. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % A translation for \cs{cutext} or \cs{Cutext} is not available. For this case % -- instead of printing the key-name (see \pkg{translations}) -- we fall back % to \cs{cunum}. % \begin{macrocode} \msg_new:nnnn { cooking-units } { cutext-no-translation-available } { For \ the \ unit \ '#1' \ there \ exists \ no \ translation \ to \ be \ used \ for \ \cutext ~ and \ \token_to_str:N \Cutext. \ You \ can \ define \ new \ translations \ for \ a \ given \ language \ using \ \cudefinename. }{ \cunum ~ is \ used \ instead. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { Translation-not-available } { The \ translation \ for \ #1 \ does \ not \ exist. Please \ define \ it \ using \ \token_to_str:N\cudefinename. }{ And \ you \ may \ send \ me \ the \ translation \ as \ it \ is \ not \ available \ yet. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { formula-in-non-singlekeys } { The \ conversion \ value \ '#1' \ seems \ to \ be \ a \ formula. \ This \ is \ not \ allowed \ for \ NON-\ singlekeys! }{ \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { <-not-allowed-as-special-sign } { Currently \ (and \ probably \ forever) \ the \ sign \ '<' \ is \ not \ allowed \ to \ be \ used \ as \ a \ special \ sign. }{ I \ apologize \ for \ the \ inconvenience. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { unknown-gender } { '#1' \ is \ not \ allowed \ to \ be \ used \ as \ a \ gender-specification. Only \ 'm', \ 'f' \ or \ 'n' \ are \ allowed. }{ Please \ remove \ spaces \ if \ there \ are \ some. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { no-natural-unit-known } { It \ seems\ that \ I \ cannot \ convert \ '#1' \ in \ a \ natural \ unit \ as \ there \ is \ no \ conversion-factor \ defined. }{ Not \ converting \ the \unit. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { curef-not-inside-list } { '#1' \ cannot \ be \ removed \ from \ the \ 'forbidden \ unit' \ list \ because \ it \ is \ not \ inside \ the \ list. }{ Currently \ the \ list\ contains \ '#2'. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { curef-forbidden-unit } { The \ amount \ of \ '#1' \ cannot \ be \ changed \ using \ cooking-units` \ label \ and \ ref \ feature. }{ You \ can \ remove \ the \ unit \ using \ 'curef-remove-forbidden-unit'. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { define-numeral-function } { You \ are \ using \ numerals \ (lowercase) \ without \ the \ load-time \ option \ 'use-fmtcount-numerals' \ as \ such \ you \ need \ to \ define \ which \ function \ is \ to \ be \ used \ for \ numerals. \ You \ can \ do \ that \ via \ 'numeral-function' \ and \ 'Numeral-function' }{ Please \ specify\ which \ function \ should \ be \ used \ to \ print \ numerals (lowercase \ and \ uppercase). \msg_see_documentation_text:n { cooking-units } } \msg_new:nnnn { cooking-units } { define-Numeral-function } { You \ are \ using \ numerals \ (capitalized) \ without \ the \ load-time \ option \ 'use-fmtcount-numerals' \ as \ such \ you \ need \ to \ define \ which \ function \ is \ to \ be \ used \ for \ numerals. \ You \ can \ do \ that \ via \ 'numeral-function' \ and \ 'Numeral-function' }{ Please \ specify\ which \ function \ should \ be \ used \ to \ print \ numerals (lowercase \ and \ uppercase). \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % \begin{macrocode} \msg_new:nnnn { cooking-units } { 42-is-...-wrong? } { You \ requested \ to \ set \ 42 \ to \ 'false'. \ This \ doesn't \ make \ any \ sense! \ It \ cannot \ be \ 'false', \ it \ is \ the \ answer \ to \ the \ question \ of \ life, \ universe \ and \ everything. \ For \ the \ sake \ of \ the \ world \ I \ cannot \ allow \ you \ to \ set \ it \ to \ 'false', \ the \ universe \ might \ explode \ otherwise. \\ The \ run \ will \ be \ aborted. }{ Phew ... \ this \ might \ have \ ended \ disastrous. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { empty-amount } { The \ amount \ given \ is \ empty \ ( spaces \ are \ removed ). }{ The \ amount \ given \ is \ empty. \msg_see_documentation_text:n { cooking-units } } % \msg_new:nnnn { cooking-units } { unknown-unit-option } { The \ unit-option \ '#1' \ is \ not \ defined. }{ Please \ define \ the \ key \ '#1' \ via \ \newcukeysadd \ and \ friends. \msg_see_documentation_text:n { cooking-units } } \msg_new:nnnn { cooking-units } { unknown-option } { The \ option \ '#1' \ is \ not \ defined. }{ Perhaps \ you \ misspelled \ the \ option. \msg_see_documentation_text:n { cooking-units } } \msg_new:nnnn { cooking-units } { unknown-choice } { The \ choice \ '#2' \ for \ key \ '#1' \ is \ not \ defined. }{ Perhaps \ you \ misspelled \ the \ choice. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { internal-error } { This \ error \ shouldn't \ show. \ This \ means \ I \ (the \ package \ author) \ screwed \ up. \ The \ error \ code \ is \ '#1'. \ Addition \ is \ '#2'. }{ Please \ send \ me \ a \ bug \ report. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % % % \begin{macrocode} \msg_new:nnnn { cooking-units } { definition-key-mistyped? } { This \ message \ shows \ during \ the \ definition \ of \ unit-keys. \ You \ may \ have \ mistyped \ the \ key \ '#1'. \ You \ may \ see \ this \ message \ because \ you \ have \ used \ a \ '=' \ sign \ in \ the \ optional \ argument. \ If \ this \ is \ the \ case \ and \ you \ still \ want \ to \ use \ '=' \ in \ a \ symbol, \ please \ wrap \ the \ symbol \ in \ two \ braces: \ {{#1=#2}}. }{ Or \ try \ using \ the \ 'symbol' \ option. \msg_see_documentation_text:n { cooking-units } } % \end{macrocode} % \end{macro} % % \section {Helper Macros} % % The name says it all. % % % % %\begin{macro} % { % \@@_if_unit_defined:nN, % \@@_error_if_unit_not_defined:nNTF, % \@@_error_if_unit_not_defined:nTF, % \@@_error_if_unit_not_defined:VTF, % } % A little helper macro. Checks if the unit is defined, if not raise an error. % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_if_unit_defined:nN #1 #2 { TF , T } { \@@_set_unit:Nn #2 {#1} \seq_if_in:NVTF \g_@@_list_of_defined_units_seq #2 { \prg_return_true: } { \prg_return_false: } } \prg_new_protected_conditional:Npnn \@@_error_if_unit_not_defined:nN #1#2 { TF, T, F } { \@@_if_unit_defined:nNTF {#1} #2 { \prg_return_false: } { \msg_error:nnn { cooking-units } { unknown-unit } {#1} \prg_return_true: } } \prg_new_protected_conditional:Npnn \@@_error_if_unit_not_defined:n #1 { TF, T, F } { \@@_error_if_unit_not_defined:nNTF {#1} \l_@@_scrap_tl { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \@@_error_if_unit_not_defined:nTF { V } % \end{macrocode} %\end{macro} % % % % \begin{macro} { \@@_if_unit_has_a_key:n, \@@_if_unit_has_a_key:V } % A little helper macro. Checks if the unit is defined, if not raise an error. % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_if_unit_has_a_key:n #1 { T , F, TF } { \keys_if_exist:nnTF { cooking-units } {#1} { \prg_return_true: } { \prg_return_false: } } \prg_generate_conditional_variant:Nnn \@@_if_unit_has_a_key:n { V } { T, TF } % \end{macrocode} %\end{macro} % % % \begin{macro} { \@@_seq_use_msg:N, \@@_seq_use_msg:c } % Used in error messages for units. Prints the units in a comma-seperated list. % \begin{macrocode} \cs_new:Npn \@@_seq_use_msg:N #1 { \seq_use:Nnnn #1 { ' , ~ ' } { ' , ~ ' } { ' ~ and ~ ' } } \cs_generate_variant:Nn \@@_seq_use_msg:N { c } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_if_integer:n , \@@_if_integer:V , \@@_if_evaluation_is_integer:nN } % Checking if the input consists only of numbers; or only of numbers and % the allowed math operators % \begin{macrocode} \prg_new_protected_conditional:Npnn \@@_if_integer:n #1 { TF , F } { \tl_map_inline:nn {#1} { \tl_if_in:NnF \l_@@_input_digits_tl {##1} { \tl_map_break:n { \use_i:nn \prg_return_false: } } } \prg_return_true: } \cs_generate_variant:Nn \@@_if_integer:nTF { V } \prg_new_protected_conditional:Npnn \@@_if_evaluation_is_integer:n #1 { TF , T , F } { \tl_concat:NNN \l_@@_tmpa_tl \l_@@_input_digits_tl \l_@@_allwed_integer_math_operators_tl \tl_map_inline:nn {#1} { \tl_if_in:NnF \l_@@_tmpa_tl {##1} { \tl_map_break:n { \use_iii:nnn } } } \use_i:nn { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{ \@@_if_integers:NNTF } % Checking if the input consists only of numbers, which works correctly for ranges. % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_integers:NN #1#2 { TF } { \@@_if_integer:VTF #1 { \@@_if_print_type_equal:nTF { range } { \@@_if_integer:VTF #2 { \prg_return_true: } { \prg_return_false: } } { \prg_return_true: } } { \prg_return_false: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_if_contains_decimal:NTF , \@@_check_for_decimal:w } % A fast check if |#1| contains a decimal point. This is only % used for already parsed input, so the input is already safe to check. % % If |#1| does not contain a decimal point, |\@@_check_for_decimal:w| % |#2| is equal to |\q_nil|, the test is true and the false branch is executed. % % If |#1| does contain a decimal point, |#2| is going equal to |<...>.\q_nil| % where |<...>| is the stuff after the decimal point. The test is false, thus the % true branch is ignored (ignoring all other tokens in |#2|). % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_contains_decimal:N #1 { TF } { \exp_after:wN \@@_check_for_decimal:w #1 . \q_nil . \q_stop } \cs_new:Npn \@@_check_for_decimal:w #1 . #2 . \q_stop { \if_meaning:w \q_nil #2 \exp_after:wN \prg_return_false: \else: \exp_after:wN \prg_return_true: \fi: } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_if_contain_decimals:NNTF} % Same as |\@@_if_integers:NNTF| only for decimal points. % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_contain_decimals:NN #1#2 { TF } { \@@_if_contains_decimal:NTF #1 { \prg_return_true: } { \@@_if_print_type_equal:nTF { range } { \@@_if_contains_decimal:NTF #2 { \prg_return_true: } { \prg_return_false: } } { \prg_return_false: } } } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{ \@@_if_gender_error:n } % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_gender_error:n #1 { TF, F , T } { \str_case:nnTF {#1} { { m } { } { f } { } { n } { } } { \prg_return_false: } { \msg_error:nnn { cooking-units } { unknown-gender } {#1} \prg_return_true: } } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{\@@_error_if_group_not_defined:nN, \@@_error_if_group_not_defined:n} % \begin{macrocode} \prg_new_conditional:Npnn \@@_error_if_group_not_defined:nN #1#2 { F, T , TF } { \@@_set_unit:Nn #2 {#1} \seq_if_exist:cTF { l_@@_group_ #2 _seq } { \prg_return_false: } { \msg_error:nn { cooking-units} { group-key-unknown } \prg_return_true: } } \prg_new_conditional:Npnn \@@_error_if_group_not_defined:n #1 { F } { \seq_if_exist:cTF { l_@@_group_ \@@_to_unit:n {#1} _seq } { \prg_return_false: } { \msg_error:nn { cooking-units} { group-key-unknown } \prg_return_true: } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_define_clear_unit_group:n} % \begin{macrocode} \cs_new:Npn \@@_define_clear_unit_group:n #1 { \@@_set_unit:Nn \l_@@_tmpa_tl {#1} \seq_clear_new:c { l_@@_group_ \l_@@_tmpa_tl _seq } \keys_define:nn { cooking-units } { #1 .code:n = { \@@_unit_group_convert_units:nn {#1} {##1} } , #1 .groups:n = { change-unit } , } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_unit_group_convert_units:nn} % \begin{macrocode} \cs_new:Npn \@@_unit_group_convert_units:nn #1#2 { \bool_set_true:N \l_@@_tmpa_bool \@@_error_if_group_not_defined:nNT {#1} \l_@@_curr_group_tl { \bool_set_false:N \l_@@_tmpa_bool } \@@_error_if_unit_not_defined:nNT {#2} \l_@@_curr_unit_tl { \bool_set_false:N \l_@@_tmpa_bool } \bool_if:NT \l_@@_tmpa_bool { \seq_if_in:cVTF { l_@@_group_ \l_@@_curr_group_tl _seq } \l_@@_curr_unit_tl { \seq_map_inline:cn { l_@@_group_ \l_@@_curr_group_tl _seq } { \@@_keys_set:nnn { cooking-units } {##1} {#2} } }{ \msg_error:nnxxx { cooking-units } { group-choice-not-allowed } { \l_@@_curr_group_tl } { \l_@@_curr_unit_tl } { \@@_seq_use_msg:c { l_@@_group_ \l_@@_curr_group_tl _seq } } } } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_add_to_unit_group_error:n , \@@_add_to_unit_group:nn } % \begin{macrocode} \cs_new:Npn \@@_add_to_unit_group_error:n #1 { \msg_error:nnn { cooking-units } { No-Value-given } { add-unit-to-group } } % \end{macrocode} % \begin{macrocode} \cs_new:Npn \@@_add_to_unit_group:nn #1#2 { \@@_error_if_group_not_defined:nNF {#1} \l_@@_curr_group_tl { \clist_map_inline:nn {#2} { \@@_error_if_unit_not_defined:nNF {##1} \l_@@_curr_unit_tl { \seq_if_in:cVF { l_@@_group_ \l_@@_curr_group_tl _seq } \l_@@_curr_unit_tl { \seq_put_right:cV { l_@@_group_ \l_@@_curr_group_tl _seq } \l_@@_curr_unit_tl } } } } } \cs_generate_variant:Nn \@@_add_to_unit_group:nn { nV } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_convert_to_eV:} % Still work in progress. Will probably forever be. Changes all % predefined units into natural units (except for those who cannot % be changed, like |Msp.| for example). % % \begin{macrocode} \cs_new:Npn \@@_seq_get_overlap:NNTF #1#2 #3#4 { \seq_map_inline:Nn #1 { \seq_if_in:NnT #2 {##1} { #3 \seq_map_break:n { \use_none:nn } } } \use:n {#4} } % \end{macrocode} % \begin{macrocode} \cs_new:Npn \@@_keys_set:nnn #1#2#3 { \keys_set:nn {#1} { #2 = #3 } } \cs_generate_variant:Nn \@@_keys_set:nnn { nVV , nVn } % \end{macrocode} % \begin{macrocode} \cs_new:Npn \@@_convert_to_eV:N #1 { \prop_get:NVNTF \g_@@_convert_to_eV_remember_prop #1 \l_@@_tmpa_tl { \@@_keys_set:nVV { cooking-units } #1 \l_@@_tmpa_tl } { % #1 is already a string \@@_if_unit_has_a_key:VTF #1 { \tl_set_eq:Nc \l_@@_tmp_unit_tl { l_@@_base_key_unit_ #1 _tl } \prop_get:cnNT { l_@@_rf_cukeys_ \l_@@_tmp_unit_tl _prop } { Liste } \l_@@_tmpa_seq { \@@_seq_get_overlap:NNTF \g_@@_natural_units_seq \l_@@_tmpa_seq { \prop_gput:NVn \g_@@_convert_to_eV_remember_prop #1 {##1} \@@_keys_set:nVn { cooking-units } #1 {##1} } { \msg_warning:nnx { cooking-units } { no-natural-unit-known } {#1} } } } { \msg_warning:nnx { cooking-units } { Key-not-defined } {#1} } } } % \end{macrocode} %\end{macro} % % \begin{macrocode} \cs_new:Npn \@@_changing_unit_bonkers:N #1 { \prop_get:NVNTF \g_@@_convert_bonkers_remember_prop #1 \l_@@_tmpa_tl { \@@_keys_set:nVV { cooking-units } #1 \l_@@_tmpa_tl } { \tl_case:NnF \l_@@_bonkers_state_tl { \c_@@_bonkers_state_going_tl { \@@_convert_going_bonkers:N #1 } \c_@@_bonkers_state_fully_tl { \@@_convert_fully_bonkers:N #1 } \c_@@_bonkers_state_xD_tl { \@@_convert_xD_bonkers:N #1 } } { \@@_create_internal_error:nN { going-bonkers } \l_@@_bonkers_state_tl } } } \cs_new:Npn \@@_convert_going_bonkers:N #1 { \@@_if_unit_has_a_key:VT #1 { \@@_prepare_going_bonkers:N #1 \keys_set:nn { cooking-units } { eval-fraction = true } \@@_keys_set:nVV { cooking-units } #1 \l_@@_tmpa_tl \prop_gput:NVV \g_@@_convert_bonkers_remember_prop #1 \l_@@_tmpa_tl } } \cs_new:Npn \@@_convert_fully_bonkers:N #1 { \@@_if_unit_has_a_key:VT #1 { \@@_prepare_going_bonkers:N #1 \keys_set:nn { cooking-units } { eval-fraction = true , curef-clear-forbidden-units = true } \prop_put:NVV \l_@@_change_unit_prop {#1} \l_@@_tmpa_tl \@@_cukeys_calculate_change_value:VVV #1 \l_@@_tmpa_tl \l_@@_tmp_unit_tl } } \cs_new:Npn \@@_prepare_going_bonkers:N #1 { \tl_set_eq:Nc \l_@@_tmp_unit_tl { l_@@_base_key_unit_ #1 _tl } \prop_get:cnN { l_@@_rf_cukeys_ \l_@@_tmp_unit_tl _prop } { Liste } \l_@@_tmpa_seq \bool_set_false:N \l_@@_tmpa_bool \bool_until_do:Nn \l_@@_tmpa_bool { \tl_set:Nf \l_@@_tmpa_tl { \seq_rand_item:N \l_@@_tmpa_seq } \seq_if_in:NVF \g_@@_natural_units_seq \l_@@_tmpa_tl { \bool_set_true:N \l_@@_tmpa_bool } } } \cs_new:Npn \@@_convert_xD_bonkers:N #1 { \keys_set:nn { cooking-units } { eval-fraction = true , curef-clear-forbidden-units = true } \bool_set_false:N \l_@@_tmpa_bool \bool_until_do:Nn \l_@@_tmpa_bool { \tl_set:Nx \l_@@_tmpa_tl { \seq_rand_item:N \g_@@_list_of_defined_units_seq } \@@_if_no_unit_marker:NF \l_@@_tmpa_tl { \bool_set_true:N \l_@@_tmpa_bool } } \prop_put:NVV \l_@@_change_unit_prop {#1} \l_@@_tmpa_tl \tl_set:cf { l_@@_unit_change_ #1 _tl } { \fp_eval:n { rand() * randint(255) } } } % \end{macrocode} % % % % \begin{macro}{\@@_tl_set_eval:Nn} % I am using this construction often enough, so \dots\ yeah. % \begin{macrocode} \cs_new:Npn \@@_tl_set_eval:Nn #1#2 { \tl_set:Nf #1 { \fp_eval:n {#2} } } \cs_generate_variant:Nn \@@_tl_set_eval:Nn { Nv, cn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_bool_reverse:N } % Also used sometimes. % \begin{macrocode} \cs_new:Npn \@@_bool_reverse:N #1 { \bool_if:NTF #1 { \bool_set_false:N } { \bool_set_true:N } #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_seq_put_right:Nnnnnn, \@@_rf_seq_put_right:NnVVVV, \@@_rf_seq_put_right:NnnnnV} % Nothing special here, just a wrapper used later. % \begin{macrocode} \cs_new:Npn \@@_rf_seq_put_right:Nnnnnn #1 #2#3#4#5#6 { \seq_put_right:Nn #1 { {#2} {#3} {#4} {#5} {#6} } } \cs_generate_variant:Nn \@@_rf_seq_put_right:Nnnnnn { NnVVVV, NnnVVV, NnnnnV } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_change_unit:TF } % I am using this construction often enough, so \dots\ yeah. % Note that if the evaluation is |true|, the path taken is the |false| one. % \begin{macrocode} \prg_new_conditional:Npnn \@@_change_unit: { TF } { \@@_if_state_equal:nTF { special_sign } { \prg_return_false: } { \bool_if:NTF \l_@@_rf_contains_fraction_bool { \bool_if:NTF \l_@@_lokal_eval_fractions_bool { \prg_return_true: } { \prg_return_false: } } { \prg_return_true: } } % \bool_if:NTF \l_@@_rf_contains_fraction_bool % { \prg_return_false: } % { % \@@_if_state_equal:nTF { special_sign } % { \prg_return_false: } % { \prg_return_true: } % } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_temperature_to_check_print_error:n,\@@_temperatures_to_check_define:nn} % Commands used in the option \texttt{temperature-to-check}. % If no value is given (first command) it raises an error. If a value % is given the unit is added to a check-list and a new % constant is defined. % \begin{macrocode} \cs_new:Npn \@@_temperature_to_check_print_error:n #1 { \@@_error_if_unit_not_defined:n {#1} \msg_error:nnn { cooking-units } { No-Value-given } { add-temperature-to-check } } \cs_new:Npn \@@_temperatures_to_check_define:nn #1#2 { \@@_error_if_unit_not_defined:nNF {#1} \l_@@_curr_unit_tl { \fp_if_exist:cF { l_@@_ \l_@@_curr_unit_tl _min_fp } { \seq_put_right:NV \l_@@_temperatures_to_check_seq \l_@@_curr_unit_tl \fp_new:c { l_@@_ \l_@@_curr_unit_tl _min_fp } } \fp_set:cn { l_@@_ \l_@@_curr_unit_tl _min_fp } {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_set_options_for_error:n } % \begin{macrocode} \cs_new:Npn \@@_set_options_for_error:n #1 { \@@_error_if_unit_not_defined:n {#1} \msg_error:nnn { cooking-units } { No-Value-given } { set-option-for } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_add_options_for_error:n } % \begin{macrocode} \cs_new:Npn \@@_add_options_for_error:n #1 { \@@_error_if_unit_not_defined:n {#1} \msg_error:nnn { cooking-units } { No-Value-given } { add-option-for } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rounding_function:n} % I think this definition is somewhat stupid, but I don't have a better idea. % \begin{macrocode} \cs_new:Npn \@@_rounding_function:n #1 { #1 , \l_@@_round_precision_int \bool_if:NT \l_@@_pckopt_round_commercial_bool { , #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_label_and_persons:n} % \begin{macrocode} \cs_new:Npn \@@_label_and_persons:n #1 { \str_if_in:nnTF {#1} { * } { \@@_label_and_persons_times_persons:ww #1 \q_stop } { \msg_error:nnn { cooking-units} { Number-of-persons-missing } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \cs_new:Npn \@@_label_and_persons_times_persons:ww #1 * #2 \q_stop { \@@_culabel:nn {#1} {#2} } % \end{macrocode} % % \begin{macro}{ \@@_culabel:nn } % \begin{macrocode} \cs_new:Npn \@@_culabel:nn #1#2 { \int_if_exist:cTF { g_@@_number_of_persons_ #1 _int } { \msg_error:nnn { cooking-units } { label-already-defined } {#1} } { \@@_if_evaluation_is_integer:nTF {#2} { \int_set:Nn \l_@@_tmpa_int {#2} \int_new:c { g_@@_number_of_persons_ #1 _int } \int_gset_eq:cN { g_@@_number_of_persons_ #1 _int } \l_@@_tmpa_int } { \msg_error:nnn { cooking-units } { Number-of-persons-is-not-an-integer } {#2} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_reference_label_and_persons:n } % \begin{macrocode} \cs_new:Npn \@@_reference_label_and_persons:n #1 { \int_if_exist:cTF { g_@@_number_of_persons_ #1 _int } { \bool_set_true:N \l_@@_reference_given_bool \int_set_eq:Nc \l_@@_local_number_of_persons_int { g_@@_number_of_persons_ #1 _int } } { \msg_error:nnn { cooking-units } { label-not-defined } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_curef:n } % \begin{macrocode} \cs_new:Npn \@@_curef:n #1 { \int_if_exist:cTF { g_@@_number_of_persons_ #1 _int } { \bool_if:NTF \l_@@_pckopt_recalculate_amount_bool { \int_use:N \l_@@_wanted_number_of_persons_int } { \int_use:c { g_@@_number_of_persons_ #1 _int } } }{ \msg_error:nnn { cooking-units } { label-not-defined } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \culabel , \curef } % \begin{macrocode} \NewDocumentCommand \culabel { m m } { \@@_culabel:nn {#1} {#2} } \NewExpandableDocumentCommand \curef { m } { \@@_curef:n {#1} } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{ \@@_check_minus_remove_and_reverse:NN } % Can probably optimize this as the sign is always at the first place % (this is done after cheking the input, so the assumption is safe), but yeah \dots % \begin{macrocode} \cs_new:Npn \@@_check_minus_remove_and_reverse:NN #1#2 { \quark_if_nil:NF #2 { %% Maybe simplify by a command? \tl_if_in:NnT #2 { - } { \tl_remove_once:Nn #2 { - } \@@_bool_reverse:N #1 } \@@_check_minus_remove_and_reverse:NN #1 } } % \end{macrocode} % \end{macro} % % % % % % % % % % \begin{macrocode} \cs_new:Npn \@@_add_forbidden_unit:n #1 { \seq_set_from_clist:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_error_if_unit_not_defined:nNF {##1} \l_@@_curr_unit_tl { \seq_if_in:NVF \l_@@_curef_do_not_change_seq \l_@@_curr_unit_tl { \seq_put_right:NV \l_@@_curef_do_not_change_seq \l_@@_curr_unit_tl } } } } \cs_new:Npn \@@_remove_forbidden_unit:n #1 { \seq_set_from_clist:Nn \l_@@_tmpa_seq {#1} \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_error_if_unit_not_defined:nNF {##1} \l_@@_curr_unit_tl { \seq_if_in:NVTF \l_@@_curef_do_not_change_seq \l_@@_curr_unit_tl { \seq_remove_all:NV \l_@@_curef_do_not_change_seq \l_@@_curr_unit_tl } { \msg_warning:nnnn { cooking-units } { curef-not-inside-list } {##1} { \@@_seq_use_msg:N \l_@@_curef_do_not_change_seq } } } } } \cs_new:Npn \@@_clear_forbidden_unit: { \seq_clear:N \l_@@_curef_do_not_change_seq } % \end{macrocode} % % % % % % % \begin{macrocode} \NewDocumentCommand \cudeclareunitgroup { m m } { \@@_define_clear_unit_group:n {#1} \@@_add_to_unit_group:nn {#1} {#2} } \NewDocumentCommand \cuaddtounitgroup { m m } { \@@_add_to_unit_group:nn {#1} {#2} } \NewDocumentCommand \cuclearunitgroup { m } { \@@_error_if_group_not_defined:nF {#1} { \@@_define_clear_unit_group:n {#1} } } % \end{macrocode} % % % % % % % % % \begin{macro}{ \cusetoptionfor , \cuaddoptionfor , \cuclearoptionfor} % \pkg{cooking-units} allows to attach options to units. Every time the unit % is used, the option is executed (technically it is similar or equal to a hook). % Using these commands, one can set (overwrites all previous options), % add or clear options for a unit or a list of units. % \begin{macrocode} \NewDocumentCommand \cusetoptionfor { m m } { \@@_set_options_for:nn {#1} {#2} } \NewDocumentCommand \cuaddoptionfor { m m } { \@@_add_options_for:nn {#1} {#2} } \NewDocumentCommand \cuclearoptionfor { m } { \@@_clear_options_for:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_set_options_for:nn , \@@_add_options_for:nn , \@@_clear_options_for:n} % These commands are quite similar, excpet for a few details. % First, store the unit or list of units |#1| and the list of options |#2|. % At the end, for each unit in |#1| a clist will be created (if not already existing) % where the options will be put \emph{via} |\clist_set:cn|, |\clist_put_right:cn| % or will be cleared (see there). % % E.g.\ |\clist_set:cn| will get something like |\clist_set:cV {...unit...clist} \clist_containing_options_clist|. % As |\clist_clear:c| does not take a second argument, we will eat it \emph{via} % |\use_i_ii:nnn|. % \begin{macrocode} \cs_new:Npn \@@_set_options_for:nn #1#2 { \@@_map_trough_valid_unit_options:nnn {#1} {#2} { \clist_set:cV } } \cs_new:Npn \@@_add_options_for:nn #1#2 { \@@_map_trough_valid_unit_options:nnn {#1} {#2} { \clist_put_right:cV } } \cs_new:Npn \@@_clear_options_for:n #1 { \@@_map_trough_valid_unit_options:nnn {#1} { \q_no_value } { \clist_clear:c } } \cs_generate_variant:Nn \@@_set_options_for:nn { V } \cs_generate_variant:Nn \@@_clear_options_for:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_map_trough_valid_unit_options:nnn} % The first thing is command does is to map through the given unit list |#1| % and check if the unit(s) exist or not. If they exist, store them in the sequence % |\l_@@_tmpa_seq| for later use. Else, ignore it and print a warning. % % If |#2| is a |\q_no_value| we know that there is no need to map through |#2|. % Whatever the command |#3| does, it does so only to the clist % |l_@@_unit_option_ _clist|. % % If |#2| is anything else, follow the |else| branch. After veryfing that |#2| % is non-empty map through the option list given in |#2|. All valid keys % (and their agrument) is stored in |\l_@@_tmpa_clist|. % Afterwards, the clist for each unit in |\l_@@_tmpa_seq| is modified by the command % in |#3| and is given the list of options |\l_@@_tmpa_clist| as an argument. % (Note: |\l_@@_tmpa_clist| can be empty). % \begin{macrocode} \cs_new:Npn \@@_map_trough_valid_unit_options:nnn #1#2 #3 { \seq_clear:N \l_@@_tmpa_seq \clist_clear:N \l_@@_tmpa_clist \clist_map_inline:nn {#1} { \@@_error_if_unit_not_defined:nNF {##1} \l_@@_curr_unit_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_curr_unit_tl } } \quark_if_no_value:nTF {#2} { \seq_map_inline:Nn \l_@@_tmpa_seq { #3 { l_@@_unit_option_ ##1 _clist } } }{ \tl_if_empty:nF {#2} { \@@_unit_options_check_validity:n {#2} } \seq_map_inline:Nn \l_@@_tmpa_seq { #3 { l_@@_unit_option_ ##1 _clist } \l_@@_tmpa_clist } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_unit_options_check_validity:n } % If the list of options is non-empty, we parse over them using % |\keyval_parse:NNn|. For this we need two commands, one for keys % with argument, one without arguments. All the commands do is to check % if the key given does exist. And if they do, store them in |\l_@@_tmpa_clist|. % \begin{macrocode} \cs_new:Npn \@@_unit_options_check_validity:n #1 { \keyval_parse:NNn \@@_unit_options_check_validity_aux_i:n \@@_unit_options_check_validity_aux_ii:nn {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_unit_options_check_validity_aux_i:n , % \@@_unit_options_check_validity_aux_ii:nn } % Check if the key exist; if it is then put it in |\l_@@_tmpa_clist| (just the key, or with the value). % Sadly, checking if the value |#2| can be accepted by the key is not really testable. % \begin{macrocode} \cs_new:Npn \@@_unit_options_check_validity_aux_i:n #1 { \keys_if_exist:nnTF { cooking-units } {#1} { \clist_put_right:Nn \l_@@_tmpa_clist {#1} } { \msg_error:nnn { cooking-units } { unknown-option } {#1} } } \cs_new:Npn \@@_unit_options_check_validity_aux_ii:nn #1#2 { \keys_if_exist:nnTF { cooking-units } {#1} { \clist_put_right:Nn \l_@@_tmpa_clist { #1= {#2} } % \keys_if_choice_exist:nnnTF { cooking-units } {#1} {#2} % { \clist_put_right:Nn \l_@@_tmpa_clist { #1= {#2} } } % { \msg_error:nnnn { cooking-units } { unknown-choice } {#1} {#2} } } { \msg_error:nnn { cooking-units } { unknown-option } {#1} } } % \end{macrocode} % \end{macro} % % % % % % % % % % % \subsection{Language Macros} % % % The following commands are just wrapper around the commands from the % \pkg{translations} package. % % \begin{macro}{ \@@_culang_def_base:nnn , \@@_culang_def_base:nnv , \@@_culang_def_base:nVn} % \begin{macrocode} \cs_new:Npn \@@_culang_def_base:nnn #1#2#3 { \declaretranslationfallback { #2 - #1 (@@) } {#3} } \cs_generate_variant:Nn \@@_culang_def_base:nnn { nnv, nVn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_def_for:nnnn, \@@_culang_def_for:VnnV , \@@_culang_def_for:Vnnn } % \begin{macrocode} \cs_new:Npn \@@_culang_def_for:nnnn #1#2#3#4 { \declaretranslation {#1} { #3 - #2 (@@) } {#4} } \cs_generate_variant:Nn \@@_culang_def_for:nnnn { VnnV, V } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_print:nn , \@@_culang_print:nV } % \begin{macrocode} \cs_new:Npn \@@_culang_print:nn #1#2 { \GetTranslation { #2 - #1 (@@) } } \cs_generate_variant:Nn \@@_culang_print:nn { nV } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_let:nnN } % \begin{macrocode} \cs_new:Npn \@@_culang_let:nnN #1#2#3 { \SaveTranslation #3 { #2 - #1 (@@) } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_let_for:nnnN , \@@_culang_let_for:VnnN } % \begin{macrocode} \cs_new:Npn \@@_culang_let_for:nnnN #1#2#3#4 { \SaveTranslationFor #4 {#1} { #3-#2 (@@) } } \cs_generate_variant:Nn \@@_culang_let_for:nnnN { V } % \end{macrocode} % \end{macro} % % \begin{macro}[TF]{ \@@_culang_if_translation_exists_for:nnn, \@@_culang_if_translation_exists_for:Vnn} % \begin{macrocode} \cs_new:Npn \@@_culang_if_translation_exists_for:nnnTF #1#2#3 { \IfTranslation {#1} { #3 - #2 (@@) } } \cs_generate_variant:Nn \@@_culang_if_translation_exists_for:nnnTF { V } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}[TF, F]{\@@_culang_unitname_let:Nn , \@@_culang_unitname_let:NV } % \begin{macrocode} \prg_new_conditional:Npnn \@@_culang_unitname_let:Nn #1#2 { TF, F , T } { \@@_culang_let:nnN { name } {#2} #1 \tl_if_eq:NNTF #1 \c_@@_no_translation_str { \prg_return_false: } { \prg_return_true: } } \cs_generate_variant:Nn \@@_culang_unitname_let:NnTF { NV, Nx } \cs_generate_variant:Nn \@@_culang_unitname_let:NnT { Nx } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_one_let:Nn , \@@_culang_one_let:NV , % \@@_culang_one_let_by_gender:Nn , \@@_culang_one_let_by_gender:NV % } % \begin{macrocode} \prg_new_conditional:Npnn \@@_culang_one_let:Nn #1#2 { T } { \@@_culang_let:nnN { gender } {#2} \l_@@_tmpa_tl \@@_culang_one_let_by_gender:NVTF #1 \l_@@_tmpa_tl { \prg_return_true: } { \tl_set:Nn #1 { 1 } \prg_return_false: } } \cs_generate_variant:Nn \@@_culang_one_let:NnT { NV } \prg_new_conditional:Npnn \@@_culang_one_let_by_gender:Nn #1#2 { TF } { \@@_culang_unitname_let:NnTF #1 { one (#2) } { \prg_return_true: } { \msg_error:nnx { cooking-units } { Translation-not-available } { one (#2) } \prg_return_false: } } \cs_generate_variant:Nn \@@_culang_one_let_by_gender:NnTF { NV } % \end{macrocode} % \end{macro} % % \begin{macro}{ \g_@@_allowed_special_keys_seq } % Defining the translation for the decimal-mark. Note that those % 'phrases' are stored inside % \verb|\g_@@_allowed_special_keys_seq|. % Furthermore some translations are defined. % \begin{macrocode} \seq_gset_from_clist:Nn \g_@@_allowed_special_keys_seq { cutext-range-sign , cutext-range-phrase , decimal-mark , one (m) , one (f) , one (n) } % \end{macrocode} % \end{macro} % % \begin{macrocode} \@@_culang_def_base:nnn { name } { decimal-mark } { . } \@@_culang_def_base:nnn { name-pl } { decimal-mark } { \c_@@_no_translation_str } \@@_culang_def_for:nnnn { German } { name-pl } { decimal-mark } { , } \@@_culang_def_for:nnnn { French } { name-pl } { decimal-mark } { , } % \end{macrocode} % Note that the plural versions just exist for completing the set. % \begin{macrocode} \@@_culang_def_base:nnn { name } { one (m) } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name } { one (f) } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name } { one (n) } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name-pl } { one (m) } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name-pl } { one (f) } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name-pl } { one (n) } { \c_@@_no_translation_str } % \end{macrocode} % \begin{macrocode} \@@_culang_def_for:nnnn { English } { name } { one (m) } { one } \@@_culang_def_for:nnnn { English } { name } { one (f) } { one } \@@_culang_def_for:nnnn { English } { name } { one (n) } { one } % \end{macrocode} % \begin{macrocode} \@@_culang_def_for:nnnn { German } { name } { one (m) } { ein } \@@_culang_def_for:nnnn { German } { name } { one (f) } { eine } \@@_culang_def_for:nnnn { German } { name } { one (n) } { ein } % \end{macrocode} % \begin{macrocode} \@@_culang_def_for:nnnn { French } { name } { one (m) } { un } \@@_culang_def_for:nnnn { French } { name } { one (f) } { une } \@@_culang_def_for:nnnn { French } { name } { one (n) } { un } % \end{macrocode} % % \begin{macrocode} \@@_culang_def_base:nnn { name } { cutext-range-phrase } { -- } %\@@_culang_def_base:nnn { name } { cutext-range-sign } { -- } %\@@_culang_def_base:nnn { name-pl } { cutext-range-sign } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { name-pl } { cutext-range-phrase } { \c_@@_no_translation_str } %\@@_culang_def_for:nnnn { German } { name } { cutext-range-sign } { ~bis~ } %\@@_culang_def_for:nnnn { English } { name } { cutext-range-sign } { ~to~ } %\@@_culang_def_for:nnnn { French } { name } { cutext-range-sign } { ~\`{a}~ } \@@_culang_def_for:nnnn { German } { name } { cutext-range-phrase } { ~bis~ } \@@_culang_def_for:nnnn { English } { name } { cutext-range-phrase } { ~to~ } \@@_culang_def_for:nnnn { French } { name } { cutext-range-phrase } { ~\`{a}~ } % \end{macrocode} % % % \section{General} % % This section collects commands which are used by all four main commands % (\mycs{cunum}, \cCutext, \mycs{cuam}), hence the name. % % % \subsection{Enums \& States} % % These variables decide what the main commands (\cs{cunum}, etc.) should do. % Is there an error? Special-sign? Fraction? Range? Which command is used? % This is defined here. % % \begin{macro}{ \@@_def_new_type:nnn , \@@_def_new_type_aux:cnn , \@@_def_new_type_aux:Nnn} % Defines my variants, all in one handy command (well, two commands). % |#1| is the base name of the new state. |#2| are the conditions for % |\prg_new_conditional|. |#3| are the possible states, given as a comma list. % \begin{macrocode} \cs_new:Npn \@@_def_new_type:nnn #1#2#3 { \tl_new:c { l_@@_ #1 _tl } \clist_map_inline:nn {#3} { \tl_const:cn { c_@@_ #1 _ ##1 _tl } {##1} } \@@_def_new_type_aux:cnn { l_@@_ #1 _tl } {#1} {#2} } % \end{macrocode} % Defines the commands |@@_set__to:n| and (if wanted) % |@@_if_ _equal:n|. For maybe optimization (not sure why) % I decided that |#1| is the token varable which stores the state we are currently % in. % \begin{macrocode} \cs_new:Npn \@@_def_new_type_aux:Nnn #1#2#3 { \cs_new_nopar:cpn { @@_set_ #2 _to:n } ##1 { \tl_if_exist:cF { c_@@_#2_ ##1 _tl } { \msg_error:nnn {cooking-units } { internal-error } { Type } { ERROR-set-#2-##1 } } \tl_set_eq:Nc #1 { c_@@_#2_ ##1 _tl } } \tl_if_empty:nF {#3} { \prg_new_conditional:cpnn { @@_if_ #2 _equal:n } ##1 {#3} { \tl_if_exist:cF { c_@@_#2_ ##1 _tl } { \msg_error:nnnn {cooking-units } { internal-error } { Type-2 } { ERROR-set-#2-##1 } } \tl_if_eq:NcTF #1 { c_@@_#2_ ##1 _tl } { \prg_return_true: } { \prg_return_false: } } } } \cs_generate_variant:Nn \@@_def_new_type_aux:Nnn { c } % \end{macrocode} % \end{macro} % % Defining some states. % \begin{macrocode} \@@_def_new_type:nnn { state } { TF , T , F , p } { normal , special_sign , 42 , error } \@@_def_new_type:nnn { cmd_type } { TF , T , p } { cunum , cCutext , cuam } \@@_def_new_type:nnn { bonkers_state } { } { normal , going , fully , xD } % \end{macrocode} % % Setting a default state.. % \begin{macrocode} \tl_set_eq:NN \l_@@_bonkers_state_tl \c_@@_bonkers_state_normal_tl % \end{macrocode} % % % \subsection{The main stuff} % % \begin{macro} { \@@_expand_unit_input:Nn , \@@_expand_amount_input:Nn } % \begin{macrocode} \cs_new:Npn \@@_expand_unit_input:Nn #1#2 { \cs_if_exist_use:c { exp_args:NN \l_@@_unit_argument_spec_tl } \@@_set_unit:Nn #1 {#2} } \cs_new:Npn \@@_expand_amount_input:Nn #1#2 { \cs_if_exist_use:c { exp_args:NN \l_@@_expand_amount_tl } \@@_set_amount:Nn #1 {#2} } % \end{macrocode} % \end{macro} % % % % \begin{macro}{ \@@_rf_main_command:nnnnnnm \@@_rf_main_command:nnnnnV } % \begin{macrocode} \cs_new:Npn \@@_rf_main_command:nnnnnn #1#2#3#4#5#6 { % #1 -- label % #2 -- options % #3 -- amount % #4 -- space % #5 -- unit \@@_rf_initialice:nnnn {#1} {#2} {#5} {#4} \@@_rf_use_hook_accordingly: \@@_rf_parse_the_amount:n {#3} \@@_rf_general_checkups: \@@_if_state_equal:nTF { normal } { \@@_rf_process_amount_normally:n {#2} \@@_rf_post_process_amount: }{ \@@_rf_process_amount_special: } \@@_rf_prepare_amount_printing:N \l_@@_processed_seq \@@_rf_print_amount_correctly: \@@_rf_print_unit_correctly: } \cs_generate_variant:Nn \@@_rf_main_command:nnnnnn { nnnnV } % \end{macrocode} % \end{macro} % % % \subsubsection{Initialization} % % \begin{macro}{\@@_rf_initialice:nnnn } % A hub command for several steps. See the inividual commands for more % details. As each command is slightly different in execution, do some setup % depending on which command is currently in use. % \begin{macrocode} \cs_new:Npn \@@_rf_initialice:nnnn #1#2#3#4 { \@@_rf_initialice_default:nnn {#1} {#2} {#3} \@@_rf_initialize_units: \@@_rf_setup_bools_for_process_and_prints: \tl_case:NnF \l_@@_cmd_type_tl { \c_@@_cmd_type_cunum_tl { \tl_set:Nn \l_@@_phantom_tl {#4} } \c_@@_cmd_type_cCutext_tl { \@@_initialice_addon_cutext: } \c_@@_cmd_type_cuam_tl { } } { \@@_create_internal_error:nN { init-acc } \l_@@_cmd_type_tl } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_rf_initialice_default:nnn} % Set some variables to their default value, set the options (unit changes, etc.), check if % a label % is used and make sure the units are defined. % % We set the state to |42| in case of using the \opt{42} option to later proceeed to the % special post-processing case. % \begin{macrocode} \cs_new:Npn \@@_rf_initialice_default:nnn #1#2#3 { \tl_clear:N \l_@@_phantom_tl \tl_clear:N \l_@@_option_unit_tl \tl_clear:N \l_@@_precompiled_options_tl \bool_set_false:N \l_@@_lokal_recalc_for_people_bool \bool_set_false:N \l_@@_lokal_eval_fractions_bool \tl_if_empty:nF {#2} { \keys_precompile:nnN { cooking-units } {#2} \l_@@_precompiled_options_tl \l_@@_precompiled_options_tl } \IfNoValueF {#1} { \@@_reference_label_and_persons:n {#1} } \@@_expand_unit_input:Nn \l_@@_original_unit_tl {#3} \@@_error_if_unit_not_defined:VTF \l_@@_original_unit_tl { \@@_set_state_to:n { error } \bool_set_true:N \l_@@_unit_error_bool } { \@@_set_state_to:n { normal } } \bool_if:NT \l_@@_pckopt_fourty_two_bool { \@@_set_state_to:n { 42 } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_rf_initialize_units:} % Looks more complicated than it is. |\l_@@_option_unit_tl| is the conversion unit (aka.\ % the unit that should be printed if a unit-change is requested). % If |\cuam| is used there is no unit, but % a special marker instead: Set the option unit to the original unit as it does not matter. % % A bonkers unit change overrules a \enquote{eV} unit change; so if both are true, % prefer the bonkers one. All currently active unit-conversions are stored in the property % list |\l_@@_change_unit_prop|; get the conversion unit (if exists) and store it into % |\l_@@_option_unit_tl|. % \begin{macrocode} \cs_new:Npn \@@_rf_initialize_units: { \@@_if_no_unit_marker:NTF \l_@@_original_unit_tl { \tl_set_eq:NN \l_@@_option_unit_tl \l_@@_original_unit_tl } { \bool_if:NTF \l_@@_pckopt_bonkers_bool { \@@_changing_unit_bonkers:N \l_@@_original_unit_tl } { \bool_if:NT \l_@@_pckopt_convert_to_eV_bool { \@@_convert_to_eV:N \l_@@_original_unit_tl } } \prop_get:NVNF \l_@@_change_unit_prop \l_@@_original_unit_tl \l_@@_option_unit_tl { \tl_set_eq:NN \l_@@_option_unit_tl \l_@@_original_unit_tl } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_rf_setup_bools_for_process_and_prints:} % Set some booleans for later use (after parsing). Funnily enough, we can already % decide some future interactions like a evaluation of fractions or if the amount should % be recalculated to fit the references number of persons. We can do this, as for % errors or special-signs we skip the process where those booleans would be needed. % \begin{macrocode} \cs_new:Npn \@@_rf_setup_bools_for_process_and_prints: { \bool_lazy_and:nnT { \l_@@_reference_given_bool } { \l_@@_pckopt_recalculate_amount_bool } { \int_compare:nNnF { \l_@@_wanted_number_of_persons_int } = { \l_@@_local_number_of_persons_int } { \bool_set_true:N \l_@@_lokal_recalc_for_people_bool } } \bool_lazy_or:nnTF { \l_@@_pckopt_eval_fractions_bool } { \l_@@_lokal_recalc_for_people_bool } { \bool_set_true:N \l_@@_lokal_eval_fractions_bool } { % "Convert" fractions \emph{only} if a unit change is requested. \bool_lazy_and:nnT { \l_@@_pckopt_convert_fractions_bool } { ! \tl_if_eq_p:NN \l_@@_option_unit_tl \l_@@_original_unit_tl } { \bool_set_true:N \l_@@_lokal_eval_fractions_bool } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_initialice_addon_cutext:} % For \cCutext\ we first check if the user wants to use \cCutext\ at all. % If not, change it to |\cunum|. % \begin{macrocode} \cs_new:Npn \@@_initialice_addon_cutext: { \bool_set_false:N \l_@@_cutext_use_single_bool \bool_if:NT \l_@@_pckopt_cutext_to_cunum_bool { \@@_set_cmd_type_to:n { cunum } } } % \end{macrocode} % \end{macro} % % \subsubsection{Hooks} % % \begin{macro}{ \@@_rf_use_hook_accordingly: } % Hooks are straightforward. I know the current command and thus use the % respective hook. % \begin{macrocode} \cs_new:Npn \@@_rf_use_hook_accordingly: { \l_@@_cmd_hook_tl \tl_case:NnF \l_@@_cmd_type_tl { \c_@@_cmd_type_cunum_tl { \l_@@_cunum_hook_tl } \c_@@_cmd_type_cCutext_tl { \bool_if:NTF \l_@@_cutext_uppercase_word_bool { \l_@@_Cutext_hook_tl } { \l_@@_cutext_hook_tl } } \c_@@_cmd_type_cuam_tl { \l_@@_cuam_hook_tl } } { \@@_create_internal_error:nN { use-hook-acc } \l_@@_cmd_type_tl } } % \end{macrocode} % \end{macro} % % % % \subsubsection{Parsing} % % \begin{macro}{\@@_rf_parse_the_amount:n} % We finally set the amount. Afterwards make sure some tokens have the correct % catcode (and remove all spaces). Not quite sure if I should also set the catcode for % |-| or others. % % If the amount is empty or the draft mode is active do some preperations to just print % the input, else parse the amount. % \begin{macrocode} \cs_new:Npn \@@_rf_parse_the_amount:n #1 { \@@_expand_amount_input:Nn \l_@@_original_amount_tl {#1} \tl_set_rescan:NnV \l_@@_original_amount_tl { \char_set_catcode_letter:N \_ % \char_set_catcode_ignore:N \ % \char_set_catcode_other:N ? % } \l_@@_original_amount_tl \bool_lazy_any:nTF { { \tl_if_empty_p:N \l_@@_original_amount_tl } { \l_@@_pckopt_draft_bool } } { \@@_rf_prepare_just_print_input: } { \@@_rf_split_and_parse_input:N \l_@@_original_amount_tl } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_rf_prepare_just_print_input:} % There is no \enquote{real} \enquote{print the input}, instead act as if there % was an error (which is the same as just printing the input). % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_just_print_input: { \@@_set_state_to:n { error } \tl_if_empty:NT \l_@@_original_amount_tl { \@@_create_error:nn { cooking-units } { empty-amount } } } % \end{macrocode} % \end{macro} % \begin{macro}{ \@@_rf_split_and_parse_input:N } % Here we split and parse the amount. First it is split (separating ranges and % times into their separate values). Afterwards the split parts are parsed for % special signs, if they contain the allowed input-tokens, etc. % % |\use_none_delimit_by_q_nil:w \q_nil| allows me to \enquote{jump} here in case % of an error. % % After everything is done everything is stored into |\l_@@_parsed_seq|. % \begin{macrocode} \cs_new:Npn \@@_rf_split_and_parse_input:N #1 { \tl_set_eq:NN \l_@@_tmpa_tl #1 \@@_rf_prepare_input_before_split:N \l_@@_tmpa_tl \@@_rf_split_input:V \l_@@_tmpa_tl \@@_rf_parse_input:N \l_@@_split_input_seq \use_none_delimit_by_q_nil:w \q_nil } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_prepare_input_before_split:N } % As the name suggests we make some preparations before we split. % We could do this (especially checking for a special sign) during the parsing, % but I think it is cleaner to do it now. First checks if a special sign is in the amount, % second replaces the times (|\times|, |xX|) or range-sign (|--|) with an internal % token. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_input_before_split:N #1 { \@@_rf_prepare_input_check_for_special_sign:N #1 \@@_rf_prepare_input_check_for_range_or_times_sign:N #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_prepare_input_check_for_special_sign:N, % \@@_rf_prepare_input_check_for_range_or_times_sign:N} % Loops over all user-given (or predefined) special signs and tests the input % for their appearances. If we find one (more are not really necessary), set the state % to |special_sign|. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_input_check_for_special_sign:N #1 { \tl_map_inline:Nn \l_@@_input_allowed_special_signs_tl { \tl_if_in:NnT #1 {##1} { \@@_if_state_equal:nF { error } { \@@_set_state_to:n { special_sign } } \tl_map_break: } } } % \end{macrocode} % Here we replace all valid \enquote{times} signs and \enquote{range} signs % with |\q_@@_range|. Both use the same marker as a combination between those % two is not allowed (cannot use ranges and times). % % Doing the times-loop first (for no real particular reason), all occurrences of a valid % times-sign are replaced. No breaking the loop here as a mix of different signs may % be used. % % Afterwards all occurences of the one valid range sign are replaced by |\q_@@_range|. % Furthermore, create an error if times signs and ranges are used both at once. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_input_check_for_range_or_times_sign:N #1 { \tl_map_inline:Nn \l_@@_input_times_sign_tl { \tl_if_in:NnT #1 {##1} { \tl_replace_all:Nnn #1 {##1} { \q_@@_range } \bool_set_true:N \l_@@_rf_contains_times_bool } } \tl_if_in:NVT #1 \l_@@_input_range_sign_tl { \tl_replace_all:NVn #1 \l_@@_input_range_sign_tl { \q_@@_range } \bool_set_true:N \l_@@_rf_contains_range_bool \bool_if:NT \l_@@_rf_contains_times_bool { \@@_create_error:n { range-and-times-not-allowed } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_split_input:n, \@@_rf_split_input:V} % Here the splitting the amount into a sequence of values is done. % Sequences are quite handy here as we can just loop over all split items % and we have an arbitrary number of parts (1, 2 or even 20). % % Not quite sure if keeping the spaces is necessary, but I guess this is a minor % optimisation (as |keep_spaces| does not actively remove spaces). % % After the \enquote{main} splitting we still need to make some furhter splitting % for frations. This does not result into additional seq-items. % % After everything is done, the split parts are stored into the sequence % |\l_@@_split_input_seq|. % \begin{macrocode} \cs_new:Npn \@@_rf_split_input:n #1 { \seq_clear:N \l_@@_split_input_seq \seq_set_split_keep_spaces:Nnn \l_@@_tmpa_seq { \q_@@_range } {#1} \int_set:Nn \l_@@_nr_of_items { \seq_count:N \l_@@_tmpa_seq } \bool_if:NT \l_@@_rf_contains_range_bool { \int_compare:nNnT { \l_@@_nr_of_items } > { 2 } { \@@_create_error:nf { range-too-many-items } { \int_use:N \l_@@_nr_of_items } } } \seq_map_function:NN \l_@@_tmpa_seq \@@_rf_split_inividual_item:n } \cs_generate_variant:Nn \@@_rf_split_input:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_split_inividual_item:n} % For a fraction the |/| token is neccessary. Thus, if not |/| is found we have a normal % number. % % |\l_@@_split_input_seq| contains 4 brace-pairs. First contains a boolean denoting if % we have a fraction or not. 2nd, 3rd and 4th contains the inivdual parts of the fraction % (2nd contains the mixed-fraction part; 3rd the numerator and 4th the denominator). % If there is no fraction we store the number in the 4th brace-pair. % \begin{macrocode} \cs_new:Npn \@@_rf_split_inividual_item:n #1 { \tl_if_in:nnTF {#1} { / } { \@@_rf_split_fraction:ww #1 \q_stop } { %% normal number \tl_if_empty:nT {#1} { \@@_create_error:n { empty-argument } } \seq_put_right:Nn \l_@@_split_input_seq { { \c_false_bool } {} {} {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_split_fraction:ww, \@@_rf_split_mixed_fraction:wwn} % Here we split the fractions into numerator |#1| and denominator |#2|. % A mixed-fraction sign is only allowed in the numerator part, having it in |#2| throws % an error (case: |1/2_3|). If a mixed fraction sign is found in |#1| we do furhter splitting, % else we store the values in |\l_@@_split_input_seq|. % % Furthermore we set |\l_@@_rf_contains_fraction_bool| to true. This is done for all % sequence parts as it changes unit-chaning behavior. % \begin{macrocode} \cs_new:Npn \@@_rf_split_fraction:ww #1 / #2 \q_stop { \bool_set_true:N \l_@@_rf_contains_fraction_bool \tl_if_in:nnT {#2} { _ } { \@@_create_error:n { fraction-wrong-order } } \tl_if_empty:nT {#2} { \@@_create_error:n { empty-argument } } \tl_if_in:nnTF {#1} { _ } { \@@_rf_split_mixed_fraction:wwn #1 \q_stop {#2} }{ \tl_if_empty:nT {#1} { \@@_create_error:n { empty-argument } } \seq_put_right:Nn \l_@@_split_input_seq { { \c_true_bool } {} {#1} {#2} } } } % \end{macrocode} % Straighforward. |#1| is the mixed-fraction part, |#2| is the numerator and |#3| % is the denominator. Make sure they are not empty and store them. % \begin{macrocode} \cs_new:Npn \@@_rf_split_mixed_fraction:wwn #1 _ #2 \q_stop #3 { \tl_if_empty:nT {#1} { \@@_create_error:n { empty-argument } } \tl_if_empty:nT {#2} { \@@_create_error:n { empty-argument } } \seq_put_right:Nn \l_@@_split_input_seq { { \c_true_bool } {#1} {#2} {#3} } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_rf_parse_input:N} % Now that splitting is done, we start parsing! Using sequences, we jus loop ober % every item. (Which is extremly useful as we can loop over 1 or 100 items). % \begin{macrocode} \cs_new:Npn \@@_rf_parse_input:N #1 { \seq_clear:N \l_@@_parsed_seq \seq_map_inline:Nn #1 { \@@_rf_parse_input:nnnn ##1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_parse_input:nnnn} % Looks more intimidating than it is. The 4 arguments are the ones from % |\l_@@_split_input_seq| and are thus: % \begin{itemize} % \item |#1|: Contains this item a fraction? % \item |#2|: mixed-fraction part (if it exists for this item). % \item |#3|: numerator part (if it exists for this item). % \item |#4|: number part or (if it is a fraction) or denominator. % \end{itemize} % % If we have a fraction we parse the inividual parts and store them temporarily in % tl's. Afterwards we remove all minus signs, but remember if the whole fraction is % positive or negative. Store the parsed parts into |\l_@@_parsed_seq| (this time with % 5 items!). % % If we just have a single number, parse it and store it in |\l_@@_parsed_seq|. % % |\l_@@_parsed_seq| contains 5 items. First is, again, if the item contains a fraction. % 2nd is new, is only used in combination with fractions (else it is empty) and stores % if the fraction is positive or negative (aka.\ if there is a minus sign in front of it % during printing). % \begin{macrocode} \cs_new:Npn \@@_rf_parse_input:nnnn #1 #2#3#4 { \bool_set_eq:NN \l_@@_rf_check_fraction_bool #1 \bool_if:NTF #1 { \@@_parse_input_and_safe_in:nN {#2} \l_@@_mixed_fraction_tl \@@_parse_input_and_safe_in:nN {#3} \l_@@_number_tmpa_tl \@@_parse_input_and_safe_in:nN {#4} \l_@@_number_tmpb_tl \@@_rf_frac_remove_minus:NNN \l_@@_mixed_fraction_tl \l_@@_number_tmpa_tl \l_@@_number_tmpb_tl \@@_rf_seq_put_right:NnVVVV \l_@@_parsed_seq {#1} \l_@@_minus_bool \l_@@_mixed_fraction_tl \l_@@_number_tmpa_tl \l_@@_number_tmpb_tl }{ \@@_parse_input_and_safe_in:nN {#4} \l_@@_number_tmpb_tl \@@_rf_seq_put_right:NnnnnV \l_@@_parsed_seq {#1} { } { } { } \l_@@_number_tmpb_tl } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_rf_frac_remove_minus:NNN} % Removes the minus signs from the inividual fraction parts, but remembers % if we have removed an odd or even number of minus signs. This will determine % if the whole fraction is negative or not. % \begin{macrocode} \cs_new:Npn \@@_rf_frac_remove_minus:NNN #1#2#3 { \bool_set_false:N \l_@@_minus_bool \tl_if_empty:NF #1 { \exp_last_unbraced:NNV \@@_rf_frac_remove_minus_aux:Nww #1 #1 \q_stop } \exp_last_unbraced:NNV \@@_rf_frac_remove_minus_aux:Nww #2 #2 \q_stop \exp_last_unbraced:NNV \@@_rf_frac_remove_minus_aux:Nww #3 #3 \q_stop } % \end{macrocode} % Not sure if |\token_if_eq_charcode| is the best choice, but I think I stole it from % \pkg{siunitx}, so it should be fine. % \begin{macrocode} \cs_new:Npn \@@_rf_frac_remove_minus_aux:Nww #1 #2#3 \q_stop { \token_if_eq_charcode:NNTF #2 - { \@@_bool_reverse:N \l_@@_minus_bool \tl_set:Nn #1 {#3} } { \tl_set:Nn #1 {#2#3} } } % \end{macrocode} % \end{macro} % % % \subsubsection{Processing} % % % % \begin{macro}{\@@_rf_process_amount_special:} % In case the current state is not-normal we end up here. No (real) processing is % done here, just a warning message is printed. Here we also change the input % if \opt{42} is used. % \begin{macrocode} \cs_new:Npn \@@_rf_process_amount_special: { \@@_if_state_equal:nT { special_sign } { \msg_warning:nnV { cooking-units } { amount-not-known } \l_@@_original_amount_tl } \bool_if:NTF \l_@@_pckopt_fourty_two_bool { \@@_rf_prepare_fourty_two_amount: } { \seq_set_eq:NN \l_@@_processed_seq \l_@@_parsed_seq } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_process_amount_normally:n} % Now that we know e.g.\ if we have an unit change (no special sign, fractions?, etc.) % we do some more initializing. Afterwards, process the input (e.g.\ evaluate fractions % if needed, scale the amount depending on the unit, etc.). % \begin{macrocode} \cs_new:Npn \@@_rf_process_amount_normally:n #1 { \@@_rf_initialise_after_parsing:n {#1} \seq_clear:N \l_@@_processed_seq \seq_map_inline:Nn \l_@@_parsed_seq { \@@_rf_process_amount_normally:nnnnn ##1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_process_amount_normally:nnnnn} % Looks more intimidating (again) as it is. Here are the arguments: % % \begin{itemize} % \item |#1|: Contains this item a fraction? (Boolean) % \item |#2|: (Only non-empty for fractions) Is the fraction negative? % \item |#2|: mixed-fraction part (if it exists for this item). % \item |#3|: numerator part (if it exists for this item). % \item |#4|: number part or denominator (if it is a fraction) . % \end{itemize} % % Two cases here: Do we have a fraction or not. % % If we have a fraction and no evaluation is done, do nothing essentially. % Else an evaluation of the fraction is required and the whole thing is calculated. % Do some Division-by-zero checks; make sure the resulting number is negative % (|#2| argument) and do some further calculations with the resulting number. % Afterwards, store the number in |\l_@@_processed_seq|. (Note that we define the % fraction-boolean as |\c_false_bool|). % % If the current item is simply a number, do some further calculations and store the result % in |\l_@@_processed_seq|. % % \begin{macrocode} \cs_new:Npn \@@_rf_process_amount_normally:nnnnn #1#2#3#4#5 { \bool_if:NTF #1 { \bool_if:NTF \l_@@_lokal_eval_fractions_bool { \bool_set_false:N \l_@@_rf_contains_fraction_bool \fp_compare:nNnT {#5} = { 0 } { \@@_create_error:n { Division-by-zero } } \@@_tl_set_eval:Nn \l_@@_number_tmpb_tl { #3 + #4/#5 } \int_compare:nNnT {#2} = { \c_one_int } { \tl_put_left:Nn \l_@@_number_tmpb_tl { - } } \@@_calculate_and_store_in:N \l_@@_number_tmpb_tl \@@_rf_seq_put_right:NnnnnV \l_@@_processed_seq { \c_false_bool } { } {} {} \l_@@_number_tmpb_tl }{ \@@_rf_seq_put_right:Nnnnnn \l_@@_processed_seq {#1} {#2} {#3} {#4} {#5} } }{ \tl_if_empty:nF {#2} { \@@_create_internal_error:nN { non-empty-sign-for-non-fraction } \l_@@_original_amount_tl } \tl_set:Nn \l_@@_number_tmpb_tl {#5} \@@_calculate_and_store_in:N \l_@@_number_tmpb_tl \@@_rf_seq_put_right:NnnnnV \l_@@_processed_seq {#1} { } { } { } \l_@@_number_tmpb_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_post_process_amount:} % Doing some post-processing. This is dependend on which command was used % (\cCutext\ has some other functionalities than |\cuam|). % \begin{macrocode} \cs_new:Npn \@@_rf_post_process_amount: { \tl_case:NnF \l_@@_cmd_type_tl { \c_@@_cmd_type_cunum_tl { \@@_rf_cunum_post_process:N \l_@@_processed_seq } \c_@@_cmd_type_cCutext_tl { \@@_rf_cutext_post_process:N \l_@@_processed_seq } \c_@@_cmd_type_cuam_tl { \@@_cuam_post_process:N \l_@@_processed_seq } }{ \@@_create_internal_error:nN { post-process } \l_@@_cmd_type_tl } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_rf_cunum_post_process:N} % Nothing is done here. % \begin{macrocode} \cs_new:Npn \@@_rf_cunum_post_process:N #1 { } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_cutext_post_process:N} % Ok, first decide if we need the plural or singular version of the unit. We can do it % now as we will not \enquote{reduce} the amounts (as |\cuam| is able to do). % % If numerals should be used and we do \emph{not} have a fraction (in all items) % we first check if all items only contain integers. If they all are integers, check if numerals % can be used. % \begin{macrocode} \cs_new:Npn \@@_rf_cutext_post_process:N #1 { \@@_rf_check_plural:N #1 \bool_lazy_and:nnT { \l_@@_lokal_numeral_bool } { ! \l_@@_rf_contains_fraction_bool } { \@@_rf_check_if_decimal:NNTF #1 \l_@@_numbers_to_check_seq { \bool_set_false:N \l_@@_lokal_numeral_bool } { \@@_rf_check_if_numerals_can_be_used:N \l_@@_numbers_to_check_seq } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_check_plural:N } % For plural-checking we alawys use the last item (and hope that in no language we would % need to e.g.\ use the first item). |\l_@@_tmpa_tl| contains 5 braced pairs; so one % expansion allows |\@@_rf_check_plural_aux:nnnnn| to get them. % \begin{macrocode} \cs_new:Npn \@@_rf_check_plural:N #1 { \seq_get_right:NN #1 \l_@@_tmpa_tl \exp_after:wN \@@_rf_check_plural_aux:nnnnn \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_check_plural_aux:nnnnn} % So \dots\ the algorithm used to determine plural or singular is depending on the % current language (french has other rules than english or german) and if we have a % fraction or not. Call the respective command and hope that the result is correct. % \begin{macrocode} \cs_new:Npn \@@_rf_check_plural_aux:nnnnn #1#2#3#4#5 { \bool_if:NTF #1 { \@@_culang_let:nnN { pluraltest } { fraction } \l_@@_translation_tmpa_tl \use:c { @@_pluraltest_fraction_ \l_@@_translation_tmpa_tl :nnn } {#3} {#4} {#5} }{ \@@_culang_let:nnN { pluraltest } { normal } \l_@@_translation_tmpa_tl \use:c { @@_pluraltest_normal_ \l_@@_translation_tmpa_tl :n } {#5} } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_rf_check_if_decimal:NN, \@@_rf_check_if_decimal_aux:Nnnnnn} % Test if all items are integers or not. As the numbers are stored in tl's and not in % fp's or int's we can just check for a |.|. If one is found, we have a decimal. To do this % efficiently we loop through all items, get the needed number (we have no fractions, % we checked that earlier), store it in |#2| for later use and |\tl_put_right:Nn| it into % |\l_@@_tmpa_tl|. |\l_@@_tmpa_tl| will then contain all numbers that need to be % checked as a single string. |#2| is a sequence we can use in the future (is is more % practical as it only contains a single number and not 6 braced pairs; making looping % easier it can be placed directly where we need it). % \begin{macrocode} \prg_new_conditional:Npnn \@@_rf_check_if_decimal:NN #1#2 { TF } { \tl_clear:N \l_@@_tmpa_tl \seq_clear:N #2 \seq_map_inline:Nn #1 { \@@_rf_check_if_decimal_aux:Nnnnnn #2 ##1 } \tl_if_in:NnTF \l_@@_tmpa_tl { . } { \prg_return_true: } { \prg_return_false: } } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_rf_check_if_decimal_aux:Nnnnnn #1#2#3#4#5#6 { \tl_put_right:Nn \l_@@_tmpa_tl {#6} \seq_put_right:Nn #1 {#6} } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_rf_check_if_numerals_can_be_used:N} % The items of sequence in |#1| only contains a number and not the 6 braced pairs. % This makes this loop a lot more straightforward to write. % \begin{macrocode} \cs_new:Npn \@@_rf_check_if_numerals_can_be_used:N #1 { \seq_map_inline:Nn #1 { \bool_lazy_and:nnF { \int_compare_p:nNn {##1} < { \l_@@_print_numerals_below_int } } { \int_compare_p:nNn {##1} > { 0 } } { \bool_set_false:N \l_@@_lokal_numeral_bool \seq_map_break: } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_cuam_post_process:N} % Ok, shit gets real at this point. Compared to the other post process stuffs (especially % |\cunum|) here we do more complicated things. This is mostly due to |\cuam| allowing % phrases (\enquote{12} to \enquote{1 Dutzend}). This needs its own processing algorithm. % Furthermore, we still allow numerals (and we need to be careful because both can be used % at the same time). Of course, all this is only done if the input conists of only integers (and % no fractions) % \begin{macrocode} \cs_new:Npn \@@_cuam_post_process:N #1 { \bool_if:NF \l_@@_rf_contains_fraction_bool { \@@_rf_check_if_decimal:NNTF #1 \l_@@_numbers_to_check_seq { \bool_set_false:N \l_@@_lokal_numeral_bool } { \bool_if:NT \l_@@_pckopt_use_phrases_bool { \@@_rf_cuam_post_process_input_phrases:NN #1 \l_@@_numbers_to_check_seq } \bool_if:NT \l_@@_lokal_numeral_bool { \@@_rf_check_if_numerals_can_be_used:N \l_@@_numbers_to_check_seq } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_cuam_post_process_input_phrases:NN} % This algorithm is thankfully much simpler compared to the old one! % First, we get the property list for the current language that contains % the phrases. If there are none (aka.\ if it is equal to |\c_@@_no_translation_str|) % we just skip to the end. Else we get the number-sequence from the property % list. |#2| is the list of numbers from the input. |\l_@@_number_tmpa_tl| will contain the % largest common divisor $l$ between those numbers (e.g. $2$, $4$ and $6$ have the % largest common divisor of $l=2$; $12$ and $16$ have $l=4$). If it is $l=1$ we do % nothing (as there is none). Else, take it as a upper cap (there cannot be a common % divisor above $l$; thus only check phrase numbers $n \leq l$). % \begin{macrocode} \cs_new:Npn \@@_rf_cuam_post_process_input_phrases:NN #1#2 { \@@_culang_get_phrase_prop:N \l_@@_phrase_prop \tl_if_eq:NNT \c_@@_no_translation_str \l_@@_phrase_prop { \use_none_delimit_by_q_stop:w } \prop_get:NnN \l_@@_phrase_prop { seq } \l_@@_phrase_numbers_seq \@@_get_largest_common_divisor:NN #2 \l_@@_largest_common_divisor_int \int_compare:nNnF { \l_@@_largest_common_divisor_int } = { 1 } { \@@_find_common_phrase:NNV #1#2 \l_@@_largest_common_divisor_int } \use_none_delimit_by_q_stop:w \q_stop } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_get_largest_common_divisor:NN, \@@_get_largest_common_divisor_aux:Nn} % |#1| is the sequence of numbers we want to check (we need in later; thus no % \enquote{pop}); in |#2| we will store the largest common % divisor $l$. The \enquote{find the largest common divisor} algorithm is only needed % if we have more than $1$ item (so if we have a range or a times). % % $l$ must be the same for all numbers. % % \begin{macrocode} \cs_new:Npn \@@_get_largest_common_divisor:NN #1#2 { \seq_get_left:NN #1 \l_@@_tmpa_tl \int_set:Nn #2 { \l_@@_tmpa_tl } \bool_lazy_or:nnT \l_@@_rf_contains_range_bool \l_@@_rf_contains_times_bool { \seq_map_inline:Nn #1 { \@@_get_largest_common_divisor_aux:Nn #2 {##1} \int_compare:nNnT {#2} = { 1 } { \seq_map_break: } } } } % \end{macrocode} % % The source for the algorithm is \url{https://en.wikipedia.org/wiki/Euclidean_algorithm#Implementations}. % To make my future life easier, here is the copy-and-pasted code: % \begin{verbatim} % function gcd(a, b) % while b \neq 0 % t := b % b := a mod b % a := t % return a % \end{verbatim} % and here is the algorithm with the current variables: % \begin{verbatim} % function gcd(#1, b) % while b \neq 0 % a := b % b := #1 mod b % #1 := a % return #2 % \end{verbatim} % % \begin{macrocode} \cs_new:Npn \@@_get_largest_common_divisor_aux:Nn #1#2 { \int_set:Nn \l_@@_tmpb_int {#2} \int_until_do:nNnn { \l_@@_tmpb_int } = { 0 } { \int_set_eq:NN \l_@@_tmpa_int \l_@@_tmpb_int \int_set:Nn \l_@@_tmpb_int { \int_mod:nn {#1} { \l_@@_tmpb_int } } \int_set_eq:NN #1 \l_@@_tmpa_int } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_find_common_phrase:NNn} % Ok, this one is complicated. % |#1| is the original prcoess sequence; |#2| is the sequence of numbers-to-check % (which were extracted from |#1| in a previous step to make loops easier); % |#3| is the largest common divisor $l$ which requires $l>1|$. % % As a reminder, |\l_@@_phrase_numbers_seq| contains two braced-pairs: % first is the phrase-number, second is a boolean which defines if multiples of the % phrase-number should be checked too. The phrase-number is thus gained by % |\use_i:nn ##1| which must be smaller or equal to $l$ (I explain why in a previous % function). So do nothing if it is larger. |\l_@@_phrase_numbers_seq| is also sorted % from largest to smallest, so no |\seq_map_break:| unless we found a number. % % If for the current phrase number all values have an integer quotient (|\l_@@_tmpa_bool| % remains |true| after |\@@_find_common_phrase_aux:Nnn|) (integer quotients are stored in % |\l_@@_reduced_numbers_seq|) we gain the other properties from this phrase-number % like the phrase-text (\enquote{Dutzend}) and its gender; break the loop. % % If the loop was terminated via a break |\l_@@_tmpa_bool| remains |true| and % we continue with updating the values in sequences |#1| and |#2|. % \begin{macrocode} \cs_new:Npn \@@_find_common_phrase:NNn #1 #2#3 { \bool_set_false:N \l_@@_lokal_phrase_bool \seq_map_inline:Nn \l_@@_phrase_numbers_seq { \int_compare:nNnF { \use_i:nn ##1 } > {#3} { \bool_set_true:N \l_@@_tmpa_bool \seq_clear:N \l_@@_reduced_numbers_seq \@@_find_common_phrase_aux:Nnn #2 ##1 \bool_if:NT \l_@@_tmpa_bool { \int_compare:nNnTF { \l_@@_tmpa_int } = { 1 } { \prop_get:NnN \l_@@_phrase_prop { ##1 } \l_@@_phrase_text_tl } { \prop_get:NnN \l_@@_phrase_prop { ##1 - pl } \l_@@_phrase_text_tl } \prop_get:NnN \l_@@_phrase_prop { ##1 - gender } \l_@@_phrase_gender_tl \seq_map_break: } } } \bool_if:NT \l_@@_tmpa_bool { \bool_set_true:N \l_@@_lokal_phrase_bool \seq_set_eq:NN #2 \l_@@_reduced_numbers_seq \@@_update_values:NN #1 \l_@@_reduced_numbers_seq } } \cs_generate_variant:Nn \@@_find_common_phrase:NNn { NNV } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_find_common_phrase_aux:Nnn} % Thankfully a lot more simplified compared to a previous version. % The idea is simple: loop over all numbers in |#1| (the \enquote{numbers-to-check} % sequence) and figure out if all its items are a multiple of the phrase-number |#2|. % If multiples should not be checked (boolean |#3|) we set the multiple to be % $1$. Else we use |\int_div_truncate:nn| to find the closest (checks Wikipedia for % terminology) integer quotient. Afterwards we see if the quotient (calculated ot $1$) % times the phrase number |#2| is equal to the original value (We check if the modulo is % $0$). We do not use the modulo directly as we need the quotient for further uses % (if an integer quotient without reminder exists). % % If integer quotient times phrase number equals the original one, we store the % quotient in |\l_@@_reduced_numbers_seq| for later usage. If we have one whiff % we break the loop as there are no integer quotients for the current phrase-number. % % \begin{macrocode} \cs_new:Npn \@@_find_common_phrase_aux:Nnn #1 #2#3 { \seq_map_inline:Nn #1 { \bool_if:NTF #3 { \int_set:Nn \l_@@_tmpa_int { 1 } } { \int_set:Nn \l_@@_tmpa_int { \int_div_truncate:nn {##1} {#2} } } \int_compare:nNnTF { \l_@@_tmpa_int * #2 } = {##1} { \seq_put_right:NV \l_@@_reduced_numbers_seq \l_@@_tmpa_int } { \bool_set_false:N \l_@@_tmpa_bool \seq_map_break: } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_update_values:NN, \@@_update_values_aux:Nnnnnn} % Here we update the numbers in |\l_@@_processed_seq| (given in |#1|) with % the integer quotient numbers in |#2|). Do a copy of |#1| (to loop over it) than clear % the process-sequence |#1|. Looping is not necessary as we technically know % every information, but this makes it a bit easier in case I change things in the future % (I can also add some tests). \cs_new:Npn \@@_update_values:NN #1#2 { \seq_set_eq:NN \l_@@_tmpa_seq #1 \seq_clear:N #1 \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_update_values_aux:NNnnnnn #1 #2 ##1 } } \cs_new:Npn \@@_update_values_aux:NNnnnnn #1 #2#3#4#5#6#7 { \bool_if:NT #3 { \@@_create_internal_error:nn { fraction-wrong-value } {#3} } \tl_if_empty:nF {#4#5#6} { \@@_create_internal_error:nn { update-values-non-empty } {#4;;#5;;#6} } \seq_pop_left:NN #2 \l_@@_tmpa_tl \@@_rf_seq_put_right:NnnnnV #1 {#3} {#4} {#5} {#6} \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % % \subsubsection{Printing} % % \begin{macro}{\@@_rf_prepare_amount_printing:N} % Printing the amount depends on the commands and the results-to-be-printed % are stored into |\l_@@_prepared_printing_seq|. If fractions and ranges are combined, % a small space is inserted between the fraction and the range sign, but \emph{only} % if it is \emph{not} the last item in the sequence. |\l_@@_nr_of_items| stores the number % of items in the sequence. If the last item in the loop is encountered (by checking if % the index |##1| is equal to |\l_@@_tmpa_int|) give the auxiliary function |\c_true_bool| % and it will figure out the rest. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_amount_printing:N #1 { \tl_case:NnF \l_@@_cmd_type_tl { \c_@@_cmd_type_cunum_tl { \cs_set_eq:NN \@@_print_number:n \@@_print_number_default:n } \c_@@_cmd_type_cCutext_tl { \cs_set_eq:NN \@@_print_number:n \@@_cutext_print_number:n } \c_@@_cmd_type_cuam_tl { \cs_set_eq:NN \@@_print_number:n \@@_cuam_print_number:n } } { \@@_create_internal_error:nN { post-process } \l_@@_cmd_type_tl } \bool_set_false:N \l_@@_seperator_after_fraction_bool \seq_clear:N \l_@@_prepared_printing_seq \seq_map_indexed_inline:Nn #1 { \int_compare:nNnTF { \l_@@_nr_of_items } = {##1} { \@@_rf_prepare_amount_printing_aux:nnnnnn { \c_true_bool } } { \@@_rf_prepare_amount_printing_aux:nnnnnn { \c_false_bool } } ##2 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_prepare_amount_printing_aux:nnnnnn} % |#1| is a boolean which signifies if the last item of the sequence is encountered. % |#2| to |#6| are our typical sequence-arguments. All this function acutally does % is to store the to-be-printed stuff into |\l_@@_prepared_printing_seq|. Could be % optimized a bit more. The booleans are set here so that later on |\seq_use:Nn| can % be used. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_amount_printing_aux:nnnnnn #1#2#3#4#5#6 { \bool_if:NTF #2 { \seq_put_right:Nn \l_@@_prepared_printing_seq { \@@_print_fraction:nnnn {#3} {#4} {#5} {#6} \bool_set_false:N \l_@@_cutext_uppercase_word_bool \bool_if:NTF #1 { \bool_set_false:N \l_@@_seperator_after_fraction_bool } { \bool_set_true:N \l_@@_seperator_after_fraction_bool } } }{ \seq_put_right:Nn \l_@@_prepared_printing_seq { \@@_print_number:n {#6} \bool_set_false:N \l_@@_cutext_uppercase_word_bool } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_print_fraction:nnnn} % Printing a fraction is quite easy and straightforward. Use |\@@_print_number_default:n| % for correct decimal-printing. % \begin{macrocode} \cs_new:Npn \@@_print_fraction:nnnn #1#2#3#4 { \int_compare:nNnT {#1} = { 1 } { \c_@@_minus_tl } \tl_if_empty:nF {#2} { \@@_print_number_default:n {#2} \skip_horizontal:N \l_@@_mixed_frac_dim } \@@_frac:nn { \@@_print_number_default:n {#3} } { \@@_print_number_default:n {#4} } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_print_number:n} % This command will be redefined. % \begin{macrocode} \cs_new:Npn \@@_print_number:n #1 {} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_print_number_default:n} % Do two things: Replace the dot with the correct decimal-sign of the current language % and replace the |-| token with the correct minus sign. % \begin{macrocode} \cs_new:Npn \@@_print_number_default:n #1 { \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_if_in:NnT \l_@@_tmpa_tl { . } { \@@_culang_let:nnN { name } { decimal-mark } \l_@@_translation_tmpa_tl \tl_replace_once:Nnn \l_@@_tmpa_tl { . } { \l_@@_translation_tmpa_tl } } \tl_replace_once:Nnn \l_@@_tmpa_tl { - } \c_@@_minus_tl \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cutext_print_number:n, \@@_cutext_print_input_aux:nn, \@@_cutext_print_input_aux:nV} % For \cCutext\ we pretty much always print the numbers using |\@@_print_number_default:n| (like |\cunum| does). However, \cCutext\ have the % ability to print numerals (if the input can be transformed into one). % % For numerals we captialize in the case of |\Cutext| and print it normally (assuming % lowercase) for |\cutext|. If the number is equal to $1$ we use the special % |one(m)|, |one(f)| or |one(n)| translation (which also gets captialized if needed). % \begin{macrocode} \cs_new_protected:Npn \@@_cutext_print_number:n #1 { \bool_if:NTF \l_@@_lokal_numeral_bool { \@@_cutext_print_input_aux:nV {#1} \l_@@_decided_unit_tl } { \@@_print_number_default:n {#1} } } \cs_new_protected:Npn \@@_cutext_print_input_aux:nn #1#2 { \int_compare:nNnTF {#1} = { 1 } { \@@_culang_one_let:NnT \l_@@_translation_tmpa_tl {#2} { \bool_if:NT \l_@@_cutext_uppercase_word_bool { \text_uppercase:x { \tl_head:V \l_@@_translation_tmpa_tl } \tl_tail:V } } \l_@@_translation_tmpa_tl }{ \bool_if:NTF \l_@@_cutext_uppercase_word_bool { \@@_print_Numeral:n {#1} } { \@@_print_numeral:n {#1} } } } \cs_generate_variant:Nn \@@_cutext_print_input_aux:nn { nV } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cuam_print_number:n, \@@_cuam_print_numerical_input_aux:nn, \@@_cuam_print_numerical_input_aux:nV } % If both a phrase and a numeral is requested we do some more complicated stuff, % else just print a number. % % If the numeral is \emph{not} equal to $1$ use the |\@@_print_numeral:n| function. % Else get the translation for |one(m)|, |one(f)| or |one(n)| (depends). % \begin{macrocode} \cs_new_protected:Npn \@@_cuam_print_number:n #1 { \bool_lazy_and:nnTF { \l_@@_lokal_phrase_bool } { \l_@@_lokal_numeral_bool } { \@@_cuam_print_numerical_input_aux:nV {#1} \l_@@_phrase_number_tl } { \@@_print_number_default:n {#1} } } \cs_new:Npn \@@_cuam_print_numerical_input_aux:nn #1#2 { \int_compare:nNnTF {#1} = { 1 } { \@@_culang_one_let_by_gender:NVTF \l_@@_translation_tmpa_tl \l_@@_phrase_gender_tl { \l_@@_translation_tmpa_tl } { \@@_print_numeral:n {#1} \bool_set_false:N \l_@@_lokal_numeral_bool } } { \@@_print_numeral:n {#1} } } \cs_generate_variant:Nn \@@_cuam_print_numerical_input_aux:nn { nV } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_rf_print_amount_correctly:} % In case of an error just print the original input. % % Else we check how many items the printing sequence has. If it is $1$ % print the sequence with an empty argument. % % Else: Do we print ranges? Do we print times? If neither, there is something wrong. % \begin{macrocode} \cs_new:Npn \@@_rf_print_amount_correctly: { \@@_if_state_equal:nTF { error } { \l_@@_original_amount_tl }{ \int_compare:nNnTF { \l_@@_nr_of_items } = { 1 } { \seq_use:Nn \l_@@_prepared_printing_seq { } } { \bool_if:NTF \l_@@_rf_contains_range_bool { \seq_use:Nn \l_@@_prepared_printing_seq { \@@_rf_print_range_sign: } } { \bool_if:NTF \l_@@_rf_contains_times_bool { \seq_use:Nn \l_@@_prepared_printing_seq { \@@_print_times_sign: } } { \@@_create_internal_error:nx { neither-range-nor-times } { \fp_eval:n { \l_@@_rf_contains_range_bool + 2*\l_@@_rf_contains_times_bool } } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_print_unit_correctly:} % Quite straightforward. % \begin{macrocode} \cs_new:Npn \@@_rf_print_unit_correctly: { \@@_if_state_equal:nTF { error } { \tl_if_eq:NNF \l_@@_cmd_type_tl \c_@@_cmd_type_cuam_tl { \@@_rf_print_unit_in_case_of_error: } } { \@@_rf_print_unit_or_phrase: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_rf_print_unit_or_phrase:} % Printing the unit depends on the unit; nothing more or less. % \begin{macrocode} \cs_new:Npn \@@_rf_print_unit_or_phrase: { \tl_case:NnF \l_@@_cmd_type_tl { \c_@@_cmd_type_cunum_tl { \@@_cunum_print_correct_unit: } \c_@@_cmd_type_cCutext_tl { \@@_cutext_print_correct_unitname: } \c_@@_cmd_type_cuam_tl { \@@_cuam_print_correct_unitphrase: } } { \@@_create_internal_error:nN { print-unit-acc } \l_@@_cmd_type_tl } } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{\@@_rf_initialise_after_parsing:n} % As the name suggests, this one is used \emph{after} parsing the amount. % This is done in order to know if there is an error, if the unit needs % to be changed and if this command needs to be called at all. % % It is kinda difficult to decide which unit is the correct one, as it may change % due to unit-options. In theory this could lead to an infinite loop, % so I decide in this place how things are printed and which unit % is used. % % Well, in the end I only decide if a fraction is printed or not. The case of error or % special signs were already sorted out. % % To make sure the lokal options override the unit-options, we set % the options again after unit-options. % \begin{macrocode} \cs_new_protected:Npn \@@_rf_initialise_after_parsing:n #1 { \clist_if_empty:cF { l_@@_unit_option_ \l_@@_decided_unit_tl _clist } { \keys_set_groups:nnv { cooking-units } { unit-hook } { l_@@_unit_option_ \l_@@_decided_unit_tl _clist } } \tl_if_empty:nF {#1} { \l_@@_precompiled_options_tl } \@@_if_cmd_type_equal:nTF { cunum } { \bool_set_false:N \l_@@_lokal_numeral_bool } { \bool_set_eq:NN \l_@@_lokal_numeral_bool \l_@@_pckopt_print_numeral_bool } } \cs_new:Npn \@@_decide_unit: { \@@_change_unit:TF { \tl_set_eq:NN \l_@@_decided_unit_tl \l_@@_option_unit_tl } { \tl_set_eq:NN \l_@@_decided_unit_tl \l_@@_original_unit_tl } } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{ \@@_rf_general_checkups: } % Doing some general stuff and tests which are done before further processing % the amount. % \begin{macrocode} \cs_new:Npn \@@_rf_general_checkups: { \@@_decide_unit: \@@_if_cmd_type_equal:nT { cCutext } { \@@_rf_cutext_check_if_translation_available: } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{ \@@_rf_prepare_fourty_two_amount: } % Simply replaces all variables with |42|. We can change all (except for the mixed fraction tl) % as we know if we are going to print a \enquote{number}, \enquote{range} or % \enquote{fraction}. % \begin{macrocode} \cs_new:Npn \@@_rf_prepare_fourty_two_amount: { \seq_map_inline:Nn \l_@@_parsed_seq { \@@_rf_prepare_fourty_two_amount_aux:nnnnn ##1 } } \tl_const:Nn \c_@@_fourty_two_tl { 42 } \tl_const:Nn \c_@@_fourty_two_unknown_tl { 42(!) } \cs_new:Npn \@@_rf_prepare_fourty_two_amount_aux:nnnnn #1#2#3#4#5 { \tl_clear:N \l_@@_mixed_fraction_tl \tl_clear:N \l_@@_number_tmpa_tl \@@_if_state_equal:nTF { special_sign } { \tl_set_eq:NN \l_@@_number_tmpb_tl \c_@@_fourty_two_unknown_tl } { \tl_set_eq:NN \l_@@_number_tmpb_tl \c_@@_fourty_two_tl } \bool_if:NT #1 { \tl_if_empty:nF {#3} { \tl_set_eq:NN \l_@@_mixed_fraction_tl \l_@@_number_tmpb_tl } \tl_if_empty:nF {#4} { \tl_set_eq:NN \l_@@_number_tmpa_tl \l_@@_number_tmpb_tl } } \@@_rf_seq_put_right:NnnVVV \l_@@_processed_seq {#1} {#2} \l_@@_mixed_fraction_tl \l_@@_number_tmpa_tl \l_@@_number_tmpb_tl } % \end{macrocode} % \end{macro} % % % % \subsubsection{Process the input} % % % \begin{macro}{ \@@_rf_print_unit_in_case_of_error: } % Prints the unit in case of error. Make sure the unit is printed correctly % with the correct spacing. If the unit is unknown, just print the input. % \begin{macrocode} \cs_new:Npn \@@_rf_print_unit_in_case_of_error: { \tl_if_empty:NF \l_@@_phantom_tl { \phantom { \l_@@_phantom_tl } } \tl_use:N \l_@@_value_unit_space_tl \bool_if:NTF \l_@@_unit_error_bool { \l_@@_original_unit_tl } { \@@_culang_print:nV { unit-symbol } \l_@@_original_unit_tl } } % \end{macrocode} % \end{macro} % % % % \begin{macro} { \@@_rf_print_range_sign: , \@@_print_times_sign: } % Prints ranges. It simply prints the numbers twice, inbetween a range sign. % \begin{macrocode} \cs_new:Npn \@@_rf_print_range_sign: { % \@@_if_cmd_type_equal:nTF { cunum } % { % \bool_if:NT \l_@@_seperator_after_fraction_bool { \, } % \tl_use:N \l_@@_cunum_range_sign_tl % }{ \bool_lazy_or:nnTF { \@@_if_cmd_type_equal_p:n { cCutext } } { \l_@@_lokal_numeral_bool } { \tl_if_empty:NTF \l_@@_cutext_range_sign_tl { \@@_culang_print:nn { name } { cutext-range-phrase } } { \tl_use:N \l_@@_cutext_range_sign_tl } }{ \bool_if:NT \l_@@_seperator_after_fraction_bool { \, } \tl_use:N \l_@@_cunum_range_sign_tl } % } } \cs_new:Npn \@@_print_times_sign: { \l_@@_times_space_tl \l_@@_times_sign_tl \l_@@_times_space_tl } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_print_numerical_input:N } % The base printing command for numbers. Makes sure the minus sign % and decimal-mark are correct. % \begin{macrocode} \cs_new_protected:Npn \@@_print_numerical_input:N #1 { \tl_if_in:NnT #1 { . } { \@@_culang_let:nnN { name } { decimal-mark } \l_@@_translation_tmpa_tl \tl_replace_once:Nnn #1 { . } { \l_@@_translation_tmpa_tl } } \tl_replace_once:Nnn #1 { - } \c_@@_minus_tl #1 } % \end{macrocode} % \end{macro} % % % % % % \subsection {Parsing and checking numbers} % % This subsection contains macros for splitting and checking the input. % % \begin{macro}{\@@_create_error:n, \@@_create_error_and_stop:n , \@@_create_error:nn } % Create error messages and set case to \enquote{error}. % \begin{macrocode} \cs_new:Npn \@@_create_internal_error:nN #1#2 { \@@_set_state_to:n { error } \msg_error:nnnV { cooking-units } { internal-error } {#1} #2 } \cs_new:Npn \@@_create_internal_error:nn #1#2 { \@@_set_state_to:n { error } \msg_error:nnnn { cooking-units } { internal-error } {#1} #2 } \cs_generate_variant:Nn \@@_create_internal_error:nn { nx } \cs_new:Npn \@@_create_error:n #1 { \@@_set_state_to:n { error } \msg_error:nnV { cooking-units } {#1} \l_@@_original_amount_tl } \cs_new:Npn \@@_create_error_and_stop:n #1 { \@@_create_error:n {#1} \use_none_delimit_by_q_nil:w } \cs_new:Npn \@@_create_error:nn #1#2 { \@@_set_state_to:n { error } \msg_error:nnnV { cooking-units } {#1} {#2} \l_@@_original_amount_tl } \cs_generate_variant:Nn \@@_create_error:nn { nf } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_prepare_just_print_input: } % If we decide the input is just to be printed, set it to \enquote{error} % (because I am lazy and do not want to create another case to check). % \begin{macrocode} \cs_new:Npn \@@_prepare_just_print_input: { \@@_set_state_to:n { error } \tl_if_empty:NT \l_@@_original_amount_tl { \@@_create_error:nn { cooking-units } { empty-amount } } } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{ \@@_parse_input_and_safe_in:nN } % Parses through |#1| and makes sure it is correct. This includes checking if there % is only one decimal sign and other stuff. The \enquote{correct} % parsed stuff is stored in |\l_@@_tmpa_tl|. % \begin{macrocode} \cs_new:Npn \@@_parse_input_and_safe_in:nN #1 #2 { \tl_clear:N \l_@@_tmpa_tl \bool_set_false:N \l_@@_check_decimal_bool \bool_set_false:N \l_@@_minus_bool \cs_set_eq:NN \@@_parse:N \@@_start_loop:N \tl_map_function:nN {#1} \@@_parse:N \@@_if_state_equal:nF { error } { \tl_set_eq:NN #2 \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_start_loop:N } % First, check for signs ($+$ and $-$ by default). If a sign is found and is $+$, do nothing % and repat the sign-checking-process. If the sign is a $-$ reverese the boolean % |\l_@@_minus_bool|. Reversing it is necessary to simulate mathematics of e.g. $+-+-1$ % or $-+-+-1$, etc. If a non-sign is found, check if a minus sign is added and parse % the rest. |\token_if_eq_charcode:NNF| is stolen from \pkg{siunitx}. % \begin{macrocode} \cs_new:Npn \@@_start_loop:N #1 { \tl_if_in:NnTF \l_@@_input_value_signs_tl {#1} { \token_if_eq_charcode:NNT #1 - { \@@_bool_reverse:N \l_@@_minus_bool } }{ \bool_if:NT \l_@@_minus_bool { \tl_put_left:Nn \l_@@_tmpa_tl { - } } \cs_set_eq:NN \@@_parse:N \@@_parse_input_loop_aux:N \tl_map_function:nN {#1} \@@_parse:N } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_input_loop_aux:N , \@@_check_which_error:n , \@@_check_repitition:N } % Parses the input. It also is more or less (more less then more) copied from % \texttt{siunitx}. Checks if the input consists only of numbers, % one decimal-sign and the allowed special input. % In other cases, raise an error. Note that the state special-sign % can only be set if there is no error. % % \begin{macrocode} \cs_new:Npn \@@_parse_input_loop_aux:N #1 { \tl_if_in:NnTF \l_@@_input_digits_tl {#1} { \tl_put_right:Nn \l_@@_tmpa_tl {#1} } { \tl_if_in:NnTF \l_@@_input_decimal_mark_tl {#1} { \@@_check_repitition:N \l_@@_check_decimal_bool \tl_put_right:Nn \l_@@_tmpa_tl { . } }{ \tl_if_in:NnTF \l_@@_input_allowed_special_signs_tl {#1} { \@@_if_state_equal:nF { error } { \@@_set_state_to:n { special_sign } } \tl_put_right:Nn \l_@@_tmpa_tl {#1} } { \@@_check_which_error:n {#1} } } } } % \end{macrocode} % The idea behind this error checking is the following: % At this point I already split the input. This means, a fraction % is already split into nominator and denominator and as such, all allowed % |/| in the input are already removed. The same goes for |_| (an error message % for |_| without |/| would happen before parsing starts). Therefore % all |/| and |_| in the input \emph{must} (hopefully) be inside a second time. % \begin{macrocode} \cs_new:Npn \@@_check_which_error:n #1 { \tl_if_in:NnTF \l_@@_input_fraction_sign_tl {#1} { \@@_create_error:n { Second-fraction-sign-not-allowed } } { \tl_if_in:NnTF \l_@@_input_mixed_fraction_sign_tl {#1} { \bool_if:NTF \l_@@_rf_check_fraction_bool { \@@_create_error:n { Second-mixed-fraction-sign-not-allowed } } { \@@_create_error:n { missing-slash } } } { \@@_create_error:nn { Token-not-allowed } {#1} } } } % \end{macrocode} % Back then this contained more checks, but now it just checks % if we have a second error decimal sign. % \begin{macrocode} \cs_new:Npn \@@_check_repitition:N #1 { \bool_if:NTF #1 { \@@_create_error:n { Second-decimal-sign-not-allowed } } { \bool_set_true:N #1 } } % \end{macrocode} % \end{macro} % % % % % % \subsection {Formatiere \& Calculiere} % % % % % \begin{macro}{ \@@_calculate_and_store_in:N } % % \changes {v0.98a} {2016/06/11} {Added warning to show if an unknown value is used.} % % The main calculation command. The split amount is given to this % command. It is seperated into several smaller macros. Unit-changing % the amount, referencing, rounding and temperature checks are done % here. Note that we are only here if the state is \enquote{normal}. % \begin{macrocode} \cs_new:Npn \@@_calculate_and_store_in:N #1 { \@@_calculate_input_and_store_in:N #1 \@@_calc_references_and_store_in:N #1 \@@_round_calculated_input_and_store_in:N #1 \@@_check_temperature_limit:N #1 } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_calculate_input_and_store_in:NN } % Well \dots\ this function calculates the input. % First check if the units need to be changes at all (if no unit change % is wanted, |\l_@@_option_unit_tl| is set to |\l_@@_original_unit_tl| at the % beginning. If we are in one case were no unit change is happening % (\enquote{special-sign}, \enquote{error}, \enquote{fraction}) we wouldn't be % here in the first place, so need to worry about this. % % As the input is parsed and good for evaluation, store it in % |\l_@@_tmpa_fp|. % % If the key definition contained a |#1| (aka, is a single key), % it was replaced by |\l_@@_tmpa_fp|. % Thus simply evaluate the tl if one or more |\l_@@_tmpa_fp| are inside. % % If no |\l_@@_tmpa_fp| is found, we have a simple multiplication with the % current amount. % \begin{macrocode} \cs_new:Npn \@@_calculate_input_and_store_in:N #1 { \tl_if_eq:NNF \l_@@_decided_unit_tl \l_@@_original_unit_tl { \fp_set:Nn \l_@@_tmpa_fp {#1} \tl_if_in:cnTF { l_@@_unit_change_ \l_@@_original_unit_tl _tl } { \l_@@_tmpa_fp } { \@@_tl_set_eval:Nv #1 { l_@@_unit_change_ \l_@@_original_unit_tl _tl } }{ \@@_tl_set_eval:Nn #1 { \l_@@_tmpa_fp * \tl_use:c { l_@@_unit_change_ \l_@@_original_unit_tl _tl } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_calc_references_and_store_in:N,\@@_calc_references_and_store_in_aux:N } % All necessary checks were already done at the beginning. % so we know that a reference was given and that the wanted number of % persons is different then the one given by |\culabel|. Before % evaluation, check if the unit is one of those that cannot be changed. % \begin{macrocode} \bool_new:N \l_@@_curef_forbidden_unit_msg_used_bool \cs_new:Npn \@@_calc_references_and_store_in:N #1 { \bool_if:NT \l_@@_lokal_recalc_for_people_bool { \seq_if_in:NVTF \l_@@_curef_do_not_change_seq \l_@@_decided_unit_tl { \bool_if:NF \l_@@_curef_forbidden_unit_msg_used_bool { \msg_error:nnV { cooking-units } { curef-forbidden-unit } \l_@@_decided_unit_tl \bool_set_true:N \l_@@_curef_forbidden_unit_msg_used_bool } } { \@@_calc_references_and_store_in_aux:N #1 } } } % \end{macrocode} % Just calculate the stuff % \begin{macrocode} \cs_new:Npn \@@_calc_references_and_store_in_aux:N #1 { \@@_tl_set_eval:Nn #1 { #1 * \l_@@_wanted_number_of_persons_int / \l_@@_local_number_of_persons_int } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_round_calculated_input_and_store_in:Nn,\@@_round_calculated_input_and_store_in:NV } % After calculation, the numbers are rounded (if needed). % If the rounding precision is negative (can be), just set the bool to true and go % to the rounding function. % Else check if the number needs rounding at all. % Afterwards, round if necessary % \begin{macrocode} \cs_new:Npn \@@_round_calculated_input_and_store_in:N #1 { \bool_if:NTF \l_@@_pckopt_round_precision_negative_bool { \bool_set_true:N \l_@@_round_number_bool } { \@@_check_number_for_rounding:V #1 } \bool_if:NT \l_@@_round_number_bool { \@@_tl_set_eval:Nn #1 { round ( \@@_rounding_function:n {#1} ) } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_check_number_for_rounding:n,\@@_check_number_after_dot_aux:ww} % Check if rounding is needed. The auxilary function splits the number into % the integer and non-integer part ($12.45$ to $12$ and $45$). % % Set |\l_@@_round_number_bool| to |false| in case of no decimal places found. % % If the rounding precision is positive, use the helper function to split the % amount. % \begin{macrocode} \cs_new:Npn \@@_check_number_for_rounding:n #1 { \bool_set_false:N \l_@@_round_number_bool \@@_check_number_after_dot_aux:ww #1 . \q_recursion_tail . \q_recursion_stop } \cs_generate_variant:Nn \@@_check_number_for_rounding:n { V } % \end{macrocode} % The function splitting the amount. So $a.b$ is split into $a$ and $b$. % If $b$ does not exist (so the amount is an integer), no rounding is needed. % If $b$ does exist, count how many tokens it conists of (e.g.\ $12$ = 2 tokens; % $1755$ = 4 tokens). If the number of tokens is larger then the wanted round precision, % round the input. % \begin{macrocode} \cs_new:Npn \@@_check_number_after_dot_aux:ww #1 . #2 . { \quark_if_recursion_tail_stop:n {#2} \int_compare:nNnT { \tl_count:n {#2} } > { \l_@@_round_precision_int } { \bool_set_true:N \l_@@_round_number_bool } \use_none_delimit_by_q_recursion_stop:w } % \end{macrocode} % \end{macro} % % % % % % % % % % \begin{macro}{\@@_check_temperature_limit:N} % If \lstinline|check_temperature_bool| is set to true it now checks % if the value is below the absolute temperature. Maybe this should be done % before rounding? % \begin{macrocode} \cs_new:Npn \@@_check_temperature_limit:N #1 { \bool_if:NT \l_@@_pckopt_check_temperature_bool { \seq_if_in:NVT \l_@@_temperatures_to_check_seq \l_@@_decided_unit_tl { \fp_compare:vNnT { l_@@_ \l_@@_decided_unit_tl _min_fp } > {#1} { \msg_error:nnxx { cooking-units } { Temperature-too-low } { #1 \space \l_@@_decided_unit_tl } { \fp_use:c { l_@@_ \l_@@_decided_unit_tl _min_fp } \space \l_@@_decided_unit_tl } } } } } % \end{macrocode} % \end{macro} % % % % % % % % % % % \subsection {Main commands helper} % % \begin{macro}{ \@@_if_arrow_grab_until_close_do:nnTF , % \@@_grab_arrows_for_safety_do_afterwards:nN } % I really do not like this constuction, but due to catcodes it can happen % that the main commands are unable to get the first command inside <>. % In order to fix this, I use this bloody construction. % \begin{macrocode} \cs_new_nopar:Npn \@@_if_arrow_grab_until_close_do:nnTF #1#2#3#4 { \str_if_eq:nnTF {#1} { < } { \tl_if_in:NoT \l_@@_input_allowed_special_signs_tl { < } { \msg_error:nn { cooking-units } { <-not-allowed-as-special-sign } } \tl_clear:N \l_@@_tmpa_tl \tl_put_right:Nx \l_@@_tmpa_tl { \tl_to_str:N < } \@@_grab_arrows_for_safety_do_afterwards:nN {#3} #2 } {#4} } \cs_new_nopar:Npn \@@_grab_arrows_for_safety_do_afterwards:nN #1#2 { \str_if_eq:nnTF {#2} { > } { \tl_put_right:Nx \l_@@_tmpa_tl { \tl_to_str:N > } \exp_last_unbraced:NV #1 \l_@@_tmpa_tl }{ \tl_put_right:Nn \l_@@_tmpa_tl {#2} \@@_grab_arrows_for_safety_do_afterwards:nN {#1} } } % \end{macrocode} % \end{macro} % % % \subsection {\mycs{cunum}} % % % \begin{macro}{ \cunum } % The main command of this package. % \begin{macrocode} \NewDocumentCommand \cunum { d<> O{} m O{} m } { \@@_if_arrow_grab_until_close_do:nnTF {#3} {#5} { \cunum } { \group_begin: \@@_cunum:nnnnn {#1} {#2} {#3} {#4} {#5} \group_end: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_cunum:nnnnn } % The \enquote{helper} command. It essentially only sets % the command type to \enquote{cunum} and calls the command that does % everything else. % \begin{macrocode} \cs_new:Npn \@@_cunum:nnnnn #1#2#3#4#5 { \@@_set_cmd_type_to:n { cunum } \@@_rf_main_command:nnnnnn {#1} {#2} {#3} {#4} {#5} { } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_cunum_print_correct_unit: } % The invisible space is added by a \lstinline|\phantom|, afterwards % \lstinline|value_unit_space_tl| is used (which is set to % \lstinline|\thinspace| by default) and if either special signs or % fractions are parsed the input-unit is printed else the converted % unit is. % \begin{macrocode} \cs_new:Npn \@@_cunum_print_correct_unit: { \tl_if_empty:NF \l_@@_phantom_tl { \phantom { \l_@@_phantom_tl } } \tl_use:N \l_@@_value_unit_space_tl \@@_culang_print:nV { unit-symbol } \l_@@_decided_unit_tl } % \end{macrocode} % \end{macro} % % % \section { cutext \& Cutext } % % % \begin{macro}{\cutext} % The other main command It is essentially similar to \mycs{cunum}, % only that we also need to take care of uppercasing the word if necessary % (which it is not for this command). % \begin{macrocode} \NewDocumentCommand \cutext { d<> O{} m m } { \@@_if_arrow_grab_until_close_do:nnTF {#3} {#4} { \cutext } { \group_begin: \bool_set_false:N \l_@@_cutext_uppercase_word_bool \@@_cutext:nnnn {#1} {#2} {#3} {#4} \group_end: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\Cutext} % Here we do need the uppercasing. % \begin{macrocode} \NewDocumentCommand \Cutext { d<> O{} m m } { \@@_if_arrow_grab_until_close_do:nnTF {#3} {#4} { \Cutext } { \group_begin: \bool_set_true:N \l_@@_cutext_uppercase_word_bool \@@_cutext:nnnn {#1} {#2} {#3} {#4} \group_end: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_cutext:nnnn } % Same as the \mycs{cunum} equivalent, just setting the command to % \enquote{cCutext}. % \begin{macrocode} \cs_new:Npn \@@_cutext:nnnn #1#2#3#4 { \@@_set_cmd_type_to:n { cCutext } \@@_rf_main_command:nnnnnn {#1} {#2} {#3} { } {#4} { } } % \end{macrocode} % \end{macro} % % % % \begin{macro} {\@@_cutext_print_input:N , \@@_cutext_print_input_aux:Nn , \@@_cutext_print_input_aux:NV } % \changes {2016/06/11} {0.99} {New.} % % This prints the amount for the \enquote{cCutext} commands. % At first, check if a numeral should be used (\enquote{1} to \enquote{one}, etc.). % If not, just print the number. If a numeral should be used, make sure the correct % unit is used and go to the next command. % \begin{macrocode} \cs_new_protected:Npn \@@_cutext_print_input:N #1 { \bool_if:NTF \l_@@_lokal_numeral_bool { \@@_cutext_print_input_aux:NV #1 \l_@@_decided_unit_tl } { \@@_print_numerical_input:N #1 } } % \end{macrocode} % If a numeral is used, make sure the correct one is printed. % If the amount is equal to \enquote{1}, get the gender correct \enquote{one}. % Else, print the numeral either in upperacse of lowercase. % % As we could be in a range, we cannot check via a bool if |#1| is 1. % % Furthermore, make sure the translation of \enquote{1} exists. If not, % set input it set to \enquote{1}. % \begin{macrocode} \cs_new_protected:Npn \@@_cutext_print_input_aux:Nn #1#2 { \int_compare:nNnTF {#1} = { 1 } { \@@_culang_one_let:NnT \l_@@_translation_tmpa_tl {#2} { \bool_if:NT \l_@@_cutext_uppercase_word_bool { \text_uppercase:x { \tl_head:V \l_@@_translation_tmpa_tl } \tl_tail:V } } \l_@@_translation_tmpa_tl }{ \bool_if:NTF \l_@@_cutext_uppercase_word_bool { \@@_print_Numeral:V #1 } { \@@_print_numeral:V #1 } } } \cs_generate_variant:Nn \@@_cutext_print_input_aux:Nn { NV } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cutext_print_correct_unitname:} % As the name suggests, it prints the unitname. At this point we already % know that a translation exists, so no bother checking again. % \begin{macrocode} \cs_new:Npn \@@_cutext_print_correct_unitname: { \bool_if:NTF \l_@@_lokal_numeral_bool { \l_@@_cutext_numeral_space_tl } { \l_@@_cutext_space_tl } \bool_if:NTF \l_@@_cutext_use_single_bool { \@@_culang_print:nV { name } \l_@@_decided_unit_tl } { \@@_culang_print:nV { name-pl } \l_@@_decided_unit_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_cutext_check_if_translation_available: } % Checks if a unitname of the current unit exists. If yes, continue. % here we can also decide if the unit should be kept the same or not % as we know that we stay in \enquote{cCutext}. % If not, change the command to \enquote{cunum}. % \begin{macrocode} \cs_new:Npn \@@_rf_cutext_check_if_translation_available: { \bool_if:NTF \l_@@_pckopt_cutext_keep_unit_bool { \tl_set_eq:NN \l_@@_curr_unit_tl \l_@@_original_unit_tl } { \tl_set_eq:NN \l_@@_curr_unit_tl \l_@@_decided_unit_tl } \@@_culang_unitname_let:NVTF \l_@@_scrap_tl \l_@@_curr_unit_tl { \bool_if:NT \l_@@_pckopt_cutext_keep_unit_bool { \tl_set_eq:NN \l_@@_decided_unit_tl \l_@@_original_unit_tl \tl_set_eq:NN \l_@@_option_unit_tl \l_@@_original_unit_tl } }{ \msg_warning:nnx { cooking-units } { cutext-no-translation-available } \l_@@_curr_unit_tl \@@_set_cmd_type_to:n { cunum } \@@_decide_unit: } } % \end{macrocode} % \end{macro} % % % \begin{macro}{ \@@_cutext_post_process_input:NN } % |#1| is always a number, |#2| only is valid if we have a range. % For later, check if either |#1| or |#2| is equal to one. % % Then (Than?) check if a numeral is requested. If yes, |#1| (and if range also |#2|) % need to be integers. If they are not, numerals cannot be used. If one or both % are integers, if they are below |\l_@@_print_numerals_below_int|. % \begin{macrocode} % \end{macrocode} % \end{macro} % \begin{macro} { \@@_cutext_post_process_fraction_input:NNN } % \begin{macrocode} % \end{macrocode} % \end{macro} % \begin{macro} { \@@_cutext_check_if_plural_is_needed:N } % Check if the input is equal to |#1|. Use fp as it is not necessary an integer. % Set the boolean according to the result. % \begin{macrocode} %\cs_new:Npn \@@_cutext_check_if_plural_is_needed:N #1 % { % \fp_compare:nNnTF {#1} = { 1.0 } % { \bool_set_true:N } { \bool_set_false:N } % \l_@@_cutext_use_single_bool % } % \end{macrocode} % \end{macro} % % % % % % % % % % % % \section{cuam} % % \begin{macro}{ \c_@@_no_unit_marker_tl , \@@_if_no_unit_marker:NTF } % A marker used for \mycs{cuam}. It essentially tells that no unit is used. % Define some commands to avoid some errors and add it to the unit list. % \begin{macrocode} \tl_const:Nx \c_@@_no_unit_marker_tl { \@@_to_unit:n { @@_no_unit } } \prg_new_conditional:Npnn \@@_if_no_unit_marker:N #1 { TF , F } { \tl_if_eq:NNTF \c_@@_no_unit_marker_tl #1 { \tl_if_eq:NNF \l_@@_cmd_type_tl \c_@@_cmd_type_cuam_tl { \@@_create_internal_error:nN { cmd-type-no-unit-maker } \l_@@_cmd_type_tl } \prg_return_true: } { \prg_return_false: } } \seq_gput_right:NV \g_@@_list_of_defined_units_seq \c_@@_no_unit_marker_tl \tl_new:c { l_@@_unit_change_ \c_@@_no_unit_marker_tl _tl } \tl_set:cn { l_@@_unit_change_ \c_@@_no_unit_marker_tl _tl } { 1.0 } \clist_new:c { l_@@_unit_option_ \c_@@_no_unit_marker_tl _clist } % \end{macrocode} % % \begin{macro}{\cuam} % Replaces and extends \mycs{cufrac}. Essentially the same as the other commands. % \begin{macrocode} \NewDocumentCommand \cuam { d<> O{} m } { \@@_if_arrow_grab_until_close_do:nnTF {#3} { } { \cuam } { \group_begin: \@@_cuam:nnn {#1} {#2} {#3} \group_end: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cuam:nnn} % Nothing really special happening here. Use |\c_@@_no_unit_marker_tl| % to tell the internal thing that we have a valid no-unit-exists case. % \begin{macrocode} \cs_new:Npn \@@_cuam:nnn #1#2#3 { \@@_set_cmd_type_to:n { cuam } \@@_rf_main_command:nnnnVn {#1} {#2} {#3} { } \c_@@_no_unit_marker_tl { } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_cuam_post_process_input} % % % Just a helper macro to tidy up the main macro. The first thing to % do is to check if the numerical input consits of integers. This % is done both both numerical values (if a range exists). % Afterwards check if phrases are used and can be used at all. This needs % to be done before the numeral check as it can change the input. % \begin{macrocode} % \end{macrocode} % \end{macro} %\begin{macro}{ \@@_cuam_post_process_input_phrases:NN} % % I really do not like this command, but I still do not have a bette idea % how to handle the whole thing. % % Does a list of phrases exist % for the current language? If yes, parse the input and check if phrases are used. % It is important for ranges to check both values and then decide if phrases % are used or not. % \begin{macrocode} % \end{macrocode} % \begin{macrocode} % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_cuam_print_numerical_input:N, \@@_cuam_print_numerical_input_aux:Nn, \@@_cuam_print_numerical_input_aux:NV } % Printing the numerical input for \mycs{cuam}. If both phrases and numerals % are requested, go to the helper macro. Else, just print the numerical input. % We need both to be true or else it can happen that numerals are used % without anything to follow it up (Instead of \enquote{zwei Dutzend} we simply would % have \enquote{zwei}). % \begin{macrocode} \cs_new_protected:Npn \@@_cuam_print_numerical_input:N #1 { \bool_lazy_and:nnTF { \l_@@_lokal_phrase_bool } { \l_@@_lokal_numeral_bool } { \@@_cuam_print_numerical_input_aux:NV #1\l_@@_phrase_number_tl } { \@@_print_numerical_input:N #1 } } % \end{macrocode} % Currently \mycs{cuam} only allows for lowercase numerals. % \begin{macrocode} \cs_new:Npn \@@_cuam_print_numerical_input_aux:Nn #1#2 { \int_compare:nNnTF {#1} = { 1 } { \@@_culang_one_let_by_gender:NVTF \l_@@_translation_tmpa_tl \l_@@_phrase_gender_tl { \l_@@_translation_tmpa_tl } { \@@_print_numeral:V #1 \bool_set_false:N \l_@@_lokal_numeral_bool } } { \@@_print_numeral:V #1 } } \cs_generate_variant:Nn \@@_cuam_print_numerical_input_aux:Nn { NV } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_cuam_print_correct_unitphrase: } % THis one is thankfully straightforward. % \begin{macrocode} \cs_new:Npn \@@_cuam_print_correct_unitphrase: { \bool_if:NT \l_@@_lokal_phrase_bool { \l_@@_cuphrase_space_tl \l_@@_phrase_text_tl } } % \end{macrocode} % \end{macro} % % % % % \subsection{Public Primitives} % % Two public commands. % \begin{macro}{ \cookingunits_print_fraction:nnn } % \begin{macrocode} \cs_new:Npn \cookingunits_print_fraction:nnn #1#2#3 { \group_begin: \@@_print_fraction:nnnn { 0 } {#1} {#2} {#3} \group_end: } % \end{macrocode} % \end{macro} % \begin{macro}{ \cookingunits_print_range:nn } % \begin{macrocode} \cs_new:Npn \cookingunits_print_range:nn #1#2 { \group_begin: \@@_set_cmd_type_to:n { cunum } \@@_print_number_default:n {#1} \@@_rf_print_range_sign: \@@_print_number_default:n {#2} \group_end: } % \end{macrocode} % \end{macro} % % \subsection{\mycs{cucomp}} % % % \begin{macrocode} %\NewDocumentCommand \cucomp { d<> O{} m m m } % { % \group_begin: % \@@_cucomp:nnnnn {#1} {#2} {#3} {#4} {#5} % \group_end: % } % \end{macrocode} % \begin{macrocode} %\cs_new:Npn \@@_cucomp:nnnnn #1#2 #3#4#5 % { % \group_begin: % \@@_set_cmd_type_to:n { cunum } % \@@_do_the_stuff:nnnnnn {#1} {#2} {#3} { } {#4} { } % / % \@@_do_the_stuff:nnnnnn {#1} {#4=#5} {#3} { } {#4} { } % \group_end: % } % \end{macrocode} % % \begin{macrocode} %\cs_new:Npn \@@_do_cucomp_stuff:nnnnn #1#2 #3#4#5 % { % \group_begin: % \@@_set_cmd_to:n { cucomp } % \@@_do_the_stuff:nnnnnn {#1} {#2} {#3} { } {#4} % \@@_do_the_stuff:nnnnnn {#1} {#2} {#3} { } {#5} % \group_end: % } % \end{macrocode} % % \begin{macrocode} %\cs_new:Npn \@@_new_process_compare_in_input: % { % \tl_set_eq:NN \l_@@_number_tmpb_tl \l_@@_number_tmpa_tl % \tl_set_eq:NN \l_@@_tmpa_unit_tl \l_@@_option_unit_tl % \@@_calculate_and_store_in:N \l_@@_number_tmpa_tl % \@@_calculate_and_store_in:N \l_@@_number_tmpb_tl % \@@_post_process_accordingly:NN \l_@@_number_tmpa_tl \q_no_value % } % \end{macrocode} % % % % \section {cukeys} % % \subsection {Define Keys} % % \begin{macro}{ } % Defining keys. % \begin{macrocode} %% To be deleted. In order to remove error messages. %\newcommand \cudefinekeys {} %\newcommand \cudefinesinglekey {} %\newcommand \cuaddkeys {} %\newcommand \cuaddsinglekeys {} %\newcommand \cuaddtokeys {} % \end{macrocode} % \end{macro} % % % % % \begin{macro}{ \cudefinekeys , \cudefinesinglekey } % Commands used for defining unit-keys. Their main differences % is in setting |\l_@@_single_key_bool| to the respective value % and for single-keys to rescan |#2| as it can contain |#1| in its argument. % % The other stuff is imply a intialization using the unit in |#1| as a base % and parsing the rest. % \begin{macrocode} \NewDocumentCommand \cudefinekeys { m m } { \bool_set_false:N \l_@@_single_key_bool \@@_initialice_cukeys_define:n {#1} \@@_cukeys_prepare_parsing_keys:nn {#1} {#2} } \NewDocumentCommand \cudefinesinglekey { m m } { \bool_set_true:N \l_@@_single_key_bool \@@_initialice_cukeys_define:n {#1} \tl_set_rescan:Nno \l_@@_tmpa_tl { \char_set_catcode_letter:N \# } {#2} \@@_cukeys_prepare_parsing_keys:nV {#1} \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_initialice_cukeys_define:n } % Using |#1| as the base unit for the following keys we set some variables. % |\l_@@_tmpa_seq| will contain the parsed units; |\l_@@_tmpb_seq| the values. % We also make sure the unit |#1| is defined, else create an error. % Also of importance is to set |\l_@@_new_key_base_tl| to $1$. This variable % is important while adding keys as it sets the base conversion value so to speak. % \begin{macrocode} \cs_new:Npn \@@_initialice_cukeys_define:n #1 { \@@_set_state_to:n { normal } \seq_clear:N \l_@@_tmpa_seq \seq_clear:N \l_@@_tmpb_seq \prop_clear:N \l_@@_tmpa_prop \tl_set:Nn \l_@@_new_key_base_tl { 1 } \@@_error_if_unit_not_defined:nNT {#1} \l_@@_original_unit_tl { \@@_set_state_to:n { error } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\cudefinekeychain} % The command that is going to replace \mycs{cudefinekeys} and friends % (but \emph{not} the single key variants). Nothing going on here. % The biggest difference to \mycs{cudefinekeys} is that we do not know % the base key by default. Thus we make a short detour to grab the first unit % we encounter in |#1| (and its value) and the rest proceeeds as before. % \begin{macrocode} \NewDocumentCommand \cudefinekeychain { m } { \bool_set_false:N \l_@@_single_key_bool \@@_initialice_cukeychain_define:n {#1} \@@_cukeys_prepare_parsing_keys:n {#1} } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_initialice_cukeychain_define:n} % Initializing the key-chain. First, grab the first unit in |#1|. This needs to % be done as the old code expects us to already know a base unit. Here the first % unit that is encountered is taken as the base unit. Its value is stored in |\l_@@_base_tl|. % This is needed to normalice the values in relation to the base unit. Remember, % the base unit is defined to be exactly |1|, thus all further values need to be % changed to reflect that. The normalization factor is stored inside |\l_@@_new_key_base_tl|. % |\l_@@_curr_unit_tl| is cleared to remind me, that it is not a unit per se % (as it is not set \emph{via} |\@@_set_unit:Nn|. % \begin{macrocode} \cs_new:Npn \@@_initialice_cukeychain_define:n #1 { \@@_cukeychain_get_first_unit_aux:nn #1 \q_recursion_tail \q_recursion_tail \q_recursion_stop \@@_if_state_equal:nF { error } { \exp_args:NV \@@_initialice_cukeys_define:n \l_@@_curr_unit_tl \@@_tl_set_eval:Nn \l_@@_new_key_base_tl { 1 / (\l_@@_base_tl) } \tl_clear:N \l_@@_curr_unit_tl } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_cukeychain_get_first_unit_aux:nn} % Nothing fancy here. Just grab the first unit and its value. % \begin{macrocode} \cs_new:Npn \@@_cukeychain_get_first_unit_aux:nn #1#2 { \quark_if_recursion_tail_stop_do:nn {#1} { \@@_set_state_to:n { error } } \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nn { cooking-units } { missing-argument } \@@_set_state_to:n { error } } \tl_set:Nn \l_@@_curr_unit_tl {#1} \tl_set:Nn \l_@@_base_tl {#2} \use_none_delimit_by_q_recursion_stop:w \@@_cukeychain_get_first_unit_aux:nn } % \end{macrocode} % \end{macro} % % % % % % % % \begin{macro}{ \cuaddkeys , \cuaddtokeys , \cuaddsinglekeys } % Thanks to rewriting the code the |add| commands and the |define| % commands share a lot of code. The difference is the initialicing, which is a bit % different. % \begin{macrocode} \NewDocumentCommand \cuaddkeys { m m } { \bool_set_false:N \l_@@_single_key_bool \@@_initialice_cukeys_add:n {#1} \@@_cukeys_prepare_parsing_keys:nn {#1} {#2} } \NewDocumentCommand \cuaddtokeys { m m m } { \bool_set_false:N \l_@@_single_key_bool \@@_initialice_cukeys_add:n {#1} \tl_set:Nn \l_@@_tmpa_tl { {#2} { \fp_eval:n { 1.0 / (#3) } } } \@@_cukeys_prepare_parsing_keys:nV {#1} \l_@@_tmpa_tl } \NewDocumentCommand \cuaddsinglekeys { m m } { \bool_set_true:N \l_@@_single_key_bool \@@_initialice_cukeys_add:n {#1} \tl_set_rescan:Nno \l_@@_tmpa_tl { \char_set_catcode_letter:N \# } {#2} \@@_cukeys_prepare_parsing_keys:nV {#1} \l_@@_tmpa_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_initialice_cukeys_add:n } % This one is quite different compared to |\@@_initialice_cukeys_define:n| as it % needs a bit more stuff to do. % % The beginning is checking if |#1| exists and later if |#1| is a key at all. % If it not a key already we cannot add keys, in either case print an error and % go to the end. % % If the checks passed without error we go to the \enquote{retrieving} step. % As we are adding (potentially) new keys to existing ones we need to retrive % the values we stored and got during the definition of keys. % % First, get the |\l_@@_original_unit_tl|. This one is the unit given in the first argument % of either |cudefinekeys| or |\cudefinesinglekey|. It is the base unit % for those keys. Afterwards retrieve the other values. Get the property list % which stores the unit-value pairs, get the list of units we can transform the unit to % and -- to make sure -- make sure a single key is only added to a single key and vice versa. % % Afterwards, this cs is done. % \begin{macrocode} \cs_new:Npn \@@_initialice_cukeys_add:n #1 { \seq_clear:N \l_@@_tmpa_seq \seq_clear:N \l_@@_tmpb_seq \@@_set_state_to:n { normal } \@@_error_if_unit_not_defined:nNT {#1} \l_@@_curr_unit_tl { \@@_set_state_to:n { error } \use_none_delimit_by_q_stop:w } \@@_if_unit_has_a_key:nF {#1} { \msg_error:nnx { cooking-units } { Key-not-defined } \l_@@_curr_unit_tl \@@_set_state_to:n { error } \use_none_delimit_by_q_stop:w } \tl_set_eq:Nc \l_@@_original_unit_tl { l_@@_base_key_unit_ \l_@@_curr_unit_tl _tl } \prop_set_eq:Nc \l_@@_tmpa_prop { l_@@_rf_cukeys_ \l_@@_original_unit_tl _prop } \prop_get:NnN \l_@@_tmpa_prop {#1} \l_@@_new_key_base_tl \prop_get:NnN \l_@@_tmpa_prop { Liste } \l_@@_tmpa_seq \prop_get:NnN \l_@@_tmpa_prop { single } \l_@@_tmpa_tl \int_compare:nNnF { \l_@@_single_key_bool } = { \l_@@_tmpa_tl } { \msg_error:nn { cooking-units } { Adding-single-to-multi-key-or-vice-verca } \@@_set_state_to:n { error } } \use_none_delimit_by_q_stop:w \q_stop } \cs_generate_variant:Nn \@@_initialice_cukeys_add:n { V } % \end{macrocode} % \end{macro} % % % \begin{macro}{\cuaddtokeychain} % This command is going to replace \mycs{cuaddkeys} and \mycs{cuaddtokeys}. % It is similar to \mycs{cudefinekeychain}. Just with some more hoops before the keys % can be defined. % \begin{macrocode} \NewDocumentCommand \cuaddtokeychain { m } { \bool_set_false:N \l_@@_single_key_bool \@@_initialice_cukeys_keychain_add:n {#1} \@@_cukeys_prepare_parsing_keys:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_initialice_cukeys_keychain_add:n} % Initiliazing the key-chain. Like \mycs{cudefinekeychain} we first need to get % the base unit. Afterwards, the \enquote{normal} initialization command is used % (with the found base key) and |\l_@@_new_key_base_tl| is normalized. % \begin{macrocode} \cs_new:Npn \@@_initialice_cukeys_keychain_add:n #1 { \@@_cukeys_keychain_get_first_valid_unit:n {#1} \@@_if_state_equal:nF { error } { \@@_initialice_cukeys_add:V \l_@@_curr_unit_tl \@@_tl_set_eval:Nn \l_@@_new_key_base_tl { (\l_@@_new_key_base_tl) / (\l_@@_base_tl) } } } % \end{macrocode} % \end{macro} % \begin{macro}{\@@_cukeys_keychain_get_first_valid_unit:n % , \@@_cukeys_keychain_get_first_valid_unit_aux:nn % } % % Just a wrapper for the loop. % \begin{macrocode} \cs_new:Npn \@@_cukeys_keychain_get_first_valid_unit:n #1 { \@@_cukeys_keychain_get_first_valid_unit_aux:nn #1 \q_recursion_tail \q_recursion_tail \q_recursion_stop } % \end{macrocode} % Finding the base unit is a bit more complex than just taking the first % one found, but not by much. The idea is simple: Loop through the units % till one is found that has an existing key. This allows the user to set the % new unit(s) first in the loop and the base unit at last place (or an arbitrary place). % We set this unit to be the |\l_@@_original_unit_tl| and its value as |\l_@@_base_tl| % (not evaluated at this place). If a unit is found, break the loop. % \begin{macrocode} \cs_new:Npn \@@_cukeys_keychain_get_first_valid_unit_aux:nn #1#2 { \quark_if_recursion_tail_stop_do:nn {#1} { \msg_error:nn { cooking-units } { Key-chain-unit-not-found } \@@_set_state_to:n { error } } \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nn { cooking-units } { missing-argument } \@@_set_state_to:n { error } } \@@_if_unit_has_a_key:nT {#1} { \@@_error_if_unit_not_defined:nNT {#1} \l_@@_curr_unit_tl { \@@_set_state_to:n { error } } \tl_set_eq:NN \l_@@_original_unit_tl \l_@@_curr_unit_tl \tl_set:Nn \l_@@_base_tl {#2} \use_none_delimit_by_q_recursion_stop:w } \@@_cukeys_keychain_get_first_valid_unit_aux:nn } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{ \@@_cukeys_prepare_parsing_keys:n % , \@@_cukeys_prepare_parsing_keys:nn % } % Just a wrapper. |#1| is the base unit and thus defined to be |1|. % \begin{macrocode} \cs_new:Npn \@@_cukeys_prepare_parsing_keys:nn #1#2 { \@@_cukeys_prepare_parsing_keys:n { {#1} { 1.0 } #2 } } % After initialising the variables we go to this command. % At first it seperates the input in |#1| into unit-value pairs via |\@@_cukeys_parse_keys|. % If no error happened, we go to the definition of keys. % % As we add new keys and potentially change the values of old ones we need % to globally clear |\g_@@_master_changer_prop|. \cs_new:Npn \@@_cukeys_prepare_parsing_keys:n #1 { \@@_cukeys_parse_keys:n {#1} \bool_lazy_or:nnF { \int_compare_p:nNn \l_@@_nr_of_units_int = 0 } { \@@_if_state_equal_p:n { error } } { \prop_gclear:N \g_@@_master_changer_prop \@@_cukeys_prepare_lists:N \l_@@_original_unit_tl \@@_rf_cukeys_create_keys:V \l_@@_original_unit_tl } } \cs_generate_variant:Nn \@@_cukeys_prepare_parsing_keys:nn { nV } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_cukeys_parse_keys:nn , \@@_cukeys_parse_keys_aux:nn} % Seperates the input into key-value pairs. This cs doesn't do much as the main % stuff is done in |\@@_cukeys_parse_keys_aux:nn|. % \begin{macrocode} \cs_new:Npn \@@_cukeys_parse_keys:n #1 { \int_zero:N \l_@@_nr_of_units_int \@@_cukeys_parse_keys_aux:nn #1 \q_recursion_tail \q_recursion_tail \q_recursion_stop } % \end{macrocode} % % Note for the beginning: % Keys used for definining (#1) are converted into strings! % No need to keep catcodes. Just string everything! % % This command does a lot. The first is the stopping condition, then two % checks for error messages. Either a \enquote{missing-argument} or a \enquote{unit % not defined} thingy. % % The value is stored in |\l_@@_tmpb_tl|. Afterwards check if a |##1| is found inside. % If it is, replace |##1| by |\l_@@_tmpa_fp|. So we do not store a command with an % argument, but rather a formula with a variable inside, which we can set. % If on ther other side it contains a |##1|, but is not a single key, we raise an error. % % Afterwards -- if nor error -- we calculate the value in |\l_@@_tmpb_tl| if we have no % single key. Here |\l_@@_new_key_base_tl| comes into play. The idea is the following: % the base key is \enquote{kg} with \enquote{1}, furthermore we have the key % \enquote{dag} with value \enquote{100}. Now we add \enquote{g} to \enquote{dag} % with \enquote{10} ($1$ dag equals 10 g). In order to have the correct relation betwen % the base key \enquote{kg} and \enquote{g}, we set |\l_@@_new_key_base_tl| during % the initialization to \enquote{100}. Then the relation between \enquote{kg} and % \enquote{g} is correct with \enquote{(10)*100=1000}. % % Add the unit to the list of units; add the unit-value to the propery-list. % \begin{macrocode} \cs_new:Npn \@@_cukeys_parse_keys_aux:nn #1#2 { \quark_if_recursion_tail_stop:n {#1} \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nn { cooking-units } { missing-argument } \@@_set_state_to:n { error } } \@@_error_if_unit_not_defined:nNT {#1} \l_@@_option_unit_tl { \@@_set_state_to:n { error } } % \tl_set:Nn \l_@@_tmpb_tl {#2} \tl_if_in:NVT \l_@@_tmpb_tl \g_@@_input_str_hash_one_tl { \bool_if:NTF \l_@@_single_key_bool { \tl_replace_all:NVn \l_@@_tmpb_tl \g_@@_input_str_hash_one_tl { \l_@@_tmpa_fp } } { \msg_error:nnx { cooking-units } { formula-in-non-singlekeys } \l_@@_tmpb_tl \@@_set_state_to:n { error } } } \@@_if_state_equal:nF { error } { \int_incr:N \l_@@_nr_of_units_int \bool_if:NF \l_@@_single_key_bool { \@@_tl_set_eval:Nn \l_@@_tmpb_tl { (\l_@@_tmpb_tl) * (\l_@@_new_key_base_tl) } } \seq_if_in:NVF \l_@@_tmpa_seq \l_@@_option_unit_tl { \seq_put_right:NV \l_@@_tmpa_seq \l_@@_option_unit_tl } \prop_put:NnV \l_@@_tmpa_prop {#1} \l_@@_tmpb_tl } \@@_cukeys_parse_keys_aux:nn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_cukeys_prepare_lists:N } % At this point we can assume everything to be correct. % |#1| the main unit aka.\ the \enquote{original unit} or \enquote{base unit}. % % First we clear/create new the property list which will contains the conversion values for % for each unit. There we will safe the the values we collected in the last step, % the list of units in this set and if the key is single or not. % % For simplification, each unit in the key-list always points to the \enquote{base unit}, % so \enquote{g}, \enquote{kg}, \enquote{dag}, etc.\ will all show to \enquote{kg}. % This is not needed if we have a single-key (due to how single-keys work). % Furhtermore, if no single-key, set the default transformation value to \enquote{1}; % not sure if the last step is necessary, but eh. % \begin{macrocode} \cs_new:Npn \@@_cukeys_prepare_lists:N #1 { \prop_clear_new:c { l_@@_rf_cukeys_ #1 _prop } \prop_set_eq:cN { l_@@_rf_cukeys_ #1 _prop } \l_@@_tmpa_prop \prop_put:cnV { l_@@_rf_cukeys_ #1 _prop } { Liste } \l_@@_tmpa_seq \prop_put:cnV { l_@@_rf_cukeys_ #1 _prop } { single } \l_@@_single_key_bool \tl_set:cn { l_@@_unit_change_ #1 _tl } { 1 } \tl_set_eq:cN { l_@@_base_key_unit_ #1 _tl } #1 \bool_if:NF \l_@@_single_key_bool { \seq_map_inline:Nn \l_@@_tmpa_seq { \tl_set_eq:cN { l_@@_base_key_unit_ ##1 _tl } #1 \tl_set:cn { l_@@_unit_change_ ##1 _tl } { 1 } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_cukeys_create_keys:n , \@@_rf_cukeys_create_keys:V } % Here we start creating the keys. If seperate into two cases: % Do we have a single-key or not. If yes, just create the key |#1| % and do what needs to be done. If not, we need to create a bunch of % keys for each unit in the list. % \begin{macrocode} \cs_new:Npn \@@_rf_cukeys_create_keys:n #1 { % % #1: original_unit \clist_set_from_seq:NN \l_@@_tmpa_clist \l_@@_tmpa_seq \bool_if:NTF \l_@@_single_key_bool { \@@_rf_cukeys_define_key:nVn {#1} \l_@@_tmpa_clist {#1} }{ \seq_map_inline:Nn \l_@@_tmpa_seq { \@@_rf_cukeys_define_key:nVn {##1} \l_@@_tmpa_clist {#1} } } } \cs_generate_variant:Nn \@@_rf_cukeys_create_keys:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_cukeys_define_key:nnn , \@@_rf_cukeys_define_key:nVn } % Now we define some key(s). |#1| is the unit and key-to-be-defined. % As |#1| is not expanded, this can contain control sequences, etc. not need for worry % here. % % We define the key |#1| with a list of choices |#2| (which is the list of units % collected in the parse step). |#3| plays an important role as it is the % \enquote{base unit}. We mainly need this to do some shortcuts later on. % So instead of getting the the base unit for \enquote{g} \emph{via} % |l_@@_base_key_unit_g_tl| (which would be \enquote{kg}), we give \enquote{kg} % directly as the argument |#3|. It was made sure that |#3| \emph{not} a % control sequence, but text. % % In order to remind the user which choices each key have, define an error % message for unknown keys. % \begin{macrocode} \cs_new:Npn \@@_rf_cukeys_define_key:nnn #1#2#3 { \@@_if_unit_has_a_key:nF {#1} { \seq_put_right:Nn \l_@@_list_of_defined_keys_seq {#1} } \keys_define:nn { cooking-units } { #1 .choices:nn = {#2} { \@@_change_unit_from_to:nVn {#1} \l_keys_choice_tl {#3} } , #1 / unknown .code:n= { \seq_set_split:Nnn \l_tmpa_seq { , } {#2} \msg_error:nnxxx { cooking-units } { key-choice-unknown } {#1} {##1} { \@@_seq_use_msg:N \l_tmpa_seq } } , #1 .default:n = {#1} , #1 .groups:n = { change-unit } } } \cs_generate_variant:Nn \@@_rf_cukeys_define_key:nnn { nVn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_change_unit_from_to:nnn } % Our main \enquote{get the conversion value} command. % |#1| is the current unit, |#2| is the unit it is converted into, % |#3| is the \enquote{base unit}, given in order to make some shortcuts. % % |\l_@@_change_unit_prop| stores which unit should be converted into which by % a key-value list. If the unit is later used we restore the value from this property list. % % Now to the calculation. |\g_@@_master_changer_prop| stores the the conversion values % globally in a | ~->~ | way. If we already have a conversion % from |#1| to |#2| we simply restore this value and store it in |l_@@_unit_change_ #1_tl|. % % If the unit-change is requested for the first time we need to calculate the % conversion value first. Afterwards, store it (globally) into |\g_@@_master_changer_prop| % to not recalculate it in the future. % \begin{macrocode} \cs_new:Npn \@@_change_unit_from_to:nnn #1#2#3 { \prop_put:Nnn \l_@@_change_unit_prop {#1} {#2} \prop_get:NncF \g_@@_master_changer_prop { #1 ~->~ #2 } { l_@@_unit_change_ #1_tl } { \@@_cukeys_calculate_change_value:nnn {#1} {#2} {#3} \prop_gput:Nnv \g_@@_master_changer_prop { #1 ~->~ #2 } { l_@@_unit_change_ #1 _tl } } } \cs_generate_variant:Nn \@@_change_unit_from_to:nnn { nVn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_cukeys_calculate_change_value:nnn } % Great, an unit-change is requested for the first time, thus calculate % the conversion value. % % First we check if the old unit |#1| is the same as the \enquote{base unit} % |#3|. This is a nice shortcut, as the property list is stored with |#3| as a base, % so direct retrival of the value is possible in this case. % % If |#1| and |#3| are different, we need to do some work. First, get the list % of key-values |l_@@_rf_cukeys_ #3 _prop| and store it in |\l_@@_tmpa_prop|. % Now eval. % % Having unit |a| and wanting unit |b|, we need the value to go from |a| to |b| % which is done by the realtion $|b|/|a|$. This is checked easily as $|a|*|b|/|a|=|b|$. % Replace this by |#1| and |#2| and we get: |#2|=|#1|*|#2|/|#1|, which we % evaluate and store in |l_@@_unit_change_ #1 _tl|. % \begin{macrocode} \cs_new:Npn \@@_cukeys_calculate_change_value:nnn #1 #2 #3 { \str_if_eq:nnTF {#1} {#3} { \prop_get:cnc { l_@@_rf_cukeys_ #1 _prop } {#2} { l_@@_unit_change_ #1 _tl } }{ \prop_set_eq:Nc \l_@@_tmpa_prop { l_@@_rf_cukeys_ #3 _prop } \@@_tl_set_eval:cn { l_@@_unit_change_ #1 _tl } { ( \prop_item:Nn \l_@@_tmpa_prop {#2} ) / ( \prop_item:Nn \l_@@_tmpa_prop {#1} ) } } } \cs_generate_variant:Nn \@@_cukeys_calculate_change_value:nnn { VVV } % \end{macrocode} % \end{macro} % % % % % % % % % % % % % % % % % \section {Creating New Units} % % %\begin{macro}{ \declarecookingunit , \newcookingunit , \providecookingunit } % Defining new units. Defining an unit is split into two parts: defining % the necassary tl's and clist's and seq's; and setting them. Depending on which % command is used, things are handled differently. % % We always make sure that the unit |#1| is first converted into a string. % \begin{macrocode} \NewDocumentCommand \declarecookingunit { o m } { \@@_if_unit_defined:nNTF {#2} \l_@@_original_unit_tl { \msg_info:nnx { cooking-units } { redefine-unit } { \l_@@_original_unit_tl } } { \@@_new_cooking_unit:V \l_@@_original_unit_tl } \@@_set_cooking_unit:nnV {#1} {#2} \l_@@_original_unit_tl } \NewDocumentCommand \newcookingunit { o m } { \@@_if_unit_defined:nNTF {#2} \l_@@_original_unit_tl { \msg_error:nnV { cooking-units } { unit-already-defined } \l_@@_original_unit_tl } { \@@_new_cooking_unit:V \l_@@_original_unit_tl \@@_set_cooking_unit:nnV {#1} {#2} \l_@@_original_unit_tl } } \NewDocumentCommand \providecookingunit { o m } { \@@_if_unit_defined:nNT {#2} \l_@@_original_unit_tl { \@@_new_cooking_unit:V \l_@@_original_unit_tl \@@_set_cooking_unit:nnV {#1} {#2} \l_@@_original_unit_tl } } % \end{macrocode} %\end{macro} % %\begin{macro}{ \@@_new_cooking_unit:n } % Creates the token and other lists for the unit |#1| (which at this point is a string). % Nothing really expectional here. % \begin{macrocode} \cs_new:Npn \@@_new_cooking_unit:n #1 { \tl_map_inline:nn {#1} { \tl_if_in:NnT \l_@@_unit_letter_not_allowed_tl {##1} { \msg_error:nnn { cooking-units } { character-not-allowed } {#1} } } \seq_gput_right:Nn \g_@@_list_of_defined_units_seq {#1} \tl_new:c { l_@@_unit_change_ #1 _tl } \tl_new:c { l_@@_base_key_unit_ #1 _tl } \tl_new:c { l_@@_default_unit_ #1 _tl } \clist_new:c { l_@@_unit_option_ #1 _clist } \tl_set:cn { l_@@_unit_change_ #1 _tl } { 1 } \keys_define:nn { cooking-units } { set-unit-hook-#1 .code:n = { \@@_set_options_for:nn {#1} {##1} } , add-unit-hook-#1 .code:n = { \@@_add_options_for:nn {#1} {##1} }, set-option-for-#1 .meta:n = { set-unit-hook-#1 = {##1} } , add-option-for-#1 .meta:n = { add-unit-hook-#1 = {##1} } , } } \cs_generate_variant:Nn \@@_new_cooking_unit:n { V } % \end{macrocode} % \end{macro} % %\begin{macro}{ \@@_set_cooking_unit:nnn , \@@_set_cooking_unit:nnV } % Here we set the in the last step defined lists to their respective value. % |#3| is the unit-string; |#2| is the unit given by the user (as it can be a command % like |/%]; |#1| is either the wanted unit-symbol \emph{or} a key-val % list. Depending on that, we either set the printed symbol to |#1| or parse the % key-val list. % % This is done in a three step procedure (if |#1| is a value): First, check if % the head of |#1| is a group. This happens if the user embraces |#1| in two % curly brace pairs (e.g.\ |{{Test}}|). This is only needed if the user really wants % the symbol to contain |=|. % % If |#1| is not protected by braces it is parsed as key(s) % \begin{macrocode} \cs_new:Npn \@@_set_cooking_unit:nnn #1#2#3 { \@@_set_cooking_unit_base_translation:n {#3} \bool_set_false:N \l_@@_symbol_was_set_by_key_bool \bool_set_false:N \l_@@_contains_valid_key_bool \IfNoValueF {#1} { \tl_if_head_is_group:nF {#1} { \keys_set:nn { cooking-units / definition } {#1} } } \bool_if:NF \l_@@_symbol_was_set_by_key_bool { \IfNoValueTF {#1} { \tl_set:cn { l_@@_default_unit_ #3 _tl } {#2} } { \bool_if:NTF \l_@@_contains_valid_key_bool { \tl_set:cn { l_@@_default_unit_ #3 _tl } {#2} } { \tl_set:cn { l_@@_default_unit_ #3 _tl } {#1} } } } \@@_culang_def_base:nnv { unit-symbol } {#3} { l_@@_default_unit_ #3 _tl } } \cs_generate_variant:Nn \@@_set_cooking_unit:nnn { nnV } % \end{macrocode} % \end{macro} % %\begin{macro}{ \@@_set_cooking_unit_base_translation:n } % Just an auxilary command. Sets the base translations to their default value. % \begin{macrocode} \cs_new:Npn \@@_set_cooking_unit_base_translation:n #1 { \@@_culang_def_base:nnn { name } {#1} \c_@@_no_translation_str \@@_culang_def_base:nnn { name-pl } {#1} \c_@@_no_translation_str \@@_culang_def_base:nnn { gender } {#1} { m } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\declarecookingderivatives} % Now this is an experimental command. The idea is simple: Combine units % (like meter and second giving meter per second or in any way possible). % The first argument contains a list of the units that should be connected, one of every type is enough. % Second is the unit-key, containing |#1|, \dots\ |#(number of units in first argument)|. % Third is the calculation. The formula for the new unit should be written in here. % And last, but not least: The last argument contains the unit-symbol. % % Now this command simply defines a bunch of units, declaring them makes things easier. % \begin{macrocode} \NewDocumentCommand \declarecookingderivatives { m m m m } { \@@_declare_cooking_derivative:nnnn {#1} {#2} {#3} {#4} } % \end{macrocode} % \end{macro} % % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % This command has the following parts: % \begin{enumerate} % \item Get list of units in |#1|, check if they are defined, % count their number and store in |\l_@@_nr_of_units_int| % \item Use |\cs_generate_from_arg_count:NNnn| to create the commands % that store the final parsed unit-keys, unit-symbols und unit-formulas inside. % \item Prepare for the following loop. % \item The main part: Do the loop. % \item Declare keys and store their formula relation into a property-list. % \item Clean up. % \end{enumerate} % \begin{macrocode} \cs_new:Npn \@@_declare_cooking_derivative:nnnn #1#2#3#4 { \seq_clear:N \l_@@_list_of_units_seq \@@_set_state_to:n { normal } \clist_map_inline:nn {#1} { \@@_if_unit_defined:nNTF {##1} \l_@@_tmpa_tl { \@@_if_unit_has_a_key:VTF \l_@@_tmpa_tl { \seq_put_right:NV \l_@@_list_of_units_seq \l_@@_tmpa_tl } { \tl_show:n { Error-no-key-defined } {##1} \@@_set_state_to:n { error } } } { \@@_set_state_to:n { error } } } \@@_if_state_equal:nT { error } { \use_none_delimit_by_q_recursion_stop:w } \int_set:Nn \l_@@_nr_of_units_int { \seq_count:N \l_@@_list_of_units_seq } % \end{macrocode} % Now we generate the functions. The first takes the unit-keys and plugs it into % the formula |#2| (unit-key) and |#4| (unit-symbol). % The second function get's the nummerical values and stores it in a sequcence. % Example: \\ % Assuming |kg|, |m| and |s| with unit key |#1*#2-#3|, formula |#1*#2/#3| % and symbol |#1\,\sfrac{#2}{#3}|. Looping through the units, the first % function creates |kg*m-s|, |kg*m-min|, \dots, |oz*in-h|; and |kg\,\sfrac{m}{s}|, % \dots, |oz\,\sfrac{in}{h}|. % % The second function creates their nummerical relation: for |kg|, |m| and |s| % this would be |1*1/86400|. |kg|, |in|, |s| would be |1*(36/0.9144)/86400|. % If we then transform the first ones |kg*m-s| into the second one |kg*in-s| % we will later calculate $ \frac{1*(36/0.9144)/86400}{1*1/86400} $ which will get % us the correct transformation. May explanation my not be very good, % but it makes sense (later). % \begin{macrocode} \cs_generate_from_arg_count:NNnn \@@_create_unit:w \cs_set:Npn { \l_@@_nr_of_units_int } { \seq_gpush:Nn \g_@@_stored_derived_unit_symbol_seq {#2} \seq_gpush:Nn \g_@@_stored_derived_unit_print_seq {#4} } \cs_generate_from_arg_count:NNnn \@@_create_formula:w \cs_set:Npn { \l_@@_nr_of_units_int } { \seq_gpush:Nn \g_@@_stored_derived_formula_seq {#3} } % \end{macrocode} % Preparing and startidoingng the loop. % \begin{macrocode} \@@_prepare_sequence_loop: \@@_step_loop_start:n { } % \end{macrocode} % Now we create new cooking-units with the unit-keys and unit-symbols % we created \emph{via} loop. As we need a \enquote{master unit} we use % the most right unit in the derived unit list. This is because the most right one % is created by the other base units. % \begin{macrocode} \seq_get_right:NN \g_@@_stored_derived_unit_symbol_seq \l_@@_curr_unit_tl \seq_map_inline:Nn \g_@@_stored_derived_unit_symbol_seq { \seq_pop:NN \g_@@_stored_derived_formula_seq \l_@@_tmpb_tl \prop_put:NnV \l_@@_derived_units_prop {##1} \l_@@_tmpb_tl % \end{macrocode} % First create the unit, \emph{then} set the reference to the base unit. % (Else it is not yet defined) % \begin{macrocode} \seq_pop:NN \g_@@_stored_derived_unit_print_seq \l_@@_tmpa_tl \exp_args:NNx \declarecookingunit [ {symbol={\l_@@_tmpa_tl}} ] {##1} \tl_set_eq:cN { l_@@_base_key_unit_ ##1 _tl } \l_@@_curr_unit_tl } % \end{macrocode} % Clean-up. Do not know if this is necessary, but eh. % \begin{macrocode} \seq_gclear:N \g_@@_stored_derived_unit_symbol_seq \seq_gclear:N \g_@@_stored_derived_unit_print_seq \seq_gclear:N \g_@@_stored_derived_formula_seq \cs_undefine:N \@@_create_unit:w \cs_undefine:N \@@_create_formula:w % \end{macrocode} % If we had an error beforehand, we would jump to this place ignoring everything % inbetween. % \begin{macrocode} \use_none_delimit_by_q_recursion_stop:w \q_recursion_stop } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % \begin{macrocode} \cs_new:Npn \@@_prepare_sequence_loop: { \seq_map_indexed_inline:Nn \l_@@_list_of_units_seq { \prop_clear_new:c { l_@@_keys_ ##1 _prop } \seq_clear_new:c { l_@@_list_ ##1 _seq } \tl_set_eq:Nc \l_@@_curr_unit_tl { l_@@_base_key_unit_ ##2 _tl } \prop_set_eq:cc { l_@@_keys_ ##1 _prop } { l_@@_rf_cukeys_ \l_@@_curr_unit_tl _prop } \prop_get:cnc { l_@@_keys_ ##1 _prop } { Liste } { l_@@_list_ ##1 _seq } } } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % \begin{macrocode} \cs_new:Npn \@@_step_loop_start:n #1 { \tl_clear:N \l_@@_stored_units_tl \tl_clear:N \l_@@_stored_formula_tl \int_set:Nn \l_@@_depth_int { 1 } \seq_map_function:cN { l_@@_list_ \int_use:N \l_@@_depth_int _seq } \@@_seq_recursive_loop_function:n } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % \begin{macrocode} \cs_new:Npn \@@_seq_recursive_loop_function:n #1 { \group_begin: \tl_put_right:Nn \l_@@_stored_units_tl { {#1} } \tl_set_eq:Nc \l_@@_original_unit_tl { l_@@_base_key_unit_ #1 _tl } \tl_put_right:Nx \l_@@_stored_formula_tl { { ( \exp_not:N \prop_item:Nn \exp_not:c { l_@@_rf_cukeys_ \l_@@_original_unit_tl _prop } {#1} ) } } \int_compare:nNnTF { \l_@@_depth_int } = { \l_@@_nr_of_units_int } { \exp_after:wN \@@_create_unit:w \l_@@_stored_units_tl \exp_after:wN \@@_create_formula:w \l_@@_stored_formula_tl }{ \int_incr:N \l_@@_depth_int \seq_map_function:cN { l_@@_list_ \int_use:N \l_@@_depth_int _seq } \@@_seq_recursive_loop_function:n } \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % \begin{macrocode} \keys_define:nn { cooking-units } { unknown .code:n = { \@@_if_key_ais_derived_unit:VTF \l_keys_key_str { \@@_convert_derived_unit:Vn \l_keys_key_str {#1} } { \msg_error:nnxx { cooking-units } { key-unknown } { \l_keys_path_str } { cooking-units } } } } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % \begin{macrocode} \prg_new_conditional:Npnn \@@_if_key_ais_derived_unit:n #1 { TF } { \tl_if_exist:cTF { l_@@_base_key_unit_ #1 _tl } { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \@@_if_key_ais_derived_unit:nTF { V } % \end{macrocode} % \end{macro} % % \begin{macro} { \@@_declare_cooking_derivative:nnnn } % The idea is the following. The relation between the units are given as % |(#1)/(#2)| (e.g.). The idea is the following: % \begin{enumerate} % \item The derivative contains two units. % \item The mathematical relationship is given by |(#1)/(#2)| % \item Define |\@@_tmp:w| to be |(#1)/(#2)|. % \item Parse through all values and store them in a list (e.g. |\l_tmpa_t|) as | {value-1}{value-2} | % \item Use |\exp_after:wN \@@_tmp:w \l_tmpa_t | to expand to |\@@_tmp:w {value-1} {value-2} | % \item Let |\@@_tmp:w| do its job. % \end{enumerate} % \begin{macrocode} \cs_new:Npn \@@_convert_derived_unit:nn #1#2 { \tl_if_eq:ccTF { l_@@_base_key_unit_ #1 _tl } { l_@@_base_key_unit_ #2 _tl } { \prop_get:NnN \l_@@_derived_units_prop {#1} \l_@@_tmpa_tl \prop_get:NnN \l_@@_derived_units_prop {#2} \l_@@_tmpb_tl \prop_put:Nnn \l_@@_change_unit_prop {#1} {#2} \@@_tl_set_eval:cn { l_@@_unit_change_ #1 _tl } { (\l_@@_tmpb_tl) / (\l_@@_tmpa_tl) } \prop_gput:Nnv \g_@@_master_changer_prop { #1 ~->~ #2 } { l_@@_unit_change_ #1 _tl } }{ \msg_error:nnnn { cooking-units } { key-no-knows-value } {#1} {#2} } } \cs_generate_variant:Nn \@@_convert_derived_unit:nn { V } % \end{macrocode} % \end{macro} % % % % % % % % \section {Names} % % Stolen from \pkg{l3keys.dtx} % \begin{macrocode} \tl_new:N \l_@@_sanitise_tl \cs_new_protected:Npn \@@_sanitise_aux:w #1 \q_mark { \tl_set:Nn \l_@@_sanitise_tl {#1} } \group_begin: \char_set_catcode_active:n { `\< } \char_set_catcode_active:n { `\> } \cs_new:Npn \@@_sanitize_open_arrow: { \exp_after:wN \@@_sanitize_open_arrow_auxi:w \l_@@_sanitise_tl \q_mark < \q_nil < \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl } \cs_new_protected:Npn \@@_sanitize_open_arrow_auxi:w #1 < { \tl_set:Nn \l_@@_sanitise_tl {#1} \@@_sanitize_open_arrow_auxii:w } \cs_new_protected:Npn \@@_sanitize_open_arrow_auxii:w #1 < { \quark_if_nil:nF {#1} { \tl_set:Nx \l_@@_sanitise_tl { \exp_not:V \l_@@_sanitise_tl \token_to_str:N < \exp_not:n {#1} } \exp_after:wN \@@_sanitize_open_arrow_auxii:w } } \cs_new:Npn \@@_sanitize_close_arrow: { \exp_after:wN \@@_sanitize_close_arrow_auxi:w \l_@@_sanitise_tl \q_mark > \q_nil > \exp_after:wN \@@_sanitise_aux:w \l_@@_sanitise_tl } \cs_new_protected:Npn \@@_sanitize_close_arrow_auxi:w #1 > { \tl_set:Nn \l_@@_sanitise_tl {#1} \@@_sanitize_close_arrow_auxii:w } \cs_new_protected:Npn \@@_sanitize_close_arrow_auxii:w #1 > { \quark_if_nil:nF {#1} { \tl_set:Nx \l_@@_sanitise_tl { \exp_not:V \l_@@_sanitise_tl \token_to_str:N > \exp_not:n {#1} } \exp_after:wN \@@_sanitize_close_arrow_auxii:w } } \group_end: \cs_new_protected:Npn \@@_sanitize_arrows:n #1 { \tl_set:Nn \l_@@_sanitise_tl {#1} \@@_sanitize_open_arrow: \@@_sanitize_close_arrow: } % \end{macrocode} % % % \begin{macro}{\cudefinename} % This command allows to (re)define \enquote{names} for units to be used % in \cCutext\ for given language. We need to sanitize the arrows (|<| and |>|) % in case they are active. Afterwards, parse through the list. % \begin{macrocode} \NewDocumentCommand \cudefinename { m m } { \tl_set:Nn \l_@@_language_tl {#1} \@@_sanitize_arrows:n {#2} \exp_after:wN \@@_parse_definename_start_cycle: \l_@@_sanitise_tl \q_recursion_tail \q_recursion_tail \q_recursion_stop } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_parse_definename_start_cycle:} % Initializes the parse cycle. Set state to \enquote{normal} and clear some variables. % Start by grabbing the first argument which is the unit. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_start_cycle: { \@@_set_state_to:n { normal } \tl_clear:N \l_@@_curr_unit_tl \prop_clear:N \l_@@_tmpa_prop \bool_set_false:N \l_@@_tmpa_bool \@@_parse_definename_unit:n } % \end{macrocode} % \end{macro} % \begin{macro}{ \@@_parse_definename_unit:n } % Grab the unit. This can be either an actual unit (defined by |\newcookingunit| and Co.) % or it is one of the allowed \enquote{special-keys} like \enquote{decimal-marker}, % etc. In case neither of those two is found, issue an error, but continue parsing % to catch more errors. % % The next optional argument is the unit-symbol, which can also be set by this % command. If it is not found, grab the next mandatory argument, which is the % unit-name. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_unit:n #1 { \quark_if_recursion_tail_stop:n {#1} \seq_if_in:NnTF \g_@@_allowed_special_keys_seq {#1} { \str_if_eq:nnTF { cutext-range-sign } {#1} { \@@_set_unit:Nn \l_@@_curr_unit_tl { cutext-range-phrase } } { \@@_set_unit:Nn \l_@@_curr_unit_tl {#1} } \bool_set_true:N \l_@@_tmpa_bool }{ \@@_error_if_unit_not_defined:nNT {#1} \l_@@_curr_unit_tl { \@@_set_state_to:n { error } } } \peek_remove_spaces:n { \peek_meaning:NTF [ { \@@_parse_definename_optional_unitsymbol:w } { \@@_parse_definename_singular:n } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_definename_optional_unitsymbol:w } % The frst optional argument has be found; it is thus the unit-symbol. % Changing the unit-symbol is only allowed for actual units, so no \enquote{special-keys} % allowed. If we have an unit, store it into |\l_@@_tmpa_prop|. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_optional_unitsymbol:w [#1] { \bool_if:NTF \l_@@_tmpa_bool { \@@_set_state_to:n { error } \msg_error:nnxn { cooking-units } { symbol-and-special-keys-error } \l_@@_curr_unit_tl {#1} } { \prop_put:Nnn \l_@@_tmpa_prop { unit-symbol } {#1} } \@@_parse_definename_singular:n } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_definename_singular:n } % Parses the \enquote{name} of the unit (or special key). % Do an error check, store the input in the property list and go to the % next one, which is the plural form. The next (optional) argument is % the plural form of the \enquote{name}. If there is none, also use |#1| for plural. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_singular:n #1 { \quark_if_recursion_tail_stop_do:nn {#1} { \msg_error:nn { cooking-units } { missing-argument } } \prop_put:Nnn \l_@@_tmpa_prop { name } {#1} \peek_remove_spaces:n { \peek_meaning:NTF [ { \@@_parse_definename_optional_plural:w } { \@@_parse_definename_optional_plural:w [#1] } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_definename_optional_plural:w } % Nothing new here. The next one is the gender. If no gender is found % I assume it to be male \enquote{m}. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_optional_plural:w [#1] { \prop_put:Nnn \l_@@_tmpa_prop { name-pl } {#1} \peek_remove_spaces:n { \peek_meaning:NTF < { \@@_parse_definename_optional_gender:w } { \@@_parse_definename_optional_gender:w } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_definename_optional_plural:w } % Check if the gender is correct. Then store it in the property-list % and finish the cycle. % \begin{macrocode} \cs_new:Npn \@@_parse_definename_optional_gender:w <#1> { \@@_if_gender_error:nT {#1} { \@@_set_state_to:n { error } } \prop_put:Nnn \l_@@_tmpa_prop { gender } {#1} \@@_parse_name_finish_cycle_and_start_new:VV \l_@@_language_tl \l_@@_curr_unit_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_name_finish_cycle_and_start_new:nn } % Now it makes sense why we used property list before. % Each unit has some basic definitions for each language (see |\newcookingunit| and Co.): % \enquote{unit-symbol}, \enquote{name}, \enquote{name\_pl} and \enquote{gender}. % % So going through the property list (where we stored the given values) % we set for the given language and unit those definitions (\enquote{unit-symbol} only % if found). % % Afterwards, start the next cycle. % \begin{macrocode} \cs_new:Npn \@@_parse_name_finish_cycle_and_start_new:nn #1#2 { \@@_if_state_equal:nT { normal } { \prop_map_inline:Nn \l_@@_tmpa_prop { \@@_culang_def_for:nnnn {#1} {##1} {#2} {##2} } } \@@_parse_definename_start_cycle: } \cs_generate_variant:Nn \@@_parse_name_finish_cycle_and_start_new:nn { VV } % \end{macrocode} % \end{macro} % % % \subsection {Define plural-usage for \cCutext} % % \begin{macro}{ pluraltest: normal/range/fraction } % Some translations used in a second. Define one for \enquote{normal} % (just a number), \enquote{range} and \enquote{fraction}. % \begin{macrocode} \@@_culang_def_base:nnn { pluraltest } { normal } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { pluraltest } { fraction } { \c_@@_no_translation_str } % \end{macrocode} % \end{macro} % \begin{macro}{ @@_pluraltest_normal_:N, % @@_pluraltest_range_:NN, % @@_pluraltest_fraction_:NNN } % Here we define the default behavior, if no explicit rule is given for a language. % By default, use the singular only if the value in question is equal to $1$. % For ranges take the last value, for fractions make sure there are no mixed fractions % and that the nominator is equal to $1$. % \begin{macrocode} \cs_set:cpn { @@_pluraltest_normal_ \c_@@_no_translation_str :n } #1 { \fp_compare:nNnTF {#1} = { 1.0 } { \bool_set_true:N } { \bool_set_false:N } \l_@@_cutext_use_single_bool } \cs_set:cpn { @@_pluraltest_fraction_ \c_@@_no_translation_str :nnn } #1#2#3 { \tl_if_empty:nTF {#1} { \fp_compare:nNnTF {#2} = { \c_one_fp } } { \use_ii:nn } { \bool_set_true:N } { \bool_set_false:N } \l_@@_cutext_use_single_bool } % \end{macrocode} % \end{macro} % \begin{macro}{ \@@_pluraltest_define:nn , \@@_pluraltest_define_range:nn , \@@_pluraltest_define_fraction:nn } % Allow to define a when to use the singular/plural for a given language. % Nothing more happens here. The default was already defined in the last step. % \begin{macrocode} \cs_new:Npn \@@_pluraltest_define:nn #1#2 { \@@_culang_def_for:nnnn {#1} { pluraltest } { normal } {#1} \cs_set:cpn { @@_pluraltest_normal_ #1 :n } ##1 {#2} } \cs_new:Npn \@@_pluraltest_define_fraction:nn #1#2 { \@@_culang_def_for:nnnn {#1} { pluraltest } { fraction } {#1} \cs_set:cpn { @@_pluraltest_fraction_ #1 :nnn } ##1##2##3 {#2} } % \end{macrocode} % \end{macro} % \begin{macro}{ Pluraltest: French } % As source see % \begin{itemize} % \item \url{https://github.com/Vidabe/cooking-units/pull/29#issue-1190629683} % \item \url{https://www.dictionnaire-academie.fr/article/A9P2979} % \end{itemize} % Note: At this point the minus signs were already removed from fractions; thus % all their values are positive. % \begin{macrocode} \@@_pluraltest_define:nn { French } { \fp_compare:nNnTF { \fp_abs:n {#1} } < { 2.0 } { \bool_set_true:N } { \bool_set_false:N } \l_@@_cutext_use_single_bool } \@@_pluraltest_define_fraction:nn { French } { \tl_if_empty:nTF {#1} { \fp_compare:nNnTF { #2/#3 } < { 2.0 } } { \fp_compare:nNnTF { #1 + #2/#3 } < { 2.0 } } { \bool_set_true:N } { \bool_set_false:N } \l_@@_cutext_use_single_bool } % \end{macrocode} % \end{macro} % % % \subsection {cudefinesymbol} % % \begin{macro}{ \cudefinesymbol } % A small and rather straightforward command. % No special sanitizing here, just setting the language and parsing. % \begin{macrocode} \NewDocumentCommand \cudefinesymbol { m m } { \tl_set:Nn \l_@@_language_tl {#1} \@@_cuprint_define_printed_unit:nn #2 \q_recursion_tail \q_recursion_tail \q_recursion_stop } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_cuprint_define_printed_unit:nn } % Sets the symbol for given units or \enquote{special keys}. % Nothing extraordinary is done here. We we have an unit we % set its \enquote{unit-symbol} for given language, for an \enquote{special key} % we set its name. Rinse and repeat. % \begin{macrocode} \cs_new:Npn \@@_cuprint_define_printed_unit:nn #1#2 { \quark_if_recursion_tail_stop:n {#1} \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nn { cooking-units } { missing-argument } } \seq_if_in:NnTF \g_@@_allowed_special_keys_seq {#1} { \str_if_eq:nnTF { cutext-range-sign } {#1} { \@@_culang_def_for:Vnnn \l_@@_language_tl { name } { cutext-range-phrase } {#2} } { \@@_culang_def_for:Vnnn \l_@@_language_tl { name } {#1} {#2} } }{ \@@_error_if_unit_not_defined:nTF {#1} { \use_none:nn {#1} {#2} } { \@@_culang_def_for:Vnnn \l_@@_language_tl { unit-symbol } {#1} {#2} } } \@@_cuprint_define_printed_unit:nn } % \end{macrocode} % \end{macro} % % % \subsection {Phrases} % % % % \begin{macro}{ phrase } % Defining phrases. Phrases (sadly) depend on the language. % The idea is going to be: Each language has its own property list % which is going to be stored for each language. Later, the property % list is recoverd for the language currently used. If no phrase is defined % the fallback |\c_@@_no_translation_str| is returned. % \begin{macrocode} \@@_culang_def_base:nnn { phrase } { phrase-prop } { \c_@@_no_translation_str } \@@_culang_def_base:nnn { phrase } { phrase-lang } { Humphry-Drumphy } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_culang_get_phrase_prop:N } % \begin{macrocode} \cs_new:Npn \@@_culang_get_phrase_prop:N #1 { \@@_culang_let:nnN { phrase } { phrase-lang } \l_@@_language_tl \prop_if_exist:cTF { l_@@_ \l_@@_language_tl _phrase_prop } { \prop_set_eq:Nc #1 { l_@@_ \l_@@_language_tl _phrase_prop } } { \tl_set_eq:NN #1 \c_@@_no_translation_str } } \cs_new:Npn \@@_culang_get_phrase_prop_for:nN #1#2 { \prop_set_eq:Nc #2 { l_@@_ #1 _phrase_prop } % \prop_if_exist:cT { l_@@_ #1 _phrase_prop } % { \prop_set_eq:Nc \l_@@_phrase_prop { l_@@_ #1 _phrase_prop } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \cudefinephrase } % Defining some phrases. This command is just a wrapper for the % subcommand. % \begin{macrocode} \tl_new:N \l_@@_phrase_tmp_tl \tl_new:N \l_@@_phrase_tmpb_tl \seq_new:N \l_@@_phrase_full_seq \NewDocumentCommand \cudefinephrase { m m } { \@@_rf_cuphrase_add:nn {#1} {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_cuphrase_add:nn } % |#1| is the language, |#2| is something to be parsed. % I am also not quite happy with this construction, but it works. % % First, check if we already have existing phrases for the language. % If so, recover it, we add the parsed one to them. Else, clear % some variables. % % Next step, sanitize |<| and |>|. I hate this so much. % % Using |\@@_parse_phrase:V| we parse through the argument |#2|. % The results are added to |\l_@@_phrase_prop| and |\l_@@_phrase_numbers_seq|. % % By our rules, we prefer big numbers over small ones. For example, % 120 can be translated into \enquote{2 Schocks} ($2\times60$) or % \enquote{10 Dutzend} ($10\times12$). We prefer the bigger number first, % so we will always get \enquote{2 Schocks}. Thus we sort the numbers in the % sequence from highest to lowest. We also have negative numbers (phrases only % used for one specific number). We prefer the negative over the positive ones % if possible. % % Then store the variables and thats it. % \begin{macrocode} \cs_new:Npn \@@_rf_cuphrase_add:nn #1#2 { \tl_set:Nn \l_@@_language_tl {#1} \@@_culang_if_translation_exists_for:VnnTF \l_@@_language_tl { phrase } { phrase-lang } { \prop_set_eq:Nc \l_@@_phrase_prop { l_@@_ \l_@@_language_tl _phrase_prop } \prop_get:NnN \l_@@_phrase_prop { seq } \l_@@_phrase_numbers_seq }{ \prop_new:c { l_@@_ \l_@@_language_tl _phrase_prop } \prop_clear:N \l_@@_phrase_prop \seq_clear:N \l_@@_phrase_numbers_seq \seq_clear:N \l_@@_phrase_full_seq } \@@_sanitize_arrows:n {#2} \@@_parse_phrase:V \l_@@_sanitise_tl \@@_rf_cuphrase_sort_sequence:N \l_@@_phrase_numbers_seq \prop_put:NnV \l_@@_phrase_prop { seq } \l_@@_phrase_numbers_seq \prop_set_eq:cN { l_@@_ \l_@@_language_tl _phrase_prop } \l_@@_phrase_prop \@@_culang_def_for:VnnV \l_@@_language_tl { phrase } { phrase-lang } \l_@@_language_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_phrase:n } % Wrapper around the parsing cycles. Nothing new. % \begin{macrocode} \cs_new:Npn \@@_parse_phrase:n #1 { \@@_parse_phrase_start_cycle: #1 \q_recursion_tail \q_recursion_tail \q_recursion_stop } \cs_generate_variant:Nn \@@_parse_phrase:n { V } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_phrase_start_cycle: } % Starts the parsing cycle. Clear some variables, set state to normal. % \begin{macrocode} \cs_new:Npn \@@_parse_phrase_start_cycle: { \@@_set_state_to:n { normal } \bool_set_false:N \l_@@_tmpa_bool \tl_clear:N \l_@@_curr_unit_tl \tl_clear:N \l_@@_phrase_tmp_tl \tl_clear:N \l_@@_phrase_tmpb_tl \prop_clear:N \l_@@_tmpa_prop \@@_parse_stuff_mandatories_plus_star:nn } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_stuff_mandatories_plus_star:nn } % Now. We have two cases: two mandatroy arguments following each other % (number plus phrase) \emph{or} number plus star plus phrase (so three arguments). % In order to not need explicetly test for the star I do the following: % Just grab the first two things. |#1| is always the number. |#2| can either be % the star |*| \emph{or} a phrase. If it is a star, set a boolean, and restart the command % with |#1| already given. % % Then check if |#2| is a number (with a primitve test). Star-numbers are stored % negatively. This makes defining a property list easier for me. % % Note that the key in the first |\l_@@_tmpa_prop| is empty. This is done % in order to not screw up the later |\prop_map_inline| loop. We also store % if the phrase is only used for one number (aka.\ if there is a star). % % Then, parse the optionals. % \begin{macrocode} \cs_new:Npn \@@_parse_stuff_mandatories_plus_star:nn #1#2 { \quark_if_recursion_tail_stop:n {#1} \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nn { cooking-units } { missing-argument } } \str_if_eq:nnTF {#2} { * } { \bool_set_true:N \l_@@_tmpa_bool \@@_parse_stuff_mandatories_plus_star:nn {#1} }{ \@@_if_evaluation_is_integer:nTF {#1} { \int_set:Nn \l_@@_tmpa_int {#1} \tl_set:NV \l_@@_curr_unit_tl \l_@@_tmpa_int \tl_put_right:Nx \l_@@_phrase_tmpb_tl { { \l_@@_curr_unit_tl } } }{ \@@_set_state_to:n { error } \msg_error:nnn { cooking-units} { phrase-unit-not-an-integer } {#1} } \bool_if:NTF \l_@@_tmpa_bool { \tl_put_right:Nn \l_@@_phrase_tmpb_tl { { \c_true_bool } } }{ \tl_put_right:Nn \l_@@_phrase_tmpb_tl { { \c_false_bool } } } % \prop_put:Nnn \l_@@_tmpa_prop { } {#2} \tl_put_right:Nn \l_@@_phrase_tmp_tl { {#2} } \prop_put:NnV \l_@@_tmpa_prop { - singlebool } \l_@@_tmpa_bool \@@_parse_phrase_optionals:n {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_phrase_optionals:n } % Get the plural form of the phrase. If the next token is an open bracket, % get the plural form, else set the plural form equal to the singular form. % \begin{macrocode} \cs_new:Npn \@@_parse_phrase_optionals:n #1 { \peek_remove_spaces:n { \peek_meaning:NTF [ { \@@_parse_stuff_plural:w } { \@@_parse_stuff_plural:w [#1] } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_stuff_plural:w } % Store the pluarl form in the property list. % Next, parse gender. Pretty much the same as before. By default we use % a male gender. % \begin{macrocode} \cs_new:Npn \@@_parse_stuff_plural:w [#1] { \prop_put:Nnn \l_@@_tmpa_prop { -pl } {#1} \tl_put_right:Nn \l_@@_phrase_tmp_tl { {#1} } \peek_remove_spaces:n { \peek_meaning:NTF < { \@@_parse_stuff_gender:w } { \@@_parse_stuff_gender:w } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_stuff_gender:w } % Check if the gender-input is correct, add it to the property list. % Then finish the cycle and start a new one. % \begin{macrocode} \cs_new:Npn \@@_parse_stuff_gender:w <#1> { \@@_if_gender_error:nTF {#1} { \@@_set_state_to:n { error } } { \prop_put:Nnn \l_@@_tmpa_prop { -gender } {#1} \tl_put_right:Nn \l_@@_phrase_tmp_tl { {#1} } } \@@_parse_phrase_finish_cycle_and_start_anew:VVV \l_@@_curr_unit_tl \l_@@_phrase_tmp_tl \l_@@_phrase_tmpb_tl } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_parse_phrase_finish_cycle_and_start_anew:n } % If not errors were found: Add the number to the other numbers (if not already inside). % Afterwards map through the property list and set the variations for each number. % % As an example, if we had \enquote{12}, |\l_@@_phrase_prop| would then contain % the keys \enquote{12}, \enquote{12-pl}, \enquote{12-gender}, \enquote{12-singlebool}. % \begin{macrocode} \cs_new:Npn \@@_parse_phrase_finish_cycle_and_start_anew:nnn #1#2#3 { \@@_if_state_equal:nT { normal } { \seq_if_in:NnF \l_@@_phrase_numbers_seq {#3} { \seq_push:Nn \l_@@_phrase_numbers_seq {#3} } \prop_map_inline:Nn \l_@@_tmpa_prop { \prop_put:Nnn \l_@@_phrase_prop { #3 ##1 } {##2} } \prop_put:Nnn \l_@@_phrase_prop { #3 -all } {#2} } \@@_parse_phrase_start_cycle: } \cs_generate_variant:Nn \@@_parse_phrase_finish_cycle_and_start_anew:nnn { VVV } % \end{macrocode} % \end{macro} % % \begin{macro}{ \@@_rf_cuphrase_sort_sequence:N } % Sorting the sequence of numbers from highest to lowest (in absolute values). % And if two absolute values are equal, negative numbers are preferred over % positive ones. % \begin{macrocode} \cs_new:Npn \@@_rf_cuphrase_sort_sequence:N #1 { \seq_sort:Nn #1 { \int_set:Nn \l_@@_tmpa_int { \use_i:nn ##1 } \int_set:Nn \l_@@_tmpb_int { \use_i:nn ##2 } \int_compare:nNnTF { \l_@@_tmpa_int } < { \l_@@_tmpb_int } { \sort_return_swapped: } { \int_compare:nNnTF { \l_@@_tmpa_int } > { \l_@@_tmpb_int } { \sort_return_same: } { \bool_if:nTF { \use_ii:nn ##1 } { \sort_return_same: } { \sort_return_swapped: } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{ \cudefinephrase } % \begin{macrocode} %\NewDocumentCommand \cudefinephrase { m m } % { %% \@@_cuphrase:nn {#1} {#2} % } % \end{macrocode} % \end{macro} % % % \subsection{Old stuff} % % \begin{macro} { \@@_old_cutext:nnn } % \begin{macrocode} \cs_new:Npn \@@_old_cutext:nnn #1#2#3 { \tl_set_eq:NN \l_@@_option_unit_tl \l_@@_original_unit_tl \@@_old_cutext_default:nnn {#1} {#2} {#3} } % \end{macrocode} % \end{macro} % % % % \begin{macro} { \@@_old_cutext_default:nnn } % \changes {2016/06/11} {0.98a} { New command } % \begin{macrocode} \cs_new:Npn \@@_old_cutext_default:nnn #1#2#3 { \bool_if:NTF \l_@@_pckopt_draft_bool {#2} { \tl_set:Nn \l_@@_cutext_last_value_tl {#2} \tl_if_in:NVTF \l_@@_cutext_last_value_tl \l_@@_input_range_sign_tl { \tl_replace_once:NVn \l_@@_cutext_last_value_tl \l_@@_input_range_sign_tl { \q_@@_range } \@@_old_cutext_parse_range:Vn \l_@@_cutext_last_value_tl {#3} }{ \@@_old_cutext_print_input:Nn \l_@@_cutext_last_value_tl {#3} } } } % \end{macrocode} % \begin{macrocode} \cs_new_protected:Npn \@@_old_cutext_print_input:Nn #1#2 { \@@_old_cutext_if_numeral_is_int_and_parse_and_smaller_then_print_numerals:NTF #1 { \int_compare:nNnTF {#1} = { 1 } { \@@_culang_one_let:NnT \l_@@_translation_tmpa_tl {#2} { \bool_if:NT \l_@@_cutext_uppercase_word_bool { \text_uppercase:x { \tl_head:V \l_@@_translation_tmpa_tl } \tl_tail:V } } \l_@@_translation_tmpa_tl }{ \bool_if:NTF \l_@@_cutext_uppercase_word_bool { \@@_print_Numeral:V #1 } { \@@_print_numeral:V #1 } } } { \@@_print_numerical_input:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro} {\@@_old_cutext_if_numeral_is_int_and_parse_and_smaller_then_print_numerals:N} % \begin{macrocode} \prg_new_conditional:Npnn \@@_old_cutext_if_numeral_is_int_and_parse_and_smaller_then_print_numerals:N #1 { TF } { \bool_if:NTF \l_@@_lokal_numeral_bool { \@@_if_integer:VTF #1 { \int_compare:nNnTF {#1} < { \l_@@_print_numerals_below_int } { \prg_return_true: } { \prg_return_false: } }{ \prg_return_false: } }{ \prg_return_false: } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \cs_new:Npn \@@_old_cutext_parse_range:nn #1 #2 { \@@_old_cutext_parse_range_aux:nww {#2} #1 \q_stop } \cs_generate_variant:Nn \@@_old_cutext_parse_range:nn { V } \cs_new:Npn \@@_old_cutext_parse_range_aux:nww #1 #2 \q_@@_range #3 \q_stop { \tl_set:Nn \l_@@_tmpa_tl {#2} \tl_set:Nn \l_@@_cutext_last_value_tl {#3} \@@_old_cutext_print_input:Nn \l_@@_tmpa_tl {#1} \tl_use:N \l_@@_cutext_range_sign_tl \@@_old_cutext_print_input:Nn \l_@@_cutext_last_value_tl {#1} } % \end{macrocode} % % % \begin{macro}{\@@_cuam_old:n} % \begin{macrocode} \cs_new:Npn \@@_cuam_old:n #1 { \tl_set_rescan:Nnn \l_@@_tmpa_tl { \char_set_catcode_letter:N \_ % \char_set_catcode_ignore:N\ % } {#1} \bool_if:NTF \l_@@_pckopt_draft_bool { \l_@@_tmpa_tl } { \@@_cuam_old_parse:V \l_@@_tmpa_tl } } % \end{macrocode} % \end{macro} % % \begin{macrocode} \cs_new_protected:Npn \@@_cuam_old_parse:n #1 { \tl_if_in:nVTF {#1} \l_@@_input_range_sign_tl { \tl_set:Nn \l_@@_tmpa_tl {#1} \tl_replace_once:NVn \l_@@_tmpa_tl \l_@@_input_range_sign_tl { \q_@@_range } \@@_cuam_old_parse_range:V \l_@@_tmpa_tl }{ \tl_if_in:nnTF {#1} { / } { \tl_if_in:nnTF {#1} { _ } { \@@_cuam_old_parse_mixed_frac:www #1 \q_stop } { \@@_cuam_old_parse_frac:ww #1 \q_stop } }{ \tl_if_in:nnTF {#1} { _ } { \msg_error:nnn { cooking-units } { missing-slash } {#1} } { \@@_cuam_old_parse_scale:n {#1} } } } } \cs_generate_variant:Nn \@@_cuam_old_parse:n { V } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_cuam_old_parse_range:n #1 { \@@_cuam_old_parse_range_aux:ww #1 \q_nil } \cs_generate_variant:Nn \@@_cuam_old_parse_range:n { V } \cs_new:Npn \@@_cuam_old_parse_range_aux:ww #1 \q_@@_range #2 \q_nil { #1 \l_@@_cunum_range_sign_tl #2 } % \end{macrocode} % % % \begin{macrocode} \cs_new:Npn \@@_cuam_old_parse_scale:n #1 {#1} % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_cuam_old_parse_frac:ww #1/#2 \q_stop { \@@_frac:nn {#1} {#2} } % \end{macrocode} % % \begin{macrocode} \cs_new:Npn \@@_cuam_old_parse_mixed_frac:www #1_#2/#3 \q_stop { #1 \skip_horizontal:N \l_@@_mixed_frac_dim \@@_frac:nn {#2} {#3} } % \end{macrocode} % % % \subsubsection {cufrac} % % Obsolete. % \begin{macrocode} \NewDocumentCommand \cufrac { O{} m } { \msg_error:nnnn { cooking-units } { obsolete-command } { \cufrac } { \cuam } \group_begin: \tl_if_empty:nF {#1} { \keys_set:nn { cooking-units } {#1} } \@@_cufrac:n {#2} \group_end: } \cs_new:Npn \@@_cufrac:n #1 { \tl_set_rescan:Nnn \l_@@_tmpa_tl { \char_set_catcode_letter:N \_ % \char_set_catcode_ignore:N\ % } {#1} \@@_cufrac_parse:V \l_@@_tmpa_tl } \cs_new:Npn \@@_cufrac_parse:n #1 { \tl_if_in:nnTF {#1} { / } { \tl_if_in:nnTF {#1} { _ } { \@@_cufrac_parse_mixed_frac:www #1 \q_stop } { \@@_cufrac_parse_frac:ww #1 \q_stop } }{ \tl_if_in:nnTF {#1} { _ } { \msg_error:nnn { cooking-units } { missing-slash } {#1} } { \@@_cufrac_parse_scale:n {#1} } } } \cs_generate_variant:Nn \@@_cufrac_parse:n { V } \cs_new:Npn \@@_cufrac_parse_scale:n #1 {#1} \cs_new:Npn \@@_cufrac_parse_frac:ww #1/#2 \q_stop { \@@_frac:nn {#1} {#2} } \cs_new:Npn \@@_cufrac_parse_mixed_frac:www #1_#2/#3 \q_stop { #1 \skip_horizontal:N \l_@@_mixed_frac_dim \@@_frac:nn {#2} {#3} } % \end{macrocode} % % % % % % % % \subsection {cusetup} % % \begin{macrocode} \NewDocumentCommand \cusetup { m } { \keys_set:nn { cooking-units } {#1} } % \end{macrocode} % % % % \subsection{Definitions et all} % % Plase note that at his point we are still inside the Expl3 \enquote{Environment} % and that spaces are ignored! % % \begin{macrocode} \newcookingunit { kg } \newcookingunit { dag } \newcookingunit { g } \newcookingunit { oz } \newcookingunit { lb } \newcookingunit { stick } \newcookingunit [ symbol={\ensuremath{ \@@_frac:nn { eV } { c^2 } } } , natural-unit ] { eVc-2 } % \end{macrocode} % % \begin{macrocode} \newcookingunit { K } \newcookingunit [ \ensuremath{ {} ^ { \circ } } \kern-\scriptspace C ] { C } \newcookingunit [ \ensuremath{ {} ^ { \circ } } \kern-\scriptspace F ] { F } \newcookingunit [ \ensuremath{ {} ^ { \circ } } \kern-\scriptspace R\'{e} ] { Re } % \end{macrocode} % % \begin{macrocode} \newcookingunit { d } \newcookingunit { h } \newcookingunit { min } \newcookingunit { s } \newcookingunit [ symbol={\ensuremath{ \@@_frac:nn { \hbar } { eV } } }, natural-unit ] { hbareV-1 } % \end{macrocode} % % \begin{macrocode} \newcookingunit { m } \newcookingunit { cm } \newcookingunit { dm } \newcookingunit { mm } \newcookingunit { in } \newcookingunit [ symbol={ \ensuremath{ \@@_frac:nn { c\hbar } { eV } } } , natural-unit ] { chbareV-1 } % \end{macrocode} % % \begin{macrocode} \newcookingunit { l } \newcookingunit { dl } \newcookingunit { cl } \newcookingunit { ml } \newcookingunit [ symbol={ \ensuremath { \@@_frac:nn { c^3 \hbar^3 } { eV^3 } } }, natural-unit ] { (chbareV-1)3 } % \end{macrocode} % % \begin{macrocode} \newcookingunit { cal } \newcookingunit { kcal } \newcookingunit { J } \newcookingunit { kJ } \newcookingunit [ natural-unit ] { eV } % \end{macrocode} % % \begin{macrocode} \newcookingunit [ pinch ] { pn } \newcookingunit { EL } \newcookingunit { TL } \newcookingunit [ ssp. ] { ssp } %% saltspoonful \newcookingunit [ csp. ] { csp } %% coffeespoonful \newcookingunit [ dsp. ] { dsp } \newcookingunit [ Msp. ] { Msp } % \end{macrocode} % % \begin{macrocode} \DeclareLanguageAlias { AmericanEnglish } { American } % \end{macrocode} % % \begin{macrocode} \cudefinename { German } { { kg } { Kilogramm } < n > { dag } { Dekagramm } < n > { g } { Gramm } < n > { oz } { Unze } < f > { lb } { Pfund } < n > % \end{macrocode} % \begin{macrocode} { d } { Tag } [ Tage ] { h } { Stunde } [ Stunden ] < f > { min } { Minute } [ Minuten ] < f > { s } { Sekunde } [ Sekunden ] < f > % \end{macrocode} % \begin{macrocode} { C } { Grad \space Celsius } { K } { Kelvin } < n > { F } { Grad \space Fahrenheit } { Re } { Grad \space R\'{e}amur } % \end{macrocode} % \begin{macrocode} { m } { Meter } < n > { dm } { Dezimeter } < n > { cm } { Centimeter } < n > { mm } { Millimeter } < n > { in } { Zoll } % \end{macrocode} % \begin{macrocode} { l } [ l ] { Liter } { dl } { Deziliter } { cl } { Centiliter } { ml } { Milliliter } % \end{macrocode} % \begin{macrocode} { cal } { Kalorie } [ Kalorien ] < f > { kcal } { Kilokalorie } [ Kilokalorien ] < f > { J } { Joule } { kJ } { Kilojoule } { eV } { Elektronenvolt } < n > % \end{macrocode} % \begin{macrocode} { Msp } [ Msp. ] { Messerspitze } [ Messerspitzen ] < f > { pn } [ Prise ] { Prise } [ Prisen ] < f > { EL } [ EL ] { Essl{\"o}ffel } { TL } [ TL ] { Teel{\"o}ffel } { csp } [ KL ] { Mokkal{\"o}ffel } % \end{macrocode} % \begin{macrocode} { decimal-mark } { , } { one (m) } { ein } { one (f) } { eine } { one (n) } { ein } } % \end{macrocode} % % % \begin{macrocode} \cudefinename { English } { { kg } { kilogramme } { dag } { decagramme } { g } { gramme } { oz } { ounce } { lb } { pound } [ pounds ] { stick } { stick } [ sticks ] % \end{macrocode} % \begin{macrocode} { d } { day } [ days ] { h } { hour } [ hours ] { min } { minute } [ minutes ] { s } { second } [ seconds ] % \end{macrocode} % \begin{macrocode} { C } { degree \space Celsius } [ degrees \space Celsius ] { F } { degree \space Fahrenheit } [ degrees \space Fahrenheit ] { K } { kelvin } { Re } { degree \space R\'{e}aumur } [ degrees \space R\'{e}aumur ] % \end{macrocode} % \begin{macrocode} { m } { metre } [ metres ] { dm } { decimetre } [ decimetres ] { cm } { centimetre } [ centimetres ] { mm } { millimitre } [ millimitres ] { in } { inch } [ inches ] % \end{macrocode} % \begin{macrocode} { l } [ \ensuremath { \ell } ] { litre } [ litres ] { dl } { decilitre } [ decilitres ] { cl } { centilitre } [ centilitres ] { ml } { millilitre } [ millilitres ] % \end{macrocode} % \begin{macrocode} { cal } { calorie } [ calories ] { kcal } { kilocalorie } [ kilocalories ] { J } { joule } [ joules ] { kJ } { kilojoule } [ kilojoules ] { eV } { electron \space volt } % \end{macrocode} % \begin{macrocode} % { Msp } [ pinch ] { pinch } [ pinches ] % { Msp } { Messerspitze } [ Messerspitzen ] { pn } [ pinch ] { pinch } [ pinches ] { EL } [ tbsp. ] { tablespoon } [ tablespoons ] { TL } [ tsp. ] { teaspoon } [ teaspoons ] { dsp } { dessertspoonful } { csp } { coffeespoonful } { ssp } { saltspoonful } % \end{macrocode} % \begin{macrocode} { decimal-mark } { . } { one (m) } { one } { one (f) } { one } { one (n) } { one } } % \end{macrocode} % % \begin{macrocode} \cudefinename { AmericanEnglish } { { kg } { kilogram } { dag } { decagram } { g } { gram } { oz } { ounce } % \end{macrocode} % \begin{macrocode} { m } { meter } [ meters ] { dm } { decimeter } [ decimeters ] { cm } { centimeter } [ centimeters ] { mm } { millimiter } [ millimiters ] { in } { inch } [ inches ] % \end{macrocode} % \begin{macrocode} { l } [ \ensuremath { \ell } ] { liter } [ liters ] { dl } { deciliter } [ deciliters ] { cl } { centiliter } [ centiliters ] { ml } { milliliter } [ milliliters ] % \end{macrocode} % \begin{macrocode} % { Msp } { Messerspitze } [ Messerspitzen ] { pn } [ pn. ] { pinch } [ pinches ] } % \end{macrocode} % % % \begin{macrocode} \cudefinename { French } { { kg } { kilogramme } [ kilogrammes ] { dag } { d\'{e}cagramme } [ d\'{e}cagrammes] { g } { gramme } [ grammes ] { oz } { once } [ onces ] < f > { lb } { livre } [ livres ] < f > % \end{macrocode} % \begin{macrocode} { d } { jour } [ jours ] { h } { heure } [ heures ] < f > { min } { minute } [ minutes ] < f > { s } { seconde } [ secondes ] < f > % \end{macrocode} % \begin{macrocode} { C } { degr\'{e} \space Celsius } [ degr\'{e}s \space Celsius ] { K } { degr\'{e} \space Fahrenheit } [ degr\'{e}s \space Fahrenheit ] { F } { kelvin } [ kelvins ] { Re } { \'{e}chelle \space R\'{e}aumur } [ degr\'{e}s \space R\'{e}aumur ] % \end{macrocode} % \begin{macrocode} { m } { m\`{e}tre } [ m\`{e}tres ] { dm } { d\'{e}cim\`{e}tre } [ d\'{e}cim\`{e}tres ] { cm } { centim\`{e}tre } [ centim\`{e}tres ] { mm } { millim\`{e}tre } [ millim\`{e}tres ] { in } [ po ] { pouce } [ pouces ] % \end{macrocode} % \begin{macrocode} { l } [ L ] { litre } [ litres ] { dl } [ dL ] { d\'{e}cilitre } [ d\'{e}cilitres ] { cl } [ cL ] { centilitre } [ centilitres ] { ml } [ mL ] { millilitre } [ millilitres ] % \end{macrocode} % \begin{macrocode} { cal } { calorie } [ calories ] < f > { kcal } { kilocalorie } [ kilocalories ] < f > { J } { joule } [ joules ] { kJ } { kilojoule } [ kilojoules ] { eV } { \'{e}lectron-volt } [ \'{e}lectron-volts ] % \end{macrocode} % \begin{macrocode} { pn } [ pinc\'{e}e ] { pinc\'{e}e } [ pinc\'{e}es ] < f > { EL } [c.\`{a}.s.] { cuill\`{e}re \space \`{a} \space soupe } [ cuill\`{e}res \space \`{a} \space soupe ] < f > { TL } [c.\`{a}.c.] { cuill\`{e}re \space \`{a} \space caf\'{e} } [ cuill\`{e}res \space \`{a} \space caf\'{e} ] < f > % \end{macrocode} % \begin{macrocode} { decimal-mark } { , } { one (m) } { un } { one (f) } { une } { one (n) } { un } } % \end{macrocode} % % % % \begin{macrocode} \cudefinekeychain { { kg } { 1 } { dag }{ 100 } { g } { 1000 } { lb } { 1/0.45359237 } { oz } { 16/0.45359237 } { stick } { 4/0.45359237 } { eVc-2 } { 560958865.0e+27 } %% (560958865.0 +- 3.5) e+27 } %\cudefinekeys { kg } % { % { dag }{ 100 } % { g } { 1000 } % { lb } { 1/0.45359237 } % { oz } { 16/0.45359237 } % { stick } { 4/0.45359237 } % { eVc-2 } { 560958865.0e+27 } %% (560958865.0 +- 3.5) e+27 % } % \end{macrocode} % \begin{macrocode} \cudefinekeys { d } { { h } { 24 } { min }{ 1440 } { s } { 86400 } { hbareV-1 } { 151926746.1e+7 * 86400 } %% (151926746.1 +- 2.1) e+7 } %\cuaddtokeys { s } { hbareV-1 } { 6.582119514e-16 } % \end{macrocode} % \begin{macrocode} \cudefinekeys { m } { { dm } { 10 } { cm } { 100 } { mm } { 1000 } { in } { 36/0.9144 } { chbareV-1 } { 5067730.759 } %% 5067730.759 +- 0.070 } %\cuaddtokeys { m } { chbareV-1 } { 1.97326972e-7 } % \end{macrocode} % \begin{macrocode} \cudefinekeys { l } { { dl } { 10 } { cl } { 100 } { ml }{ 1000 } { (chbareV-1)3 } { 130148929.5e+12 * 1e-3 } %% (130148929.5 +- 5.4 e+12)*1e-3 } % \end{macrocode} % \begin{macrocode} \cudefinekeys { J } { { kJ }{ 1e-3 } { cal }{ 0.2388459 } { kcal }{ 0.2388459e-3 } { eV }{ 624150912.6e+10 } %% (624150912.6 +- 3.9) e+10 } %\cuaddtokeys { J } { eV } { 1.6021766208e-19 } % \end{macrocode} % \begin{macrocode} \fp_const:Nn \c_@@_kb_eV_fp { 8.617 330 3 e-5 } \cudefinesinglekey { C } { { K } { #1 + 273.15 } { F } { #1 * 1.8 + 32 } { Re } { #1 * 0.8 } { eV } { ( #1 + 273.15 ) * \c_@@_kb_eV_fp } } \cudefinesinglekey { F } { { C } { ( #1 - 32 ) * 5/9 } { K } { ( #1 + 459.67 ) * 5/9 } { Re } { ( #1 - 32 ) * 4/9 } { eV } {( #1 + 459.67 ) * 5/9 * \c_@@_kb_eV_fp } } \cudefinesinglekey { K } { { C } { #1 - 273.15 } { F } { #1 * 1.8 - 459.67 } { Re } { ( #1 - 273.15 ) * 0.8 } { eV } { #1 * \c_@@_kb_eV_fp } } \cudefinesinglekey { Re } { { K } { #1 * 1.25 + 273.15 } { C } { #1 * 1.25 } { F } { #1 * 2.25 + 32 } { eV } { ( #1 * 1.25 + 273.15 ) * \c_@@_kb_eV_fp } } % \end{macrocode} % % % \begin{macrocode} \cudefinephrase { German } { % { 6 } * { halbes \ Dutzend } < n > % { 6 } { halbe \ Dutzend } < n > { 12 } { Dutzend } < n > % { 60 } { Schock } < n > % { 1728 } { Gro{\ss}gros } < n > % { 144 } { Gros } < n > } % \end{macrocode} % % % \subsection{Finish} % % \begin{macrocode} \cudeclareunitgroup { weight } { kg , dag, g, oz, lb, stick } \cudeclareunitgroup { length } { m , dm , cm , mm , in } \cudeclareunitgroup { volume } { l , dl , cl , ml } \cudeclareunitgroup { temperature } { C , F , K , Re } \cudeclareunitgroup { energy } { cal, kcal , J , kJ , eV } \cudeclareunitgroup { time } { d , h , min , s } \cusetoptionfor { F , C , K , Re } { round-precision = 0 } \cusetup { curef-add-forbidden-unit = { F , C , K , Re } , add-temperature-to-check = { K = 0, C = -273.15 , F = -459.67 , Re = -218.52 } , round-precision = 2 , } %\AtBeginDocument{ %\keys_define:nn { cooking-units } % { % unknown .code:n = { \tl_show:N {\l_keys_key_str} } % } % \keys_set:nn { cooking-units } {#2} % %} % \end{macrocode} % % % % % % % % % % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % % % \begin{thebibliography}{} % \bibitem{Gattopardo} % Guiseppe Tomasi di Lampedusa, \emph{Der Gattopardo}, % Piper, Volume~8 (2018), ISBN~978-3-492-24586-9 % % \bibitem{Sh:3} % Sir Arthur Conan Doyle, \emph{Sherlock Holmes The Complete Novels and Stories Volume~II}, % Bantam Books % % \bibitem{Nist2008} \emph{Guide for the Use of the International System of Units (SI)}, % NIST Special Publication 811, 2008 Edition, Ambler Thompson and Barry N. Taylor % % \bibitem{Nist2006} \emph{The International System of Units (SI) – Conversion Factors % for General Use}, NIST Special Publication 1038, May 2006, % Kenneth Butcher, Linda Crown and Elizabeth J. Gentry % % \bibitem{WM1985} \emph{Weights and Measures Act 1985}, % \url{https://www.legislation.gov.uk/ukpga/1985/72} % % \bibitem{StickSource} \url{https://cooking.stackexchange.com/questions/784/translating-cooking-terms-between-us-uk-au-ca-nz} % % \bibitem{Plural-French-Academia} % Dictionnaire de l’Académie française. % \url{https://www.dictionnaire-academie.fr/article/A9P2979} % [Last checked: 2022-05-29] % % \bibitem{Plural-French-absolute-1} % \url{https://www.antidote.info/fr/blogue/enquetes/15-gramme-ou-15-grammes} % [Last checked: 2022-05-29] % % \bibitem{Plural-French-absolute-2} % \url{https://french.stackexchange.com/q/1975} % [Last checked: 2022-05-29] % % % \end{thebibliography} % % % % \endinput % % % % % Local Variables: % mode: doctex % TeX-master: t % End: