summaryrefslogtreecommitdiff
path: root/macros/generic/texdimens/texdimens.tex
blob: 189caca95d09f350507cbb2d4201de5ec0d5dc4d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
% 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