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\startluacode
30if not tex.issetup then
31
32 function tex.issetup(n)
33 return tex.isdefined(tokens.getters.macro("??setup") .. ":" .. n)
34 end
35
36end
37
38local tonumber = tonumber
39
40local context = context
41local copytable = table.copy
42local round = math.round
43
44local shows = { }
45local helpers = { }
46
47local implement = interfaces.implement
48
49local v_none = interfaces.variables.none
50
51local ctx_NC = context.NC
52local ctx_NR = context.NR
53local ctx_startcolor = context.startcolor
54local ctx_stopcolor = context.stopcolor
55
56local result = {
57 n = 0,
58 numerator = { },
59 denominator = { },
60 steps = { },
61}
62
63do
64
65 local function show(t,split,symbol,mark)
66 local n = #t
67 local done = false
68 for i=n,1,-1 do
69 local tn = t[i]
70 ctx_NC()
71 if not tn or tn == 0 then
72 if split then
73 ctx_NC()
74 end
75 else
76 if mark then ctx_startcolor { mark } end
77 if done and tn > 0 then
78 context("+")
79 end
80 if tn < 0 then
81 context("-")
82 tn = - tn
83 end
84 if split then
85 if mark then ctx_stopcolor() end
86 ctx_NC()
87 if mark then ctx_startcolor { mark } end
88 end
89 if i > 1 then
90 if tn ~= 1 then
91 context("%.3N",tn)
92 end
93 context(symbol)
94 else
95 context("%.3N",tn)
96 end
97 if i > 2 then
98 context("^{%s}",i-1)
99 end
100 if mark then ctx_stopcolor() end
101 done = true
102 end
103 end
104 ctx_NR()
105 end
106
107 local function default(settings)
108 local split = settings.split
109 local symbol = settings.symbol or "x"
110 context.startalign {
111 n = (split and 2 or 1) * result.n,
112 align = "all:right",
113 }
114 local steps = result.steps
115 local previous = result.numerator
116 show(result.denominator,split,symbol,"blue")
117 show(result.numerator,split,symbol)
118 for i=1,#steps do
119 local current = steps[i].numerator
120 if previous then
121 show(helpers.subtract(previous,current),split,symbol,"red")
122 end
123 show(current,split,symbol)
124 previous = current
125 end
126 show(result.quotient,split,symbol,"green")
127
128 context.stopalign()
129 end
130
131 shows["align:normal"] = function(settings) default(settings,false) end
132 shows["align:split"] = function(settings) default(settings,true) end
133
134end
135
136do
137
138 local function show(t,symbol)
139 local n = #t
140 local done = false
141 for i=n,1,-1 do
142 local tn = t[i]
143 if not tn or tn == 0 then
144
145 else
146 if done and tn > 0 then
147 context("+")
148 end
149 if tn < 0 then
150 context("-")
151 tn = - tn
152 end
153 if i > 1 then
154 if tn ~= 1 then
155 context("%.3N",tn)
156 end
157 context(symbol)
158 else
159 context("%.3N",tn)
160 end
161 if i > 2 then
162 context("^{%s}",i-1)
163 end
164 done = true
165 end
166 end
167 end
168
169 helpers.show = show
170
171 local function show_n_over_d(n,d,index,symbol)
172 local colors = result.colors
173 local c = colors and colors[index]
174 local cn = c and (c.ns or c.n)
175 local cd = c and (c.ds or c.d)
176 context.frac(
177 function()
178 if cn then
179 ctx_startcolor { cn }
180 show(n,symbol)
181 ctx_stopcolor()
182 else
183 show(n,symbol)
184 end
185 end,
186 function()
187 if cd then
188 ctx_startcolor { cd }
189 show(d,symbol)
190 ctx_stopcolor()
191 else
192 show(d,symbol)
193 end
194 end
195 )
196 end
197
198 local function show_what(w,what,index,symbol)
199 local colors = result.colors
200 local c = colors and colors[index]
201 local cw = c and c[what]
202 if cw then
203 ctx_startcolor { cw }
204 show(w,symbol)
205 ctx_stopcolor()
206 else
207 show(w,symbol)
208 end
209 end
210
211 local function show_n(n,index,symbol) show_what(n,"n",index,symbol) end
212 local function show_d(d,index,symbol) show_what(d,"d",index,symbol) end
213 local function show_q(q,index,symbol) show_what(q,"q",index,symbol) end
214
215 helpers.show_n = show_n
216 helpers.show_d = show_d
217 helpers.show_q = show_q
218 helpers.show_n_over_d = show_n_over_d
219
220 local function lines(settings,align)
221 local steps = result.steps
222 local nofsteps = #steps
223 local colors = result.colors
224 local symbol = result.symbol or "x"
225 show_n_over_d(result.numerator,result.denominator,0,symbol)
226 if nofsteps > 0 then
227 if align then
228 context.alignhere()
229 end
230 context(steps[1].clipped and "≈" or "=")
231 for i=1,nofsteps do
232 local step = steps[i]
233 show_q(step.quotient,i,symbol)
234 if not helpers.iszero(step.numerator) then
235 context("+")
236 show_n_over_d(step.numerator,result.denominator,i,symbol)
237 end
238 if i < nofsteps then
239 if align then
240 context.breakhere()
241 end
242 context(step.clipped and "≈" or "=",symbol)
243 end
244 end
245 else
246
247
248 end
249 end
250
251 shows["text:normal"] = function(settings) lines(settings,false) end
252 shows["text:align"] = function(settings) lines(settings,true) end
253
254end
255
256do
257
258 local function subtract(n,d)
259 local t = { }
260 for i=1,#d do
261 t[i] = n[i] - d[i]
262 end
263 return t
264 end
265
266 local function negate(v)
267 local t = { }
268 for i=1,#v do
269 t[i] = -v[i]
270 end
271 return t
272 end
273
274 local function iszero(t)
275 for i=1,#t do
276 if t[i] ~= 0 then
277 return false
278 end
279 end
280 return true
281 end
282
283 local function last(t)
284 for i=#t,1,-1 do
285 local ti = t[i]
286 if ti ~= 0 then
287 return i
288 end
289 end
290 return false
291 end
292
293 local meps <const> = -0.000001
294 local peps <const> = 0.000001
295
296 local function solve(settings)
297 local numerator = settings.numerator
298 local denominator = settings.denominator
299 if type(numerator) == "string" then
300 numerator = utilities.parsers.settings_to_array(numerator)
301 settings.numerator = numerator
302 end
303 if type(denominator) == "string" then
304 denominator = utilities.parsers.settings_to_array(denominator)
305 settings.denominator = denominator
306 end
307 local n = #numerator
308 local d = #denominator
309 for i=1,n do numerator [i] = tonumber(numerator [i]) or 0 end
310 for i=1,d do denominator[i] = tonumber(denominator[i]) or 0 end
311 for i=1,n do denominator[i] = tonumber(denominator[i]) or 0 end
312
313 if d > n then
314 logs.report("polynomial","denominator has a higher degree")
315
316 end
317
318 local nlast = last(numerator)
319 local dlast = last(denominator)
320 local steps = { }
321 local quotient = { }
322 result = {
323 n = n,
324 numerator = numerator,
325 denominator = denominator,
326 steps = steps,
327 colors = settings.colors,
328 symbol = settings.symbol or "x",
329 }
330 for i=1,n do
331 quotient[i] = 0
332 end
333 while nlast and dlast and nlast >= dlast do
334 numerator = copytable(numerator)
335 local shift = nlast - dlast
336 local nvalue = numerator[nlast]
337 local dvalue = denominator[dlast]
338
339 local factor = nvalue / dvalue
340 local clipped = false
341 for i=1,n do
342 local ni = numerator[i]
343 local di = denominator[i-shift] or 0
344 local ni = ni - factor * di
345 if ni ~= 0 and ni > meps and ni < peps then
346 ni = 0
347 clipped = true
348 end
349 numerator[i] = ni
350 end
351 quotient[shift+1] = factor
352 steps[#steps+1] = {
353 numerator = numerator,
354 factor = factor,
355 nvalue = nvalue,
356 dvalue = dvalue,
357 shift = shift,
358 quotient = copytable(quotient),
359 clipped = clipped
360 }
361 local l = last(numerator)
362 if l == nlast then
363 break
364 end
365 nlast = l
366 end
367 return result
368 end
369
370 helpers.last = last
371 helpers.subtract = subtract
372 helpers.negate = negate
373 helpers.iszero = iszero
374 helpers.solve = solve
375
376 shows[v_none] = function(settings)
377
378 end
379
380 shows.default = shows["text:normal"]
381
382 local function tocolors(colors)
383 if type(colors) == "string" then
384 local list = utilities.parsers.settings_to_hash(colors)
385 local done = { }
386 for k, v in next, list do
387 done[tonumber(k) or 0] = utilities.parsers.settings_to_hash(v)
388 end
389 return done
390 end
391 end
392
393 implement {
394 name = "polynomial",
395 arguments = {
396 {
397 { "alternative" },
398 { "numerator" },
399 { "denominator" },
400 { "split", "boolean" },
401 { "colors" },
402 { "symbol" },
403 },
404 },
405 actions = function(settings)
406 settings.colors = tocolors(settings.colors)
407 result = solve(settings)
408 local alternative = settings.alternative or "default"
409 if shows[alternative] then
410 shows[alternative](settings)
411 else
412 local s = "math:polynomial:" .. alternative
413 if tex.issetup(s) then
414 context.formatted.directsetup(s)
415 else
416 shows.default(settings)
417 end
418 end
419 end
420 }
421
422 local show = helpers.show
423
424 local function getcolor(n,what)
425 local colors = result.colors
426 local c = colors and colors[n]
427 return c and c[what]
428 end
429
430 implement {
431 name = "polynomialsteps",
432 usage = "value",
433 actions = function()
434 return tokens.values.integer, #result.steps
435 end
436 }
437
438 implement {
439 name = "polynomialfactor",
440 arguments = "integer",
441 actions = function(n)
442 local s = result.steps[n]
443 if s and s.factor then
444 context(round(s.factor))
445 else
446 context(0)
447 end
448 end,
449 }
450
451 implement {
452 name = "polynomialshift",
453 arguments = "integer",
454 actions = function(n)
455 local s = result.steps[n]
456 if s and s.shift then
457 context(round(s.shift))
458 else
459 context(0)
460 end
461 end,
462 }
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483 local function whatever(kind,s)
484 if kind == "-" then
485 s = helpers.negate(s)
486 end
487 if kind == "+" or kind == '-' then
488 for i=#s,1,-1 do
489 if s[i] < 0 then
490 break
491 elseif s[i] > 0 then
492 context("+")
493 break
494 end
495 end
496 end
497 helpers.show(s,result.symbol)
498 end
499
500 implement {
501 name = "polynomialnumerator",
502 arguments = { "string", "integer" },
503 actions = function(kind,n)
504 if n > 0 then
505 local s = result.steps[n]
506 if s then
507 whatever(kind,s.numerator)
508 end
509 else
510 whatever(kind,result.numerator)
511 end
512 end
513 }
514
515 implement {
516 name = "polynomialdenominator",
517 arguments = { "string", "integer" },
518 actions = function(kind,n)
519 whatever(kind,result.denominator)
520 end,
521 }
522
523 implement {
524 name = "polynomialquotient",
525 arguments = { "string", "integer" },
526 actions = function(kind,n)
527 if n > 0 then
528 local s = result.steps[n]
529 if s then
530 whatever(kind,s.quotient)
531 end
532 end
533 end,
534 }
535
536 implement {
537 name = "polynomialstep",
538 arguments = { "string", "integer" },
539 actions = function(kind,n)
540 local s = n > 0 and result.steps[n] or result
541 if s then
542 helpers.show_n_over_d(s.numerator,result.denominator,n,result.symbol)
543 end
544 end
545 }
546
547 implement {
548 name = "polynomialquotientstep",
549 arguments = { "string", "integer" },
550 actions = function(kind,n)
551 local s = result.steps[n]
552 if s then
553 local f = s.factor
554 if n > 1 then
555 local p = result.steps[n-1]
556 s = helpers.subtract(s.quotient,p.quotient)
557 else
558 s = s.quotient
559 end
560 if kind == "-" then
561 if f > 0 then
562 context("-")
563 else
564 s = helpers.negate(s)
565 context("+")
566 end
567 elseif kind == "+" then
568 if f > 0 then
569 context("+")
570 end
571 end
572 helpers.show_q(s,n,result.symbol)
573 end
574 end,
575 }
576
577 implement {
578 name = "ifpolynomialclipped",
579 arguments = "integer",
580 usage = "condition",
581 public = true,
582 actions = function(n)
583 local s = result.steps[n]
584 return tokens.values.boolean, s and s.quotient
585 end,
586 }
587
588 implement {
589 name = "ifpolynomialnumerator",
590 arguments = "integer",
591 usage = "condition",
592 public = true,
593 actions = function(n)
594 local s
595 if n > 0 then
596 s = result.steps[n]
597 else
598 s = result
599 end
600 return tokens.values.boolean, s and not helpers.iszero(s.numerator)
601 end
602 }
603
604 implement {
605 name = "polynomialsymbol",
606 public = true,
607 protected = true,
608 actions = function()
609 context(result.symbol)
610 end,
611 }
612
613end
614
615\stopluacode
616
617\unprotect
618
619\installcorenamespace {polynomial}
620
621\installparameterhandler\??polynomial {polynomial}
622\installsetuphandler \??polynomial {polynomial}
623
624\setuppolynomial
625 [\c!split=\v!no,
626 \c!alternative=text:align,
627 \c!symbol=x]
628
629\tolerant\protected\def\polynomial[#S#1]#*[#2]#*[#3]
630 {\begingroup
631 \ifhastok={#1}
632 \setupcurrentpolynomial[#1]
633 \donetrue
634 \else
635 \donefalse
636 \fi
637 \clf_polynomial {
638 numerator \ifdone{#2}\else{#1}\fi
639 denominator \ifdone{#3}\else{#2}\fi
640 alternative {\polynomialparameter\c!alternative}
641 colors {\polynomialparameter\c!color}
642 symbol {\polynomialparameter\c!symbol}
643 split \ifcstok{\polynomialparameter\c!split}\v!yes true \else false\fi
644 }
645 \endgroup}
646
647\tolerant\protected\def\polynomialsteps {\the\clf_polynomialsteps}
648\tolerant\protected\def\polynomialfactor [#1]{\clf_polynomialfactor \numexpr\ifparameter#1\or#1\else\zerocount\fi\relax}
649\tolerant\protected\def\polynomialshift [#1]{\clf_polynomialshift \numexpr\ifparameter#1\or#1\else\zerocount\fi\relax}
650
651\tolerant\def\module_polynomial_component[#1]#*[#2]#*[#3]
652 {\ifarguments
653 \or
654 #1{}\zerocount
655 \or
656 #1{}\numexpr\ifparameter#2\or#2\else\zerocount\fi\relax
657 \else
658 #1{#2}\numexpr\ifparameter#3\or#3\else\zerocount\fi\relax
659 \fi}
660
661\protected\def\polynomialnumerator {\module_polynomial_component[\clf_polynomialnumerator ]}
662\protected\def\polynomialdenominator {\module_polynomial_component[\clf_polynomialdenominator ]}
663\protected\def\polynomialquotient {\module_polynomial_component[\clf_polynomialquotient ]}
664\protected\def\polynomialquotientstep{\module_polynomial_component[\clf_polynomialquotientstep]}
665\protected\def\polynomialstep {\module_polynomial_component[\clf_polynomialstep ]}
666
667\startsetups math:polynomial:complete
668 \frac {
669 \polynomialnumerator
670 } {
671 \polynomialdenominator
672 }
673 \alignhere
674 \localcontrolledrepeat \polynomialsteps {
675 =
676 \ifnum\currentloopiterator>\plusone
677 \polynomialquotient[\currentloopiterator 1]
678 \fi
679 \frac {
680 \polynomialquotientstep[\currentloopiterator]
681 (\polynomialdenominator)
682 \polynomialnumerator[][\currentloopiterator 1]
683
684 \polynomialquotientstep[][\currentloopiterator]
685
686 (\polynomialdenominator)
687 } {
688 \polynomialdenominator
689 }
690 \breakhere
691 =
692 \polynomialquotient[\currentloopiterator]
693 \ifpolynomialnumerator\currentloopiterator
694
695 \frac {
696 \polynomialnumerator[\currentloopiterator]
697 } {
698 \polynomialdenominator
699 }
700 \fi
701 \ifnum\currentloopiterator<\polynomialsteps
702 \breakhere
703 \fi
704 }
705\stopsetups
706
707\protect
708
709\continueifinputfile{mpolynomial.mkxl}
710
711\setuplayout[tight]
712
713\setuppapersize[A4,landscape][A4,landscape]
714
715\starttext
716
717\setuppolynomial[symbol=z]
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739\def\TestPolynomial#1#2#3
740 {\startformula
741 \polynomial
742 [alternative=#1]
743 [#2]
744 [#3]
745 \stopformula}
746
747
748
749\def\TestPolynomials#1#2
750 {\page
751 \TestPolynomial{text:normal} {#1}{#2}
752 \blank[3*big]
753 \TestPolynomial{text:align} {#1}{#2}
754 \page
755 }
756
757\TestPolynomials
758 {7, 5, 2, 3}
759 {3, 0, 1}
760
761\TestPolynomials
762 {7, 5, 2, 3}
763 {3, 0, 2.7}
764
765\TestPolynomials
766 {7, 5, 2, 3}
767 {3, 0, 1}
768
769\startformula
770\polynomial
771 [color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange}}]
772 [7, 5, 2, 3, 2]
773 [3, 0, 1]
774\stopformula
775
776\polynomial
777 [alternative=none,
778 color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange,ds=darkgray}}]
779 [7, 5, 2, 3, 2]
780 [3, 0, 1]
781
782numerator
783
784\startformula
785 \polynomialnumerator
786\stopformula
787
788numerator 2
789
790\startformula
791 \polynomialnumerator[2]
792\stopformula
793
794denominator
795
796\startformula
797 \polynomialdenominator
798\stopformula
799
800quotient 123
801
802\startformula
803 \polynomialquotient[1]
804 \polynomialquotient[2]
805 \polynomialquotient[3]
806\stopformula
807
808quotientstep 123
809
810\startformula
811 \polynomialquotientstep[1]
812 \polynomialquotientstep[2]
813 \polynomialquotientstep[3]
814\stopformula
815
816steps
817
818\startformula
819 \polynomialsteps
820\stopformula
821
822factor
823
824\startformula
825 \polynomialfactor[2]
826\stopformula
827a
828shift
829
830\startformula
831 \polynomialshift[2]
832\stopformula
833
834
835\startformula
836 \frac {\polynomialnumerator}{\polynomialdenominator}
837 =
838 \polynomialquotient[1]
839
840 \frac {\polynomialnumerator[1]}{\polynomialdenominator}
841\stopformula
842
843\startformula
844\polynomial
845 [alternative=complete,
846 color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange,ds=darkgray}}]
847 [7, 5, 2, 3, 2]
848 [3, 0, 1]
849\stopformula
850
851
852
853\dorecurse\polynomialsteps{
854 \startformula
855 \polynomialstep[#1]
856 \stopformula
857}
858
859\input tufte
860
861\startformula
862 \polynomialdenominator
863 \polynomialnumerator[2]
864 \frac{3x32x25x7}{x23}
865 \alignhere
866 = 3x \frac{2x2 14x 7}{x23}
867 \breakhere
868 = 3x 2 \frac{14x1}{x23}
869\stopformula
870
871\input tufte
872
873\startformula
874 \frac{3x32x25x7}{x23}
875 \alignhere
876 = 3x \frac{2x2 14x 7}{x23}
877 \breakhere
878 = 3x 2 \frac{14x1}{x23}
879\stopformula
880
881
882
883\TestPolynomials
884 {5, 3, 3, 1, 1, 0, 2, 3, 2, 1, 4}
885 {8, 7, 5, 2, 8, 9, 1}
886
887\TestPolynomials
888 {1, 0, 1}
889 {1, 1}
890
891
892
893\TestPolynomials
894 {1, 1}
895 {1, 0, 1}
896
897
898
899\stoptext
900 |