% This is file texdimens.tex, part of texdimens package, which
% is distributed under the LPPL 1.3c. Copyright (c) 2021 Jean-François Burnol
% 2021/07/15 v0.9delta
\edef\texdimensendinput{\endlinechar\the\endlinechar\catcode`\noexpand _=\the\catcode`\_\relax\noexpand\endinput}%
\endlinechar13\relax%
\catcode`\_=11
%
% Mathematics
% ===========
%
% Is T sp attainable from unit "uu"?. Here we suppose T>0.
% phi>1, psi=1/phi, psi<1
% U(N,phi)=trunc(N phi) is strictly increasing
%     U(N)<= T <  U(N+1)    iff    N = ceil((T+1)psi) - 1
%     U(M)<  T <= U(M+1)    iff    M = ceil(T psi)    - 1
% Either:
% case1:  M = N, i.e. T is not attainable, M=N < T psi < (T+1) psi <= N+1
% case2:  M = N - 1, i.e. T is attained, T psi <= N < (T+1) psi, T = floor(N phi) 
%
% Let X = round(T psi). And let Y = trunc(X phi).
%
% case1: X can be N or N+1. It will be N+1 iff Y > T.
% case2: X can be N or N-1. It will be N iff trunc((X+1)phi)>T.
%
% This is not convenient: if Y <= T it might still be that we are in case 2
% and we must check then if trunc((X+1) phi) > T or not.
%
% If psi < 0.5
% ------------
%
% The situation then simplifies:
%
% case1: X can be N or N+1. It will be N+1 iff Y = trunc(X phi) > T.
% case2: X is necessarily N.
%
% Thus:
% a) compute X = round(T psi)
% b) compute Y = trunc(X phi) and test if Y > T. If true, we
%    were in case 1, replace X by X - 1, else we were either
%    in case 1 or case 2, but we can leave X as is.
% We have thus found N.
%
% The operation Y = trunc(X phi) can be achieved this way:
% i) use \the\dimexpr to convert X sp into D pt, 
% ii) use \the\numexpr\dimexpr  to convert "D uu" into sp.
% These steps give Y.
%
% This way we find the maximal dimension at most T sp exactly
% representable in "uu" unit.
%
% The computations of X and Y can be done independently of sign of T.
% But the final test has to be changed to Y < T if T < 0 and then
% one must replace X by X+1. So we must filter sign.
%
% If the goal is only to find a decimal D such that "D uu" is 
% exactly T sp in the case this is possible, then things are simpler
% because from X = round(T psi) we get D such as X sp is same as D pt
% and "D uu" will work.
% We don't have to take sign into account for this computation.
% But if T sp was not atteignable we don't know if this X will give
% a D such that D uu < T sp or D uu > T sp.
%
% If psi > 0.5
% ------------
%
% For example unit "bp" has phi=803/800.
%
% It is then not true that if T sp is atteignable, the X = round(T psi)
% will always work.
%
% But it is true that R = round((T + 0.5) psi) will always work.
% Here we must use -0.5 if T < 0, though.
%
% This R=round((T+0.5) psi) can always be computed via \numexpr because 2T+1
% will not trigger arithmetic overflow.
%
% So this gives an approach to find a D such that "D uu" is exactly
% T sp when this is possible.
% 
% If Tsp (positive) is not attainable, this R however can produce
% either N or N+1.
%
% But we can decide what happened by computing Z = trunc(R phi).
% If and only if Z > T this means R was N+1.
%
% It is slightly less costly to compute X = round(T psi) than
% R = round((T + 0.5) psi),
% but if we then realize that trunc(X phi) < T  we do not yet know
% if trunc((X+1) phi) = T  or is > T.
%
% To recapitulate: we have our algorithm for all units to find out
% maximal dimension exactly atteignable in "uu" unit and at most equal
% to (positive) T sp.
%
% Unfortunately the check that Y (in case psi < 0.5) or Z (in case psi >
% 0.5) may trigger a Dimension too large error if T sp was near
% non-atteignable \maxdimen.
%
% For additional envisioned "safe versions" we would tabulate first per unit
% what is the integer Rmax such that trunc(Rmax phi) <= \maxdimen. Then
% the "safe" versions would have an extra check of X or R before
% proceeding further. But the "up macros" supposed to give the next
% dimension above Tsp and exactly atteignable in "uu" unit, if compliant
% to their description can not avoid "Dimension too large" for inputs
% close to non-attainable \maxdimen.
%
% After having written the macros we will tabulate what is for each unit
% the maximal attainable dimension.
%
% About the macros such as \texdiminbp whose constraints are:
% - give a decimal D such that  "Duu" = "T sp" for TeX if possible
% - else give nearest from below or above without knowing
%   which one,
%
% there was some hesitation about whether or not using the simpler
% round(T psi) approach for units > 2pt and the \texdimin<uu> macros.
% Testing showed that this did not change the output for \maxdimen
% with the units "nc" and "in": still N+1 is returned...
%
% As it has great
% advantage to not have to check the sign of the input, the
% "simpler" approach was chosen for those units to which it
% applies, i.e. the units uu > 2pt (phi>2, psi<1/2), i.e.
% all units except bp, nd and dd.
%
% Implementation
% ==============
%
\def\texdimenfirstofone#1{#1}%
{\catcode`p 12\catcode`t 12
 \csname expandafter\endcsname\gdef\csname texdimenstrippt\endcsname#1pt{#1}}%
%
% down macros:
% for units with phi < 2:
\def\texdimendown_A#1{\if-#1\texdimendown_neg\fi\texdimendown_B#1}%
\def\texdimendown_B#1;#2;{\expandafter\texdimendown_c\the\numexpr(2*#1+1)#2;#1;}%
% for units with phi > 2:
\def\texdimendown_a#1{\if-#1\texdimendown_neg\fi\texdimendown_b#1}%
\def\texdimendown_b#1;#2;{\expandafter\texdimendown_c\the\numexpr#1#2;#1;}%
% shared macros:
\def\texdimendown_c#1;{\expandafter\texdimendown_d\the\dimexpr#1sp;#1;}%
{\catcode`P 12\catcode`T 12\lowercase{\gdef\texdimendown_d#1PT};#2;#3;#4;%
   {\ifdim#1#4>#3sp \texdimendown_e{#2}\fi\texdimenfirstofone{#1}}%
}%
% this #2 will be \fi
\def\texdimendown_e#1#2#3#4{#2\expandafter\texdimenstrippt\the\dimexpr\numexpr#1-1sp\relax}%
% negative branch:
% The problem here is that if input very small, output can be 0.0, and we
% do not want -0.0 as output.
% So let's do this somewhat brutally and non-efficiently.
% Anyhow, negative inputs are not our priority.
% #1 is \fi here and #2 is \texdimendown_b or _B:
\def\texdimendown_neg#1#2-#3;#4;#5;{#1\expandafter\texdimenstrippt\the\dimexpr-#2#3;#4;#5;pt\relax}%
%
% up macros:
\def\texdimenup_A#1{\if-#1\texdimenup_neg\fi\texdimenup_B#1}%
\def\texdimenup_B#1;#2;{\expandafter\texdimenup_c\the\numexpr(2*#1+1)#2;#1;}%
\def\texdimenup_a#1{\if-#1\texdimenup_neg\fi\texdimenup_b#1}%
\def\texdimenup_b#1;#2;{\expandafter\texdimenup_c\the\numexpr#1#2;#1;}%
\def\texdimenup_c#1;{\expandafter\texdimenup_d\the\dimexpr#1sp;#1;}%
{\catcode`P 12\catcode`T 12\lowercase{\gdef\texdimenup_d#1PT};#2;#3;#4;%
   {\ifdim#1#4<#3sp \texdimenup_e{#2}\fi\texdimenfirstofone{#1}}%
}%
% this #2 will be \fi
\def\texdimenup_e#1#2#3#4{#2\expandafter\texdimenstrippt\the\dimexpr\numexpr#1+1sp\relax}%
% negative branch:
% Here we can me more expeditive than for the "down" macros.
% But this breaks f-expandability.
% #1 will be \fi and #2 is \texdimenup_b or _B:
\def\texdimenup_neg#1#2-{#1-#2}%
%
% pt
%
\def\texdiminpt#1{\expandafter\texdimenstrippt\the\dimexpr#1\relax}%
%
% bp 7227/7200 = 803/800
%
\def\texdiminbp#1{\expandafter\texdiminbp_\the\numexpr\dimexpr#1;}%
\def\texdiminbp_#1#2;{%
    \expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*400/803sp\relax
}%
% \texdiminbpdown: maximal dim exactly expressible in bp and at most equal to input
\def\texdiminbpdown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*400/803;bp;}%
% \texdiminbpup: minimal dim exactly expressible in bp and at least equal to input
\def\texdiminbpup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*400/803;bp;}%
%
% nd 685/642
%
\def\texdiminnd#1{\expandafter\texdiminnd_\the\numexpr\dimexpr#1;}%
\def\texdiminnd_#1#2;{%
    \expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*321/685sp\relax
}%
% \texdiminnddown: maximal dim exactly expressible in nd and at most equal to input
\def\texdiminnddown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*321/685;nd;}%
% \texdiminndup: minimal dim exactly expressible in nd and at least equal to input
\def\texdiminndup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*321/685;nd;}%
%
% dd 1238/1157
%
\def\texdimindd#1{\expandafter\texdimindd_\the\numexpr\dimexpr#1;}%
\def\texdimindd_#1#2;{%
    \expandafter\texdimenstrippt\the\dimexpr\numexpr(2*#1#2+\if-#1-\fi1)*1157/2476sp\relax
}%
% \texdimindddown: maximal dim exactly expressible in dd and at most equal to input
\def\texdimindddown#1{\expandafter\texdimendown_A\the\numexpr\dimexpr#1;*1157/2476;dd;}%
% \texdiminddup: minimal dim exactly expressible in dd and at least equal to input
\def\texdiminddup#1{\expandafter\texdimenup_A\the\numexpr\dimexpr#1;*1157/2476;dd;}%
%
% mm 7227/2540 phi now >2, use from here on the simpler approach
%
\def\texdiminmm#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*2540/7227\relax}%
% \texdiminmmdown: maximal dim exactly expressible in mm and at most equal to input
\def\texdiminmmdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*2540/7227;mm;}%
% \texdiminmmup: minimal dim exactly expressible in mm and at least equal to input
\def\texdiminmmup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*2540/7227;mm;}%
%
% pc 12/1
%
\def\texdiminpc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)/12\relax}%
% \texdiminpcdown: maximal dim exactly expressible in pc and at most equal to input
\def\texdiminpcdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;/12;pc;}%
% \texdiminpcup: minimal dim exactly expressible in pc and at least equal to input
\def\texdiminpcup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;/12;pc;}%
%
% nc 1370/107
%
\def\texdiminnc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*107/1370\relax}%
% \texdiminncdown: maximal dim exactly expressible in nc and at most equal to input
\def\texdiminncdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*107/1370;nc;}%
% \texdiminncup: minimal dim exactly expressible in nc and at least equal to input
\def\texdiminncup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*107/1370;nc;}%
%
% cc 14856/1157
%
\def\texdimincc#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*1157/14856\relax}%
% \texdiminccdown: maximal dim exactly expressible in cc and at most equal to input
\def\texdiminccdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*1157/14856;cc;}%
% \texdiminccup: minimal dim exactly expressible in cc and at least equal to input
\def\texdiminccup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*1157/14856;cc;}%
%
% cm 7227/254
%
\def\texdimincm#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*254/7227\relax}%
% \texdimincmdown: maximal dim exactly expressible in cm and at most equal to input
\def\texdimincmdown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*254/7227;cm;}%
% \texdimincmup: minimal dim exactly expressible in cm and at least equal to input
\def\texdimincmup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*254/7227;cm;}%
%
% in 7227/100
%
\def\texdiminin#1{\expandafter\texdimenstrippt\the\dimexpr(#1)*100/7227\relax}%
% \texdiminindown: maximal dim exactly expressible in in and at most equal to input
\def\texdiminindown#1{\expandafter\texdimendown_a\the\numexpr\dimexpr#1;*100/7227;in;}%
% \texdimininup: minimal dim exactly expressible in in and at least equal to input
\def\texdimininup#1{\expandafter\texdimenup_a\the\numexpr\dimexpr#1;*100/7227;in;}%
%
\texdimensendinput
