evenmore-numbers.tex /size: 11 Kb    last modification: 2021-10-28 13:50
1% language=us runpath=texruns:manuals/evenmore
2
3\startcomponent evenmore-numbers
4
5\environment evenmore-style
6
7\startchapter[title={Numbers}]
8
9% \startsection[title={Introduction}]
10
11A few decades of programming in the \TEX\ language can make one wish for certain
12features. It will therefore be no surprise that in \LUATEX\ (and even more in
13\LUAMETATEX) we have some additional functionality. However, I have to admit that
14some of these are not used that much in \CONTEXT\ \MKIV\ and \LMTX. The reason is
15that some wishes date from \MKII\ times and because we now have \LUA\ we actually
16don't need that many new fancy features. Also, it makes no sense to rewrite
17mechanisms that are already working well. However, in order to fully exploit the
18possibilities that \LUA\ gives, there are some additions that relate to the way
19this language can communicate with \TEX. Of course there's also the issue of a
20potentially enhanced performance, but there is not much to gain in that
21department.
22
23A side effect of adding features, of which some are just there to complete the
24picture, or, as mentioned, because they were supposed to make sense, is that I
25make examples. Here I show the result of one of these experiments. I have no clue
26how useful this is, but I've learned not to underestimate users in their demands
27and creativity.
28
29Internally, \TEX\ does all in 32 bit integers. When you say:
30
31\starttyping
32\scratchcounter 123
33\scratchdimen   123pt
34\stoptyping
35
36the \type {123} gets assigned to a count register and the \type {123pt} is
37assigned to a dimen register but actually that is then also an integer: the
38internal unit of a dimen is a scaled point (sp) and only when its value is shown
39to the user, a real number can show up, followed by the \type {pt} unit. The
40precision is limited, so you can expect about four decimal positions precision
41here. There is no concept of a floating point number in \TEX, and the only case
42where a float gets used is in the final calculations of glue and even that only
43comes into play in the backend.
44
45So, although I don't really have an application for it in \CONTEXT\ (otherwise
46I'd already added a float data type to the engine), it sounded like a good idea
47to see if we could emulate float support. In the following examples the numbers
48are managed in \LUA\ and therefore they are global. I could make a local variant
49but why complicate matters. These macros start with \type {\lua} to make clear
50that they are not managed by \TEX.
51
52\startbuffer
53\luacardinal bar  123
54\luainteger  bar -456
55\luafloat    bar  123.456E-3
56\stopbuffer
57
58\typebuffer \getbuffer
59
60We define \type {bar} three times. Each type gets its own hash, so from the
61perspective of \LUA\ its nature is kept: integer or double.
62
63\startbuffer
64\the\luacardinal bar \quad
65\the\luainteger  bar \quad
66\the\luafloat    bar
67\stopbuffer
68
69\typebuffer \getbuffer
70
71Instead of decimal values, you can also use hexadecimal values (watch the \type
72{p} for exponents):
73
74\startbuffer
75\luacardinal bar  0x123
76\luainteger  bar -0x456
77\luafloat    bar  0x123.456p-3
78\stopbuffer
79
80\typebuffer \getbuffer
81
82So, now we get:
83
84\startbuffer
85\the\luacardinal bar \quad
86\the\luainteger  bar \quad
87\the\luafloat    bar
88\stopbuffer
89
90\getbuffer
91
92From these examples you see two kind of usage: setting a value, and using it. It
93is that property that makes them special. Because the macros are implemented
94using \LUA\ calls it means that at the \LUA\ end we know what usage is expected.
95And it is that dualistic property that I wanted to explore but that in the end
96only makes sense it a very few cases, but sometimes those few are important. We
97could of course two macros, a setter and a getter, but using one kind of its in.
98
99The setters accept an optional equal sign, as in:
100
101\startbuffer
102\luainteger gnu=  123456   \luafloat gnu=  123.456e12
103\luainteger gnu = 123456   \luafloat gnu = 123.456e12
104\luainteger gnu  =123456   \luafloat gnu  =123.456e12
105\stopbuffer
106
107\typebuffer
108
109Although \LUA\ is involved in picking up the value, storing it someplace, and
110retrieving it on demand, performance is pretty good. You probably won't notice
111the overhead anyway.
112
113The values that \type{\the} returns are serialized numbers. However, sometimes
114you want what \TEX\ sees as a numeric token, For that we have these variants
115
116\startbuffer
117\luadimen test 100pt
118\scratchdimen = .25 \luadimen test
119\the\scratchdimen
120\stopbuffer
121
122\typebuffer
123
124Which produces the expected value: {\tttf \inlinebuffer}, something that depends
125on the fact that the dimension is not a serialized. Talking of serialization,
126there are several ways that \LUA\ can do that so let's give some examples. We
127start with some definitions. Beware, floats and cardinals are stored
128independently!
129
130\startbuffer
131\luacardinal x = -123
132\luacardinal y =  456
133
134\luafloat    x =  123.123
135\luafloat    y = -456.456
136\stopbuffer
137
138\typebuffer \getbuffer
139
140We have a macro \type {\luaexpression} (not to be confused with \type {\luaexpr}) that
141takes an optional keyword:
142
143\startbuffer
144- : \luaexpression          {n.x + 2*n.y}
145f : \luaexpression float    {n.x + 2*n.y}
146i : \luaexpression integer  {n.x + 2*n.y}
147c : \luaexpression cardinal {n.x + 2*n.y}
148b : \luaexpression boolean  {n.x + 2*n.y}
149l : \luaexpression lua      {n.x + 2*n.y}
150\stopbuffer
151
152\typebuffer
153
154The serialization can be different for these cases:
155
156\startlines
157\tt \getbuffer
158\stoplines
159
160The \type {numbers} namespace resolves to a float, integer or cardinal (in that
161order) and calculations take place as in \LUA. If you only use integers then
162normally \LUA\ will also serialize them as such.
163
164Here is another teaser. Say that we set the \type {scratchdimen} register to
165a value:
166
167\startbuffer
168\scratchdimen 123.456pt
169\stopbuffer
170
171\typebuffer \getbuffer
172
173We now introduce the \type {\nodimen} macro, that can be used this way:
174
175\startbuffer
176[\the\scratchdimen] [\the\nodimen\scratchdimen]
177\stopbuffer
178
179\typebuffer \getbuffer
180
181which is not that spectacular. Nor is this:
182
183\startbuffer
184\nodimen\scratchdimen = 654.321pt
185\stopbuffer
186
187\typebuffer \getbuffer
188
189But how about this:
190
191\starttabulate[|T|T|]
192\NC \type {\the\nodimen bp \scratchdimen} \NC \the\nodimen bp \scratchdimen \NC \NR
193\NC \type {\the\nodimen cc \scratchdimen} \NC \the\nodimen cc \scratchdimen \NC \NR
194\NC \type {\the\nodimen cm \scratchdimen} \NC \the\nodimen cm \scratchdimen \NC \NR
195\NC \type {\the\nodimen dd \scratchdimen} \NC \the\nodimen dd \scratchdimen \NC \NR
196\NC \type {\the\nodimen in \scratchdimen} \NC \the\nodimen in \scratchdimen \NC \NR
197\NC \type {\the\nodimen mm \scratchdimen} \NC \the\nodimen mm \scratchdimen \NC \NR
198\NC \type {\the\nodimen nc \scratchdimen} \NC \the\nodimen nc \scratchdimen \NC \NR
199\NC \type {\the\nodimen nd \scratchdimen} \NC \the\nodimen nd \scratchdimen \NC \NR
200\NC \type {\the\nodimen pt \scratchdimen} \NC \the\nodimen pt \scratchdimen \NC \NR
201\NC \type {\the\nodimen sp \scratchdimen} \NC \the\nodimen sp \scratchdimen \NC \NR
202\stoptabulate
203
204So here we have a curious mix of setter and getter. The setting part is not that
205interesting but we just provide it as convenience (and demo). Of course we can
206have 10 specific macros instead. Keep in mind that this is a low level macro, so
207it doesn't use the normal \CONTEXT\ user interface.
208
209A bit more complex are one or two dimensional arrays. Again this is an example
210implementation where users can come up with more ideas.
211
212\startbuffer
213\newarray name integers   type integer   nx 2 ny 2
214\newarray name booleans   type boolean   nx 2 ny 2
215\newarray name floats     type float     nx 2 ny 2
216\newarray name dimensions type dimension nx 4
217\stopbuffer
218
219\typebuffer \getbuffer
220
221Here we define three two|-|dimensional assays and one one|-|dimensional
222array. The type determines the initialization as well as the scanner and
223serializer.  Values can be set as follows:
224
225\startbuffer
226\arrayvalue integers   1 2 4        \arrayvalue integers   2 1 8
227\arrayvalue booleans   1 2 true     \arrayvalue booleans   2 1 true
228\arrayvalue floats     1 2 12.34    \arrayvalue floats     2 1 34.12
229\arrayvalue dimensions 1   12.34pt  \arrayvalue dimensions 3   34.12pt
230\stopbuffer
231
232\typebuffer \getbuffer
233
234If you want to check an array on the console, you can say:
235
236\starttyping
237\showarray integers
238\stoptyping
239
240We now access some values. Apart from the float these are (sort of) native data
241types.
242
243\startbuffer
244[\the\arrayvalue integers   1 2]
245[\the\arrayvalue booleans   1 2]
246[\the\arrayvalue floats     1 2]
247[\the\arrayvalue dimensions 1  ]\crlf
248[\the\arrayvalue integers   2 1]
249[\the\arrayvalue booleans   2 1]
250[\the\arrayvalue floats     2 1]
251[\the\arrayvalue dimensions   3]
252\stopbuffer
253
254\typebuffer
255
256This produces:
257
258\getbuffer
259
260You can of course use these values in many ways:
261
262\startbuffer
263\dostepwiserecurse{1}{4}{1}{
264    [\the\arrayvalue dimensions #1 :
265     \luaexpression dimen {math.sind(30) * a.dimensions[#1]}]
266}
267\stopbuffer
268
269\typebuffer
270
271This gives:
272
273\getbuffer
274
275In addition to the already seen integer and dimension variables fed back into
276\TEX, we also have booleans. These are just integers with the value zero or one.
277In order to make their use easier there is a new \type {\ifboolean} primitive
278that takes such a bit:
279
280\startbuffer
281slot 1 is \ifboolean\arrayequals dimensions 1 0pt zero \else not zero \fi
282slot 2 is \ifboolean\arrayequals dimensions 2 0pt zero \else not zero \fi
283\stopbuffer
284
285\typebuffer
286
287We get:
288
289\startlines
290\getbuffer
291\stoplines
292
293A variant is a comparison macro. Of course we can use the dimen comparison
294conditional instead:
295
296\startbuffer
297slot 1: \ifcase\arraycompare dimensions 1 3pt lt \or eq \else gt \fi zero
298slot 2: \ifcase\arraycompare dimensions 2 3pt lt \or eq \else gt \fi zero
299slot 3: \ifcase\arraycompare dimensions 3 3pt lt \or eq \else gt \fi zero
300slot 4: \ifcase\arraycompare dimensions 4 3pt lt \or eq \else gt \fi zero
301
302slot 1: \ifcmpdim\arrayvalue dimensions 1 3pt lt \or eq \else gt \fi zero
303slot 2: \ifcmpdim\arrayvalue dimensions 2 3pt lt \or eq \else gt \fi zero
304slot 3: \ifcmpdim\arrayvalue dimensions 3 3pt lt \or eq \else gt \fi zero
305slot 4: \ifcmpdim\arrayvalue dimensions 4 3pt lt \or eq \else gt \fi zero
306\stopbuffer
307
308\typebuffer
309
310We get:
311
312\startlines
313\getbuffer
314\stoplines
315
316Anyway, the question is: do we need this kind of trickery, and if so, what more
317is needed? But beware: we do have \LUA\ anyway, so there is no need for a complex
318user interface at the \TEX\ end just for the sake of it looking more \TEX. The
319above shows a bit what is possible.
320
321It is too soon to discuss the low level interface because it still evolves. After
322some initial experiments, I decided to follow a slightly different route, and
323often the third implementation starts to look what I like more.
324
325% \newarray name whatever type integer nx 100 ny 100
326%
327% \testfeatureonce{1}{
328%     \dorecurse {100} {
329%         \dorecurse {100} {
330%             \scratchcounter \arrayvalue whatever ##1 ####1 \relax
331%         }
332%     }
333% } \elapsedtime
334
335% \startluacode
336% local whatever = { foo = true, bar = true }
337%
338% interfaces.implement {
339%     name      = "MyMatch",
340%     public    = true,
341%     value     = true,
342%     actions   = function(b)
343%         -- we gobble spaces
344%         return
345%             tokens.functionvalues.boolean,
346%             whatever[tokens.scanners.word(true)] or false
347%     end,
348% }
349% \stopluacode
350%
351% [\ifboolean\MyMatch foo YES\else NOP\fi]
352% [\ifboolean\MyMatch rab YES\else NOP\fi]
353% [\ifboolean\MyMatch bar YES\else NOP\fi]
354% [\ifboolean\MyMatch oof YES\else NOP\fi]
355%
356% \def\Matched#1{\ifboolean\MyMatch #1 }
357%
358% [\ifcondition\Matched{oof}YES\else NOP\fi]
359% [\ifcondition\Matched{foo}YES\else NOP\fi]
360
361% \stopsection
362
363\stopchapter
364
365\stopcomponent
366