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
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770\startformula
771\polynomial
772 [color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange}}]
773 [7, 5, 2, 3, 2]
774 [3, 0, 1]
775\stopformula
776
777\polynomial
778 [alternative=none,
779 color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange,ds=darkgray}}]
780 [7, 5, 2, 3, 2]
781 [3, 0, 1]
782
783numerator
784
785\startformula
786 \polynomialnumerator
787\stopformula
788
789numerator 2
790
791\startformula
792 \polynomialnumerator[2]
793\stopformula
794
795denominator
796
797\startformula
798 \polynomialdenominator
799\stopformula
800
801quotient 123
802
803\startformula
804 \polynomialquotient[1]
805 \polynomialquotient[2]
806 \polynomialquotient[3]
807\stopformula
808
809quotientstep 123
810
811\startformula
812 \polynomialquotientstep[1]
813 \polynomialquotientstep[2]
814 \polynomialquotientstep[3]
815\stopformula
816
817steps
818
819\startformula
820 \polynomialsteps
821\stopformula
822
823factor
824
825\startformula
826 \polynomialfactor[2]
827\stopformula
828a
829shift
830
831\startformula
832 \polynomialshift[2]
833\stopformula
834
835
836\startformula
837 \frac {\polynomialnumerator}{\polynomialdenominator}
838 =
839 \polynomialquotient[1]
840
841 \frac {\polynomialnumerator[1]}{\polynomialdenominator}
842\stopformula
843
844\startformula
845\polynomial
846 [alternative=complete,
847 color={1={n=red,d=green,q=blue},2={n=cyan,d=magenta,q=orange,ds=darkgray}}]
848 [7, 5, 2, 3, 2]
849 [3, 0, 1]
850\stopformula
851
852
853
854\dorecurse\polynomialsteps{
855 \startformula
856 \polynomialstep[#1]
857 \stopformula
858}
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900\stoptext
901 |