%D The calculator %D %D This document was made in 1998 as demonstration of widgets in \CONTEXT in \MKII. %D It has been adapted to run in \MKIV. Not many changes were needed. The macro %D definitions are layout a bit more readable and we use official scratch variables. %D %D The \JAVASCRIPT\ interpeter has changes and also became more strict. So, I %D reformatted the code bit and added some more semicolons and vars. %D %D We changed to font from a Helvetica lookalike to Dejavu but kept the colors as %D in the original. We also kept the \JAVASCRIPT\ and \METAPOST\ code (although %D we had to add some initalizations for \METAPOST\ due to the runtime graphic %D generation. I didn't check the functionality. %D %D Should I do it different nowaways? For sure. I might use layers of make nicer %D \METAPOST\ code but it's also a demonstration of how thinsg were done in 1998. % acrobat 3->4 : different field initialization % acrobat 4->5 : typecasting fails [switch"2" vs 2] % acrobat 6-> : more strict interpreter \starttext \dontcomplain \setuppapersize [S6][S6] \setupwhitespace [medium] \setupbodyfont [dejavu,9pt] \setuptyping [margin=standard] \setuplayout [backspace=1cm, topspace=1cm, header=0pt, footer=0pt, width=middle, height=middle] \setupinteraction [page=yes, state=start, author=Hans Hagen, title=The Calculator, color=keyboard] \setupinteractionscreen [option=max] \definecolor [action] [r= 1, g=.9, b=.5] \definecolor [keyboard] [r= 0, g=.7, b=.7] \definecolor [stack] [r=.7, g=.6, b=.8] \useURL[pragma-mail][mailto:pragma@xs4all.nl][][pragma@xs4all.nl] %D We have to use \type {String(...)=""} otherwise zero is regarded %D as non|-|entry (empty). This is a bit fuzzy. \def\MinLevel{50} \def\MaxLevel {8} \startJSpreamble {variables} used now var Growing = false ; var MinLevel = -50 ; var MaxLevel = 8 ; var Level = 1 ; var NoErrorFound = ">ok" ; var NoValueError = ">invalid" ; var WhateverError = ">error" ; var OverflowError = ">overflow" ; var ExhaustedError = ">exhausted" ; var Stack = new Array() ; var Stats = new Array() ; // console.clear() ; // console.println("preamble loaded: variables") ; \stopJSpreamble \startJSpreamble {housekeeping} used now function do_reset_all () { if (Growing) { Level = 1 ; } else { Level = MaxLevel ; } for (var i=MinLevel ; i<=MaxLevel ; i++) { Stack[i] = "" ; } do_mark (NoErrorFound) ; } function do_refresh (i) { var vv = this.getField("Stack." + String(i)) ; if (vv) { vv.value = Stack[i] ; vv.readonly = (i != Level) ; this.dirty = false ; } } function do_refresh_all () { for (var i=1 ; i<=MaxLevel ; i++) { do_refresh(i) ; } } function do_update_A () { if (Stack[1] == "") { for (var i=1 ; i<=MaxLevel ; i++) { vv = this.getField("Stack." + String(i)) ; if (vv) { Level = i ; if (valid_number(vv.value)) { Stack[Level] = String(vv.value) } else { vv.value = "" ; this.dirty = false ; return ; } } } } } function do_update_B () { if (String(Stack[MaxLevel-1]) == "") { for (var i=1 ; i<=MaxLevel ; i++) { vv = this.getField("Stack." + String(i)) ; if (vv) { if (valid_number(vv.value)) { Stack[Level] = String(vv.value) ; } else { vv.value = "" ; this.dirty = false ; return ; } } } } } function do_update () { if (Growing) { do_update_A() ; } else { do_update_B() ; } } function do_mark (s) { var vv = this.getField("Stack." + String(Level)) ; if (vv) { vv.value = s ; this.dirty = false ; } } // console.println("preamble loaded: housekeeping") ; \stopJSpreamble \startJSpreamble {handling} used now function do_enter () { do_update() ; var vv = this.getField("Stack." + String(Level)) ; if ((vv.value!=Stack[Level]) && (String(vv.value).search(/>/)==-1)) { do_push (vv.value) ; } else { do_push (Stack[Level]) ; } } function do_push_A (Value) { do_update() ; Stack[Level] = String(Value) ; do_parse(Level) ; if (String(Stack[Level])!="") { if (LevelMinLevel ; i--) { Stack[i] = Stack[i-1] ; } Stack[MinLevel] = "" ; do_refresh_all() ; Level = MaxLevel ; } else { while ((Level>1) && (String(Stack[Level])=="")) { do_refresh(Level) ; --Level ; } } if (String(Stack[Level])=="") { return("") ; } else { Value = Number(Stack[Level]) ; Stack[Level] = "" ; do_refresh(Level) ; return(Value) ; } } function do_pop_B () { do_update() ; if (String(Stack[MaxLevel])=="") { for (var i=MaxLevel ; i>MinLevel ; i--) { Stack[i] = Stack[i-1] ; } Stack[MinLevel] = "" ; do_refresh_all() ; } if (String(Stack[MaxLevel])=="") { return("") ; } else { Value = Number(Stack[MaxLevel]) ; Stack[MaxLevel] = "" ; return(Value) ; } } function do_pop () { if (Growing) { return(do_pop_A()) ; } else { return(do_pop_B()) ; } } function do_dup () { var X = do_pop() ; if (valid_number(X)) { do_push(X) ; do_push(X) ; } else { do_mark (ErrorString) ; } } function do_parse ( i ) { if (valid_number(Stack[i])) { Stack[i] = parseFloat(Stack[i]) ; do_refresh(i) ; } else { Stack[i] = "" ; do_refresh(i) ; do_mark (ErrorString) ; } } function do_digit ( d ) { Stack[Level] += String(d) ; do_refresh(Level) ; } function valid_number ( x ) { if (String(x) == "") { ErrorString = NoValueError ; return(false) ; } else if (isNaN(x)) { ErrorString = NoValueError ; return(false) ; } else if (isFinite(x)) { ErrorString = NoErrorFound ; return(true) ; } else { ErrorString = OverflowError ; return(false) ; } } // console.println("preamble loaded: handling") ; \stopJSpreamble \startJSpreamble {operations} used now function do_calculate (Operator) { do_enter() ; var Y = do_pop() ; var X = 0 ; if (valid_number(Y)) { X = do_pop() ; if (valid_number(X)) { switch (Number(Operator)) { case 1 : if (Y) { X /= Y ; } break ; case 2 : X *= Y ; break ; case 3 : X -= Y ; break ; case 4 : X += Y ; break ; case 5 : X = Math.max(X,Y) ; break ; case 6 : X = Math.min(X,Y) ; break ; case 7 : X = Math.pow(X,Y) ; break } do_push(X) ; } else { do_push(Y) ; ErrorString = NoValueError ; } } do_mark(ErrorString) ; } // console.println("preamble loaded: operations") ; \stopJSpreamble \startJSpreamble {functions} used now function do_facultate (Value) { var n = Math.min(Math.round(Value),500) ; if (n <= 1) { return(1) ; } else { var m = 1 ; for (m=1, i=1 ; i<=n ; i++) { m = m * i ; } return(m) ; } } function do_constant (Operation) { do_update() ; switch (Number(Operation)) { case 1 : do_push(Math.PI) ; break ; case 2 : do_push(Math.E) ; break ; case 3 : do_push(Math.random(1)) ; break ; case 4 : do_dup() ; break ; } } function do_do_memory (mv, Sign) { var X = do_pop() ; if (valid_number(X)) { mv.value += Sign*X ; if (!valid_number(mv.value)) { mv.value = "" ; } this.dirty = false ; } do_mark(ErrorString) ; } function do_memory (Operation) { var mv = this.getField("Stats.mem") ; if (mv) { switch (Number(Operation)) { case 1 : mv.value = "" ; break ; case 2 : do_do_memory(mv, 1) ; break ; case 3 : do_do_memory(mv, -1) ; break ; case 4 : if (mv.value == "") { ErrorString = NoValueError ; do_mark(ErrorString) ; } else { do_push(mv.value) ; } break ; } this.dirty = false ; } } function do_operation (Operator) { do_enter() ; var X = do_pop() ; if (valid_number(X)) { switch (Number(Operator)) { case 1 : do_push(Math.sin(X)) ; break ; case 2 : do_push(Math.cos(X)) ; break ; case 3 : do_push(Math.tan(X)) ; break ; case 4 : do_push(Math.exp(X)) ; break ; case 5 : do_push(Math.ceil(X)) ; break ; case 6 : do_push(Math.pow(X,2)) ; break ; case 7 : do_push(do_facultate(X)) ; break ; case 8 : do_push(Math.asin(X)) ; break ; case 9 : do_push(Math.acos(X)) ; break ; case 10 : do_push(Math.atan(X)) ; break ; case 11 : do_push(Math.log(X)) ; break ; case 12 : do_push(Math.floor(X)) ; break ; case 13 : do_push(Math.sqrt(X)) ; break ; case 14 : do_push(Math.round(X)) ; break ; case 15 : do_push(Math.pow(X,-1)) ; break ; case 16 : do_push(X*(2*Math.PI/360)) ; break ; case 17 : do_push(X/(2*Math.PI/360)) ; break ; case 18 : do_push(-X) ; break ; } } do_mark(ErrorString) ; } // console.println("preamble loaded: functions") ; \stopJSpreamble \startJSpreamble {statistics} used now var s_min_max = 1 ; var s_sqrt = 0 ; function do_statcalcs (Sign, X) { s_n.value += Sign ; s_sqrt += Sign*X*X ; s_total.value += Sign*X ; s_mean.value = s_total.value / s_n.value ; s_temp = Math.max(s_sqrt - 2*s_total.value*s_mean.value + s_n.value*s_mean.value*s_mean.value, 0) ; s_sdev.value = Math.sqrt(s_temp/s_n.value) ; if (!(valid_number(X) && valid_number(s_sqrt) && valid_number(s_sdev.value) && valid_number(s_total.value) && valid_number(s_mean.value))) { do_statistics(1) ; } this.dirty = false ; } function do_statistics(Operator) { var s_n = this.getField("Stats.n") ; var s_min = this.getField("Stats.min") ; var s_max = this.getField("Stats.max") ; var s_total = this.getField("Stats.total") ; var s_mean = this.getField("Stats.mean") ; var s_sdev = this.getField("Stats.sdev") ; if ((s_sqrt==0) && (s_n.value!="") && (s_n.value>0)) { s_min_max = 1 ; s_sqrt = s_n.value*s_sdev.value*s_sdev.value + 2*s_total.value*s_mean.value - s_n.value*s_mean.value*s_mean.value ; } switch (Number(Operator)) { case 1 : s_min_max = 1 ; s_n.value = "" ; s_min.value = "" ; s_max.value = "" ; s_total.value = "" ; s_mean.value = "" ; s_sdev.value = "" ; s_sqrt = 0 ; break ; case 2 : do_enter() ; var X = do_pop() ; if (valid_number(X)) { if (s_n.value=="") { s_n.value = 0 ; s_total.value = 0 ; s_mean.value = 0 ; if (s_min_max) { s_min.value = X ; s_max.value = X ; } } else { if ((s_min_max) && (Xs_max.value)) { s_max.value = X ; } } do_statcalcs(1,X) ; } break ; case 3 : do_enter() ; var X = do_pop() ; if (valid_number(X)) { s_min.value = "" ; s_max.value = "" ; s_min_max = 0 ; if (s_n.value>0) { do_statcalcs(-1,X) ; } else { do_statistics(1) ; } } break ; } do_mark(ErrorString) ; this.dirty = false ; } function do_copystat (Field) { var sv = this.getField("Stats.".concat(Field)) ; do_push(sv.value) ; } // console.println("preamble loaded: statistics") ; \stopJSpreamble \startJSpreamble {initialization} used now do_reset_all() ; do_update() ; do_refresh_all() ; do_mark (NoErrorFound) ; // console.println("preamble loaded: initialization") ; \stopJSpreamble %D We could use functions instead but this demonstrates inline %D code. We also could use predefined references. \startJScode{period} if (Stack[Level].search(/[.|e]/)==-1) { do_digit(".") ; } \stopJScode \startJScode{sign} L = Stack[Level].length ; if ((L==0) || (Stack[Level].charAt(L-1)=="e")) { do_digit("-") ; } \stopJScode \startJScode{exponent} L = Stack[Level].length ; if ((L>0) && valid_number(Stack[Level]) && (Stack[Level].search(/[e]/)==-1)) { do_digit("e") ; } \stopJScode \startJScode{reset} do_reset_all() ; do_refresh_all() ; do_mark (NoErrorFound) ; \stopJScode \startJScode{clear} do_update() ; Stack[Level] = String(Stack[Level]).substring(0,String(Stack[Level]).length-1) ; do_refresh(Level) ; \stopJScode \startJScode{pop} X = do_pop() ; do_mark(NoErrorFound) ; \stopJScode \startJScode{push} do_enter() ; \stopJScode \startJScode{grow} Growing = !Growing ; do_reset_all() ; do_refresh_all() ; do_mark (NoErrorFound) ; \stopJScode % graphics \startuniqueMPgraphic{page} fill OverlayBox withcolor .4white ; draw OverlayBox enlarged -10pt withpen pencircle scaled 5pt withcolor .8white ; \stopuniqueMPgraphic \startuniqueMPgraphic{shape} path p ; color c ; p := OverlayBox ; c := \overlaycolor ; fill p withcolor c ; draw p withpen pencircle scaled 1.5pt withcolor .8c ; \stopuniqueMPgraphic \defineoverlay [page] [\uniqueMPgraphic{page}] \defineoverlay [shape] [\uniqueMPgraphic{shape}] \setupbackgrounds [page] [background=page] \definemeasure[ButtonWidth][\makeupwidth/15] \setupbuttons [width=\measured{ButtonWidth}, height=\measured{ButtonWidth}, background=shape, backgroundcolor=\MPcolor{keyboard}, frame=off, style=, color=] \setupfield [Results] [horizontal,frame] [width=fit, align={lohi}, height=.5\measured{ButtonWidth}, background=shape, backgroundcolor=\MPcolor{stack}, frame=off] [width=3.5\measured{ButtonWidth}, frame=off] [width=3.5\measured{ButtonWidth}, height=.45\measured{ButtonWidth}, option=readonly, frame=off] \starttexdefinition unexpanded Star \lower.65ex\hbox { * } \stoptexdefinition \starttexdefinition unexpanded InfoButton #1 \setbox\scratchbox\hbox { \lower .5cm \hbox { \button [ width=\dimexpr\overlaywidth + .5cm\relax, height=\dimexpr\overlayheight + .5cm\relax, strut=no, frame=off, background= ] { } [ info:#1 ] } } \wd\scratchbox\overlaywidth \ht\scratchbox\overlayheight \box\scratchbox \stoptexdefinition \starttexdefinition unexpanded SomeKey #1#2#3 \bgroup \doifelsenothing {#2} { \button[background=]{#2}[#3] } { \defineoverlay [infobutton] [\InfoButton{#1}] % \button % [background={infobutton,shape}] % {#2} % [#3] \framed [offset=overlay, frame=off, background=infobutton, backgroundcolor=red] {\button{#2}[#3]} } \egroup \ignorespaces \stoptexdefinition \starttexdefinition unexpanded StatField #1 \hbox \bgroup \doifelsenothing {#1} { \framed [ height=.5\measured{ButtonWidth}, width=3.5\measured{ButtonWidth}, frame=off ] { } } { \definefield[Stats.#1][line][Results] \field[Stats.#1][option=readonly] } \egroup \ignorespaces \stoptexdefinition \setbox\scratchboxone=\hbox to .4\makeupwidth \bgroup \ss \SomeKey {7} {\ssb 7} {JS(do_digit{7})} \hss \SomeKey {8} {\ssb 8} {JS(do_digit{8})} \hss \SomeKey {9} {\ssb 9} {JS(do_digit{9})} \hss \SomeKey {div} {\ssb /} {JS(do_calculate{1})} \hss \SomeKey {del} {del} {JS(clear)} \egroup \setbox\scratchboxtwo=\hbox to \wd\scratchboxone \bgroup \ss \SomeKey {4} {\ssb 4} {JS(do_digit{4})} \hss \SomeKey {5} {\ssb 5} {JS(do_digit{5})} \hss \SomeKey {6} {\ssb 6} {JS(do_digit{6})} \hss \SomeKey {mul} {\ssb \Star} {JS(do_calculate{2})} \hss \SomeKey {E} {E} {JS(exponent)} \egroup \setbox\scratchboxthree=\hbox to \wd\scratchboxone \bgroup \ss \SomeKey {1} {\ssb 1} {JS(do_digit{1})} \hss \SomeKey {2} {\ssb 2} {JS(do_digit{2})} \hss \SomeKey {3} {\ssb 3} {JS(do_digit{3})} \hss \SomeKey {sub} {\ssb --} {JS(do_calculate{3})} \hss \SomeKey {pop} {pop} {JS(pop)} \egroup \setbox\scratchboxfour=\hbox to \wd\scratchboxone \bgroup \ss \SomeKey {0} {\ssb 0} {JS(do_digit{0})} \hss \SomeKey {period} {\ssb .} {JS(period)} \hss \SomeKey {sign} {\ssb -} {JS(sign)} \hss \SomeKey {add} {\ssb +} {JS(do_calculate{4})} \hss \SomeKey {push} {push} {JS(push)} \egroup \setbox\scratchboxone=\vbox to .8\wd\scratchboxone \bgroup \box\scratchboxone \vss \box\scratchboxtwo \vss \box\scratchboxthree\vss \box\scratchboxfour \egroup \setbox\scratchboxtwo=\vbox to \ht\scratchboxone \bgroup \dostepwiserecurse {\MaxLevel} {1} {-1} { \definefield[Stack.\recurselevel][line][Results] \hbox \bgroup \field[Stack.\recurselevel][option=readonly]% \egroup \vfill } \unskip \egroup \setbox\scratchboxthree=\vbox to \ht\scratchboxone \bgroup \StatField {n} \vfill \StatField {min} \vfill \StatField {max} \vfill \StatField {total} \vfill \StatField {mean} \vfill \StatField {sdev} \vfill \StatField {} \vfill \StatField {} \egroup \setbox\scratchboxfour=\vbox to \ht\scratchboxone \bgroup \ss\setstrut \setupbuttons [width=\measured{ButtonWidth}, height=.5\measured{ButtonWidth}, backgroundcolor=\MPcolor{action}]% \SomeKey {sn} {n} {JS(do_copystat{n})} \vfill \SomeKey {smin} {min} {JS(do_copystat{min})} \vfill \SomeKey {smax} {max} {JS(do_copystat{max})} \vfill \SomeKey {stotal} {total} {JS(do_copystat{total})} \vfill \SomeKey {smean} {mean} {JS(do_copystat{mean})} \vfill \SomeKey {ssdev} {sdev} {JS(do_copystat{sdev})} \vfill \SomeKey {} {} {} \vfill \SomeKey {} {} {} \egroup \setbox\scratchboxone=\hbox to \hsize \bgroup \box\scratchboxone \hss \box\scratchboxtwo \hss \box\scratchboxthree\hss \box\scratchboxfour \egroup \setupbuttons [width=1.5cm, height=1cm, backgroundcolor=\MPcolor{action}] \setbox\scratchboxtwo=\hbox to \wd\scratchboxone \bgroup \ss\setstrut \SomeKey {sin} {sin} {JS(do_operation{1})} \hss \SomeKey {cos} {cos} {JS(do_operation{2})} \hss \SomeKey {tan} {tan} {JS(do_operation{3})} \hss \SomeKey {max} {max} {JS(do_calculate{5})} \hss \SomeKey {exp} {exp} {JS(do_operation{4})} \hss \SomeKey {ceil} {ceil} {JS(do_operation{5})} \hss \SomeKey {sqr} {x\high{2}} {JS(do_operation{6})} \hss \SomeKey {fac} {x!} {JS(do_operation{7})} \hss \SomeKey {pow} {x\high{y}} {JS(do_calculate{7})} \hss \SomeKey {rad} {rad} {JS(do_operation{16})} \egroup \setbox\scratchboxthree=\hbox to \wd\scratchboxone \bgroup \ss\setstrut \SomeKey {asin} {asin} {JS(do_operation{8})} \hss \SomeKey {acos} {acos} {JS(do_operation{9})} \hss \SomeKey {atan} {atan} {JS(do_operation{10})} \hss \SomeKey {min} {min} {JS(do_calculate{6})} \hss \SomeKey {ln} {ln} {JS(do_operation{11})} \hss \SomeKey {floor}{floor} {JS(do_operation{12})} \hss \SomeKey {sqrt} {sqrt} {JS(do_operation{13})} \hss \SomeKey {round}{round} {JS(do_operation{14})} \hss \SomeKey {inv} {1/x} {JS(do_operation{15})} \hss \SomeKey {deg} {deg} {JS(do_operation{17})} \egroup \setbox\scratchboxfour=\hbox to \wd\scratchboxone \bgroup \ss\setstrut \SomeKey {} {} {} \hss \SomeKey {} {} {} \hss \setupbuttons [backgroundcolor=\MPcolor{keyboard}]% \SomeKey {new} {new} {JS(reset)} \hss \SomeKey {} {} {} \hss \SomeKey {} {} {} \hss \SomeKey {} {} {} \hss \SomeKey {} {} {} \hss \SomeKey {newn} {new} {JS(do_statistics{1})} \hss \SomeKey {addn} {+n} {JS(do_statistics{2})} \hss \SomeKey {subn} {--n} {JS(do_statistics{3})} \egroup \setbox\scratchboxfive=\hbox to \wd\scratchboxone \bgroup \ss\setstrut \SomeKey {neg} {--x} {JS(do_operation{18})} \hss \SomeKey {random}{random} {JS(do_constant{3})} \hss \SomeKey {pi} {pi} {JS(do_constant{1})} \hss \SomeKey {e} {e} {JS(do_constant{2})} \hss \SomeKey {dup} {dup} {JS(do_constant{4})} \hss \SomeKey {} {} {} \hss \SomeKey {} {} {} \hss \setupbuttons [backgroundcolor=\MPcolor{keyboard}]% \SomeKey {} {} {} \hss \SomeKey {exit} {exit} {CloseDocument} \hss \SomeKey {help} {info} {info:help} \egroup \setbox\scratchboxsix=\hbox to \wd\scratchboxone \bgroup \ss\setstrut \setupbuttons [width=\measured{ButtonWidth}, height=.5\measured{ButtonWidth}, backgroundcolor=\MPcolor{keyboard}]% \SomeKey {newmem} {new} {JS(do_memory{1})} \hss \SomeKey {addmem} {+m} {JS(do_memory{2})} \hss \SomeKey {submem} {--m} {JS(do_memory{3})} \hss \StatField {mem} \hss \setupbuttons [backgroundcolor=\MPcolor{action}]% \SomeKey {copmem} {mem} {JS(do_memory{4})} \hss \SomeKey {} {} {} \hss \setupbuttons [backgroundcolor=\MPcolor{stack}]% \SomeKey {grow} {grow} {JS(grow)} \hss \StatField {} \egroup \startstandardmakeup \pagereference[calculator] \vfill \hbox to \hsize \bgroup \hss \vbox to \vsize \bgroup \box\scratchboxtwo \vss \box\scratchboxthree\vss \box\scratchboxone \vss \box\scratchboxfour \vss \box\scratchboxfive \vss \box\scratchboxsix \egroup \hss \egroup \vfill \stopstandardmakeup \starttexdefinition unexpanded BackgroundButton \button [ background=screen, backgroundscreen=.8, backgroundoffset=5pt, height=\vsize, width=\hsize ] { } [ calculator ] \stoptexdefinition \setuptexttexts [\BackgroundButton] [] \starttexdefinition unexpanded Key #1#2 \goto { \ss#1 } [ info:#2 ] \stoptexdefinition \startstandardmakeup[top=,bottom=] \switchtobodyfont[8pt] \start \setupwhitespace[big] \midaligned{\ssd The Calculator} \blank[2*big] \pagereference[info:help] This calculator is stack based, which means that one enters values and invokes an action that acts on the value(s) last entered. Subtracting 10 from 20 using (\Key {--} {sub}) for instance comes down to clicking: \startnarrower\ss 10\quad in\quad 20\quad -- \stopnarrower while calculating a sinus (\Key {sin} {sin}) results from entering: \startnarrower\ss .89\quad sin \stopnarrower The left column of fields (numbers) shows the Stack. One uses \Key {push} {push} to push a value on the stack and \Key {pop} {pop} to remove a value. Clicking \Key {new} {new} removes them all and the \Key {del} {del} button can be used to undo the last entered digit. When a dyadic operation is applied, the top value is used as~y. The \Key {grow} {grow} key toggles between two different visualizations of the stack. The stack is considerably larger than the screen representation suggests. In the rare occasion that one encounters the message {\ss exhausted}, the amount of stack entries already has totaled far beyond \MinLevel\ and one probably already has forgotten what the values first entered represent. The right column of fields reports the statistic calculations. By clicking on the tag, one pushes the value on the Stack. The lower buttons are used to reset~(\Key {new} {newn}), enter~(\Key {+} {addn}) and remove~(\Key {--} {subn}) values to be taken into account when calculating those statistics. This document is produced by \ConTeXt, a macro package written in \TeX. The graphics are METAPOST graphics. The graphics, the PDF objects and the form fields as well as JavaScript code were generated and inserted at run time. Originally we used PDF\TeX\ and \MKII\ to process this document but this one is done by \LUATEX\ and \MKIV. We kept the design and code original so that it reflects how things were done (for readability we updated some \TEX\ definitions). \stop \vfilll \startMPrun logo_type := 302 ; % force single logo type mpgraph := 302 ; % and use this number input mp-prag ; % calculate logo of type \stopMPrun \startlinecorrection \midaligned{\externalfigure[mprun.302][height=1.5cm]} \stoplinecorrection \midaligned{\strut Hans Hagen, PRAGMA ADE, \ConTeXt\ 18/2/1998--25/9/2018} \blank \midaligned{\strut\url[pragma-mail]} \stopstandardmakeup \starttexdefinition unexpanded BackgroundButton \button [ background=screen, backgroundscreen=.8, backgroundoffset=5pt, height=\vsize, width=\hsize ] { } [ firstpage ] \stoptexdefinition \starttexdefinition unexpanded ShowInfo #1#2 \startstandardmakeup \pagereference[info:#1] \vfill \hbox to \hsize \bgroup \hss \useMPgraphic{#1} \hss \egroup \blank[2*big] \midaligned{#2} \vfill \stopstandardmakeup \stoptexdefinition \startMPinclusions path ax, ay, p[] ; color c ; c := \MPcolor{action} ; pmax := 0 ; let normalpow = pow ; def draw_function (text fun) (expr xmin, xmax, xstep) = pmax := pmax+1 ; p[pmax] := for x=xmin step xstep until xmax: (x,fun(x)) .. endfor (xmax,fun(xmax)) ; enddef ; def draw_axis = % should sort of snap, to-do pickup pencircle scaled 0 ; for i=1 upto pmax: draw p[i] ; endfor ; xmin := xpart llcorner currentpicture ; xmax := xpart urcorner currentpicture ; ymin := ypart llcorner currentpicture ; ymax := ypart urcorner currentpicture ; ax := (xmin,0)--(0,0)--(xmax,0) ; ay := (0,ymin)--(0,0)--(0,ymax) ; currentpicture := nullpicture ; sx := 400/(xmax-xmin) ; sy := 250/(ymax-ymin) ; pickup pencircle xscaled (10/sx) yscaled (10/sy) ; draw ax withcolor .4white ; draw ay withcolor .4white ; for i=1 upto pmax: draw p[i] withcolor c ; endfor ; currentpicture := currentpicture xscaled sx yscaled sy ; pmax := 0 ; enddef ; \stopMPinclusions \startuseMPgraphic{sin} draw_function(sind)(-360,360,60) ; draw_axis ; \stopuseMPgraphic \ShowInfo{sin}{Calculate the sine of the topmost stack entry.} \startuseMPgraphic{cos} draw_function(cosd)(-360,360,60) ; draw_axis ; \stopuseMPgraphic \ShowInfo{cos}{Calculate the cosine of the topmost stack entry.} \startuseMPgraphic{tan} draw_function(tand)(-240,-120,30) ; draw_function(tand)( -60, 60,30) ; draw_function(tand)( 120, 240,30) ; draw_axis ; \stopuseMPgraphic \ShowInfo{tan}{Calculate the tangent of the topmost stack entry.} \startuseMPgraphic{asin} draw_function(asin)(-1,1,.2) ; draw_axis ; \stopuseMPgraphic \ShowInfo{asin}{Calculate the arcsine of the topmost stack entry.} \startuseMPgraphic{acos} draw_function(acos)(-1,1,.2) ; draw_axis ; \stopuseMPgraphic \ShowInfo{acos}{Calculate the arccosine of the topmost stack entry.} \startuseMPgraphic{atan} draw_function(atan)(-1,1,.2) ; draw_axis ; \stopuseMPgraphic \ShowInfo{atan}{Calculate the arctangent of the topmost stack entry.} \startuseMPgraphic{sqr} draw_function(sqr)(-3,3,1) ; draw_axis ; \stopuseMPgraphic \ShowInfo{sqr}{Calculate the square of the topmost stack entry.} \startuseMPgraphic{sqrt} draw_function(sqrt)(0,5,1) ; draw_axis ; \stopuseMPgraphic \ShowInfo{sqrt}{Calculate the square root of the topmost stack entry.} \startuseMPgraphic{exp} draw_function(exp)(0,5,1) ; draw_axis ; \stopuseMPgraphic \ShowInfo{exp}{Calculate the exponential function of the topmost stack entry.} \startuseMPgraphic{ln} draw_function(ln)(0,50,5) ; draw_axis ; \stopuseMPgraphic \ShowInfo{ln}{Calculate the natural logaritm of the topmost stack entry.} \startuseMPgraphic{pow} vardef mypow(expr n) = 3**n enddef ; draw_function(mypow)(-3,3,.5) ; draw_axis ; \stopuseMPgraphic \ShowInfo{pow}{Calculate x\high{y} where y is the topmost stack entry.} \startuseMPgraphic{inv} draw_function(inv)(-10,10,1) ; draw_axis ; \stopuseMPgraphic \ShowInfo{inv}{Calculate 1/x using the topmost stack entry.} \startMPinclusions def draw_statistics (expr ShowNew, ShowAdd, ShowSubtract, ShowN, ShowSum, ShowMin, ShowMax, ShowMean, ShowSdev) = color c ; c := \MPcolor{action} ; Delta := 20 ; Total := 100 ; Range := 24 ; Sum := 0 ; Sqr := 0 ; randomseed := .5 ; pickup pencircle scaled .5Delta ; for r := 0 upto Range: Value[r] := 0 ; endfor ; for i=1 upto Total: r := uniformdeviate 1 ; Sum := Sum + r ; Sqr := Sqr + r*r ; r := round(r*Range) ; Value[r] := Value[r]+1 ; endfor ; Mean := Sum/Total ; Sdev := sqrt((Sqr-2*Sum*Mean+Total*Mean*Mean)/Total) ; Mean := Mean*Range ; Sdev := Sdev*Range ; SdevMin := Mean-Sdev ; SdevMax := Mean+Sdev ; for r := 0 upto Range: draw (r*Delta,0)--(r*Delta,Value[r]*Delta) withcolor if (ShowSdev and (r>SdevMin) and (r4: .8ca else: .8cb fi ; else: fill p withcolor .4white ; fi ; draw p withcolor ca ; endfor ; currentpicture := currentpicture scaled 1.5 ; enddef ; \stopMPinclusions \startuseMPgraphic{push} draw_stack (false, true, false, false) \stopuseMPgraphic \ShowInfo{push}{Push a new entry to the stack.} \startuseMPgraphic{pop} draw_stack (false, false, true, false) \stopuseMPgraphic \ShowInfo{pop}{Remove the topmost entry from the stack.} \startuseMPgraphic{new} draw_stack (true, false, false, false) \stopuseMPgraphic \ShowInfo{new}{Erase the whole stack.} \startuseMPgraphic{dup} draw_stack (false, false, false, true) \stopuseMPgraphic \ShowInfo{dup}{Duplicate the topmost stack entry.} \startMPinclusions def draw_funcalc (expr p, q, r, action) = color b ; b := \MPcolor{keyboard} ; color c ; c := \MPcolor{stack} ; draw ((-2.25max(p,q,r))-1,0)--(-1,0) withcolor .4white ; pickup pencircle scaled 2 ; for i=1 upto p: draw (-i*2.25, 3.75) withcolor c ; endfor ; for i=1 upto q: draw (-i*2.25, 1.50) withcolor c ; endfor ; for i=1 upto r: draw (-i*2.25,-1.50) withcolor c ; endfor ; pickup pencircle scaled .5 ; push_boundingbox currentpicture ; pair w ; w := (1.125,0) ; path ww ; ww := -w -- w ; if action=1: draw ww shifted w withcolor b ; draw ww rotated 90 shifted w withcolor b ; elseif action=2: draw ww shifted w withcolor b ; elseif action=3: draw ww rotated 45 shifted w withcolor b ; draw ww rotated 135 shifted w withcolor b ; elseif action=4: draw ww rotated 45 shifted w withcolor b ; fi ; pop_boundingbox currentpicture ; currentpicture := currentpicture scaled 15 ; enddef ; \stopMPinclusions \startuseMPgraphic{add} draw_funcalc (6, 6, 7, 1) \stopuseMPgraphic \ShowInfo{add}{Add the two topmost stack entries.} \startuseMPgraphic{sub} draw_funcalc (5, 4, 5, 2) \stopuseMPgraphic \ShowInfo{sub}{Subtract the topmost stack entry from the one below.} \startuseMPgraphic{mul} draw_funcalc (3, 4, 7, 3) \stopuseMPgraphic \ShowInfo{mul}{Multiply the two topmost stack entries.} \startuseMPgraphic{div} draw_funcalc (5, 2, 4, 4) \stopuseMPgraphic \ShowInfo{div}{Divide the pre-last stack entry by the topmost one.} \startuseMPgraphic{grow} pickup pencircle scaled 5 ; ahlength := 10 ; path p ; p := (0,0)--(60,0) ; path q ; q := (30,2*15)--(30,7*15) ; path r ; r := (30,0)--(30,7*15) ; for i=0 upto 7: draw p shifted (0,i*15) withcolor if i<3: \MPcolor{stack} else: .4white fi ; endfor ; addto currentpicture also currentpicture rotatedaround (center currentpicture, 180) shifted (90, 0) ; drawarrow q withcolor \MPcolor{keyboard} ; drawarrow reverse r shifted (90, 0) withcolor \MPcolor{keyboard} ; currentpicture := currentpicture scaled 2 ; \stopuseMPgraphic \ShowInfo{grow}{Toggle grow mode, another way of stacking.} \startuseMPgraphic{exit} path p ; p := (100,30)--(100,0)--(0,0)--(0,100)--(100,100)--(100,70) ; pickup pencircle scaled 10 ; linecap := butt ; ahlength := 60 ; ahangle := 75 ; filldraw p--cycle withcolor \MPcolor{action} ; draw p withcolor \MPcolor{stack} ; push_boundingbox currentpicture ; pickup pencircle scaled 30 ; draw center currentpicture -- (xpart lrcorner currentpicture+.75ahlength, ypart center currentpicture) withcolor \MPcolor{keyboard} ; pickup pencircle scaled 5 ; drawarrow center currentpicture -- (xpart lrcorner currentpicture, ypart center currentpicture) withcolor \MPcolor{keyboard} ; pop_boundingbox currentpicture ; currentpicture := currentpicture scaled 1.75 ; \stopuseMPgraphic \ShowInfo{exit}{Close this document.} \stoptext