You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: dpvc v. a. <we...@ma...> - 2007-08-31 13:47:23
|
Log Message: ----------- Add the PGcourse.pl file, since we are encouraging authors to include this in theie loadMacros() calls. Added Files: ----------- pg/macros: PGcourse.pl Revision Data ------------- --- /dev/null +++ macros/PGcourse.pl @@ -0,0 +1,12 @@ + +=head1 PGcourse.pl + + # + # Do course-specific initialization here. + # (E.g. loading source.pl to get "show source" buttons + # for example courses, and so on). + # + +=cut + +1; |
From: dpvc v. a. <we...@ma...> - 2007-08-31 13:46:39
|
Log Message: ----------- Fixed POD documentation formatting. Modified Files: -------------- pg/macros: contextPiecewiseFunction.pl Revision Data ------------- Index: contextPiecewiseFunction.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextPiecewiseFunction.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Lmacros/contextPiecewiseFunction.pl -Lmacros/contextPiecewiseFunction.pl -u -r1.2 -r1.3 --- macros/contextPiecewiseFunction.pl +++ macros/contextPiecewiseFunction.pl @@ -5,59 +5,59 @@ =head1 Context("PiecewiseFunction"); -###################################################################### -# -# This file implements a context in which piecewise-defined functions -# can be specified by students and problem authors. To use it, add -# -# loadMacros("contextPiecewieFunction.pl"); -# -# and then use -# -# Context("PiecewiseFuntion"); -# -# to select the context for piecewise functions. There are several -# ways to produce a piecewise function. For example: -# -# $f = Compute("x if x >= 0 else -x"); -# $f = Compute("x if x >= 0 else -x if x < 0"); -# $f = Formula("x+1 if x > 2 else 4 if x = 2 else 1-x"); -# $f = PiecewiseFunction("x^2 if 1 < x <= 2 else 2x+1"); -# $f = PiecewiseFunction("1 < x <= 2" => "x^2", "2x+1"); -# $f = PiecewiseFunction("(1,2]" => "x^2", "2x+1"); -# $f = PiecewiseFunction(Interval("(1,2]") => "x^2", "2x+1"); -# -# You can use either Compute() or Formula() interchangeably to -# convert a string containing "if" and "else" to the corresponding -# PiecewiseFunction. The PiecewiseFunction() constructor can -# also do this, or you can pass it a list of interval=>formula -# pairs that specify the various branches. If there is an -# unpaired final formula, it represents the "otherwise" portion -# of the function (the formula to use of the input is not in -# any of the given intervals). -# -# Note that you can use Inveral, Set, or Union objects in place of -# the intervals in the specification of a piecewise function. -# -# The PiecewiseFunction object TeXifies using a LaTeX "cases" -# environment, so you can use these objects to produce nice -# output even if you are not asking a student to enter one. -# For example: -# -# Context("PiecewiseFunction"); -# -# $f = Formula("1-x if x > 0 else 4 if x = 0 else 1+x if x < 0"); -# $a = random(-2,2,.1); -# -# Context()->texStrings; -# BEGIN_TEXT -# Suppose \(f(x)=$f\). Then \(f($a)\) = \{ans_rule(20)\}. -# END_TEXT -# Context()->normalStrings; -# -# ANS($f->eval(x=>$a)->cmp); -# -###################################################################### + ###################################################################### + # + # This file implements a context in which piecewise-defined functions + # can be specified by students and problem authors. To use it, add + # + # loadMacros("contextPiecewieFunction.pl"); + # + # and then use + # + # Context("PiecewiseFuntion"); + # + # to select the context for piecewise functions. There are several + # ways to produce a piecewise function. For example: + # + # $f = Compute("x if x >= 0 else -x"); + # $f = Compute("x if x >= 0 else -x if x < 0"); + # $f = Formula("x+1 if x > 2 else 4 if x = 2 else 1-x"); + # $f = PiecewiseFunction("x^2 if 1 < x <= 2 else 2x+1"); + # $f = PiecewiseFunction("1 < x <= 2" => "x^2", "2x+1"); + # $f = PiecewiseFunction("(1,2]" => "x^2", "2x+1"); + # $f = PiecewiseFunction(Interval("(1,2]") => "x^2", "2x+1"); + # + # You can use either Compute() or Formula() interchangeably to + # convert a string containing "if" and "else" to the corresponding + # PiecewiseFunction. The PiecewiseFunction() constructor can + # also do this, or you can pass it a list of interval=>formula + # pairs that specify the various branches. If there is an + # unpaired final formula, it represents the "otherwise" portion + # of the function (the formula to use of the input is not in + # any of the given intervals). + # + # Note that you can use Inveral, Set, or Union objects in place of + # the intervals in the specification of a piecewise function. + # + # The PiecewiseFunction object TeXifies using a LaTeX "cases" + # environment, so you can use these objects to produce nice + # output even if you are not asking a student to enter one. + # For example: + # + # Context("PiecewiseFunction"); + # + # $f = Formula("1-x if x > 0 else 4 if x = 0 else 1+x if x < 0"); + # $a = random(-2,2,.1); + # + # Context()->texStrings; + # BEGIN_TEXT + # Suppose \(f(x)=$f\). Then \(f($a)\) = \{ans_rule(20)\}. + # END_TEXT + # Context()->normalStrings; + # + # ANS($f->eval(x=>$a)->cmp); + # + ###################################################################### =cut |
From: dpvc v. a. <we...@ma...> - 2007-08-30 13:14:21
|
Log Message: ----------- Update to version 3.4d of jsMath. * Fixed a problem with easy/load.js where jsMath could fail to run when easy/load.js is loaded at the top of the BODY of the document (rather than the HEAD) and autoload is specified but tex2math is not needed (i.e., autoload is set to 1, while processSlashParens, processSlashBrackets, processDoubleDollars, and processSingleDollars, are all set to 0 and customDelimiters is commented out). * Fixed a bug where easy/load.js could run its onload handler more than once in some browsers. * Fixed a bug in the autoload plugin where jsMath.js could be loaded twice if a call to jsMath.Autoload.ReCheck() is made while the first copy of jsMath.js is in the process of being downloaded. * Fixed a subtle timing issue with jsMath.Process() when some mathematics on the page requires an external file to be autoloaded and there are additional jsMath.Process() calls pending. * Modified the algorithm used by easy/load.js for finding the root when it is not specified so that it will work better with MSIE in the situation where easy/load.js is loaded via a relative URL. * Added \operatorname to the AMSmath extension, which was acidentally left out previously. * Fixed some alignment problems with MSIE when running in "standards" mode rather than "quirks" mode (i.e., in the presence of certain DOCTYPEs). Modified Files: -------------- webwork-modperl/htdocs/jsMath: jsMath-easy-load.js jsMath.js webwork-modperl/htdocs/jsMath/easy: load.js webwork-modperl/htdocs/jsMath/plugins: autoload.js webwork-modperl/htdocs/jsMath/test: index-images.html sample.html webwork-modperl/htdocs/jsMath/uncompressed: jsMath.js Revision Data ------------- Index: jsMath-easy-load.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath-easy-load.js,v retrieving revision 1.1 retrieving revision 1.2 diff -Lhtdocs/jsMath/jsMath-easy-load.js -Lhtdocs/jsMath/jsMath-easy-load.js -u -r1.1 -r1.2 --- htdocs/jsMath/jsMath-easy-load.js +++ htdocs/jsMath/jsMath-easy-load.js @@ -82,6 +82,7 @@ if (!jsMath.Autoload) {jsMath.Autoload = {}} jsMath.Autoload.root = jsMath.Easy.root+'/'; + if (jsMath.Easy.autoload) { jsMath.Autoload.findTeXstrings = 0; jsMath.Autoload.findLaTeXstrings = 0; @@ -89,9 +90,9 @@ jsMath.Autoload.findCustomSettings = jsMath.Easy.findCustomSettings; jsMath.Autoload.loadFiles = jsMath.Easy.loadFiles; jsMath.Autoload.loadFonts = jsMath.Easy.loadFonts; - jsMath.Autoload.root = jsMath.Easy.root + '/'; if (!document.body) {jsMath.Easy.autoloadCheck = 1} + else {jsMath.Easy.autoloadReCheck = 1} document.write('<script src="'+jsMath.Autoload.root+'plugins/autoload.js"></script>'); } else { @@ -117,7 +118,9 @@ } jsMath.Easy.onload = function () { + if (jsMath.Easy.loaded) {return} else {jsMath.Easy.loaded = 1} if (jsMath.Easy.autoloadCheck) jsMath.Autoload.Check(); + if (jsMath.Easy.autoloadReCheck) jsMath.Autoload.ReCheck(); if (jsMath.Easy.tex2math) { jsMath.Synchronize(function () { if (jsMath.Easy.findCustomSettings) Index: jsMath.js =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/htdocs/jsMath/jsMath.js,v retrieving revision 1.37 retrieving revision 1.38 diff -Lhtdocs/jsMath/jsMath.js -Lhtdocs/jsMath/jsMath.js -u -r1.37 -r1.38 --- htdocs/jsMath/jsMath.js +++ htdocs/jsMath/jsMath.js @@ -40,11 +40,12 @@ //start = new Date().getTime(); jsMath.Script.Uncompress([ - ['if(!','window','.','jsMath','||!',1,'.','jsMath.','loaded','){var ','jsMath_old','=',1,'.',3,';',0,'document.','getElementById','||!',17,'childNodes||!',17,'createElement','){','alert("','The',' mathematics ','on this page requires W3C DOM support in its JavaScript. "+"Unfortunately, your ','browser',' doesn\'t seem to have this.");}','else{',1,'.',3,'={version:"3.4c",document:document,',1,':',1,',','platform',':(','navigator.',40,'.match(/','Mac/)?"mac":',42,40,44,'Win/)?"pc":"unix"),sizes:[50,60,70,85,100,120,144,173,207,249],','styles',':{".math','":"font-family',':serif; font-style:normal; font-weight:normal','",".typeset',52,53,'; ','line-height:normal','","div.','typeset','":"text-align',':center; margin:1em 0px;","span.',60,61,':left',54,' span',61,65,'; border:','0px; margin:0px; ','padding:0px','",".typeset .','normal',52,53,73,'size0','":"font-size:','50','%",".typeset .','size1',79,'60',81,'size2',79,'70',81,'size3',79,'85',81,'size4',79,'100',81,'size5',79,'! 120',81,'size6',79,'144',81,'size7',79,'173',81,'size8',79,'207',81,'size9',79,'249',81,'cmr10',52,':',3,'-cmr10',', serif',73,'cmbx10',52,':',3,'-',125,', ',3,122,73,'cmti10',52,':',3,'-',135,', ',3,122,73,'cmmi10',52,':',3,'-',145,73,'cmsy10',52,':',3,'-',152,73,'cmex10',52,':',3,'-cmex10',73,'textit',52,':serif; ','font-style:italic',73,'textbf',52,':serif; font-weight:bold',73,'link":"','text-decoration:none',73,'error',79,'10pt; ',168,'; "+"','background-color',':#FFFFCC; padding:1px',181,'border:','1px solid',' #CC0000',73,'blank','":"','display:inline-block','; ','overflow:hidden',70,'0px none; width:0px','; height:','0px;",".',60,' .','spacer','":"',191,'","#jsMath_hiddenSpan":"','visibility',':','hidden','; ','position:absolute','; top:0px',';left:0px',181,58,'; text-indent:0px","#','jsMath_message','":"','position:fixed','; bottom:','1px; left:2px; ',182,':#E6E6E6','; "+"border:solid 1px #959595; margin:0px; padding:','1px 8px','; "+"z-index:102; color',': black',! '; font-size:','small; ','width:auto',';","#','jsMath_panel','! ":"',216 ,217,'1.5em; right:1.5em','; padding:.8em 1.6em',181,182,':#DDDDDD',70,'outset 2px',181,'z-index:103; ',227,'; color:black',225,'10pt; font-style:normal","#',229,' .disabled":"color:#888888","#',229,' .infoLink',79,'85%","#',229,' *":"','font-size:inherit; font-style:inherit; font-family:inherit','; ',58,'","#',229,' div":"',182,':inherit; color:inherit;","#',229,' span":"',182,261,229,' td','":"border:0px; padding:0px; margin:0px; background-color:inherit; color:inherit;","#',229,' tr',268,229,' table',268,'jsMath_button','":"',216,217,'1px; right:2px; ',182,':white',221,'0px 3px 1px 3px',223,':black; ',175,225,'x-small',181,227,'; cursor:hand;","#',275,253,72,70,71,58,181,254,'","#jsMath_global":"',168,';","#jsMath_float','":"',208,209,'; left:0px; max-width:80%; "+"z-index:101; ',227,'; height:auto',302,' .drag":"',182,':#DDDDDD',70,'outset 1px',196,'12px',225,'1px',302,' .close":"',182,220,70,'inset 1px; width:8px',196,'8px; margin:1px 2px',302,' .source":"',182,':#E2E2E! 2',70,314,181,227,308,'; padding:8px 15px',181,'font-family:courier, fixed',225,'90%","#jsMath_noFont .message',61,': center',234,70,'3px solid #DD0000',181,182,':#FFF8F8; color: #AA0000',225,226,227,228,'jsMath_noFont .link','":"',72,' 5px 2px 5px',70,'2px outset; ',182,':#E8E8E8',181,'color:black',225,'80%; ',227,291,'jsMath_PrintWarning',' .message',61,':center',234,70,'3px solid #DD0000',181,182,': #FFF8F8; color: #AA0000',225,'x-',226,227,';","@media print":"#',275,' {display:none','}\\n"+"#jsMath_Warning',383,'}","@media screen":"#',367,383,'}"},Element',':function(','_1){return ',7,17,18,'("jsMath_"+_1);},','BBoxFor',390,'s','){this.','hidden.innerHTML','="<nobr><','span class=\\"',60,'\\"><',402,'scale\\">"+s+"</span></span></nobr>";var _3={w:this.',206,'.offsetWidth',',h:this.',206,'.offsetHeight','};this.',400,'="";return _3;},EmBoxFor',390,'s',9,'_5=',7,'Global.cache','.R;',0,'_5[this.em]){_5[this.em]={};}',0,'_5','[this.em][s',']){var _6','=this.BBoxFor','(s);_5! ',426,']={w:_6.w/this.em,h:_6','.h/this.em};}return ','_5',426! ,'];},', 'EmBoxForItalics',390,'s',9,'_8=',7,420,'.R;',0,'_8[this.em]){_8[this.em]={};}',0,'_8',426,']){var _9',428,'(s);if(s.match(/<i>|class=\\"(icm|italic|igreek|iaccent)/i)){_9.w',428,'(s+',7,'Browser.','italicString',').w-',7,455,'italicCorrection',';}_8',426,']={w:_9.w/this.em,h:_9',432,'_8',426,'];},Init',':function(){','if(',7,'Setup.inited','!=1){',0,7,471,'){',7,'Setup.','Body();}if(',7,471,'!=1){if(',7,471,'==-100','){return;}',25,'It looks like ',3,' failed to set up properly (error code "+',7,471,'+"). "+"I will try to keep going, but it could get ugly.");',7,471,'=1;}}this.em',428,'("<span style=\\""+',7,455,'block+";','width:13em;height:1em','\\"></span','>").w/13;if(this.em==0',399,'em',428,'("<img src=\\""+',7,189,'+"\\" style=\\"',502,'\\"/>").w/13;}var _a=',7,420,'.B;',0,'_a[this.em]){_a[this.em]={};','_a[this.em].','bb',428,'("x");var hh=',519,'bb.h;',519,'d',428,'("x"+',7,'HTML.Rule(1,','hh/',7,'em)).h-hh;if(',7,455,456,'){',519,'ic=',7,396,'(',7,455,456,').w;}}! ',7,455,460,'=',519,'ic;var bb=',519,'bb;var h=bb.h;var d=',519,'d;this.h=(h-d)/this.em;this.d=d/this.em;this.hd=this.h+this.d;this.xWidth=bb.w;this.',478,'TeXfonts','();var _f=this.EmBoxFor("<',402,118,'\\">M</span>").w/2;this.TeX.M_height=_f*(26/14);this.TeX.h=this.h;this.TeX.d=this.d;this.TeX.hd=this.hd;this.Img.Scale();',0,'this.initialized',399,478,'Sizes','();this.','Img.UpdateFonts();}this.p_height=(','this.TeX.cmex10[0].','h+',570,'d)/0.85;',564,'=1;},ReInit',468,'var w',428,'("x").w;if(w!=this.xWidth',399,'Init();}},Loaded',468,'if(',10,9,'_11=["Process","ProcessBeforeShowing","ConvertTeX","ConvertTeX2","ConvertLaTeX","ConvertCustom","CustomSearch","Synchronize","Macro","document"];','for(var i=0;i<','_11','.length;i++){if(',10,'[_11[i]]){delete ',10,'[_11[i]];}}}if(',10,'){this.Insert(',3,',',10,');}',10,'=null;',7,8,'=1;},Add',390,'dst,src){for(var id in src){','dst[id]=src[id','];}},Insert',390,606,'if(dst[id]&&typeof (src[id])=="object"&&(','typeof (dst[id])=="! ','object"||',612,'function")){this.Insert(dst[id],src[id]);}'! ,31,607, '];}}},Package',390,'obj,def',595,'obj.prototype,def);}};',7,'Global={isLocal:1,cache:{','T:{},D:{},R:{},B',':{}},ClearCache',468,7,420,'={',625,':{}};},GoGlobal',390,'_1b',9,'url=String(',7,1,'.','location);','var c=(',7,'isCHMmode','?"#":"?");if(_1b){url=url.replace(/\\?.*/,"")+"?"+_1b;}',7,'Controls.','Reload(',7,'root+"',3,'-global.html"+c+escape(url));},Init',468,'if(',7,'Controls.cookie.','global=="always"&&!',7,'noGoGlobal','){','if(navigator.','accentColorName',486,0,7,1,'){',7,1,'=',1,';}',7,646,8,'=1;',7,646,'defaults.hiddenGlobal',601,'this.GoGlobal(',7,646,'SetCookie(2));}},Register',468,'var _1e=',7,1,'.parent;',0,7,'isCHMode){',7,643,'=(',7,1,'.','location.protocol','=="mk:");}try{',0,7,643,399,'Domain();}if(_1e.',3,'&&_1e.',7,'isGlobal){_1e.',7,'Register(',7,1,');}}','catch(err){',7,658,'=1;}},Domain',468,660,'appName=="Microsoft Internet Explorer"&&',7,40,'=="mac"&&',42,'userProfile','!=null',486,'if(',1,'==parent',486,'var _1f=',7,17,'domain;try{while(true){! try{if(parent.',17,'title',726,'){return;}}','catch(err){}',0,17,'domain.match(/\\..*\\./)){break;}',7,17,'domain=',7,17,'domain.replace(/^[^.]*\\./,"");}}',740,7,17,746,'_1f;}};',7,'Script={request:null,Init',468,'if(!(',7,655,'asynch&&',7,655,'progress',')){if(',1,'.XMLHttpRequest','){try{','this.request','=new XMLHttpRequest;}catch(err){}}',0,769,'&&',1,'.','ActiveXObject',9,'xml=["MSXML2.XMLHTTP.5','.0","MSXML2.XMLHTTP','.4',779,'.3',779,'","Microsoft.XMLHTTP"];',587,'xml.length&&!',769,';i++){try{',769,'=new ',776,'(xml[i]);}catch(err){}}}}',0,769,'||',7,478,'domainChanged',399,'Load=this.delayedLoad;this.needsBody=1;}},Load',390,'url,_23){if(_23){',7,'Message.Set("Loading "+url);',7,'Script.','Delay(1);',7,'Script.Push(','this,"xmlRequest",url',');',7,809,7,'Message',',"Clear");}',31,7,809,810,');}},xmlRequest',390,'url){','this.blocking','=1;try{',769,'.open("GET",url,false);',769,'.send(null);}',714,824,'=0;if(',7,'Translate.','restart&&',7,'Translate.asynchronous){! return "";}throw "jsMath can\'t load the file \'"+url+"\'\\n"+! "',815,' : "+err.message;}if(',769,'.status','&&',769,841,'>=400){',824,832,7,834,'restart&&',7,837,'Error status: "+',769,841,';}',0,'url.match(/\\.js$/)){','return (',769,'.responseText',');}var _25=','this.queue',';',863,'=[];',7,1,'.eval(',769,861,');',824,'=0;','this.queue=this.queue.concat(','_25);this.Process();return "";},cancelTimeout:30*1000,iframe:null,blocking:0,cancelTimer:null,needsBody:0,queue:[],Synchronize',390,'_26,_27){','if(typeof (','_26)!="string"){',7,809,'null,_26,_27);}',31,7,809,7,1,',"eval",_26);}},Push',390,'_28,_29,_2a){',863,'[',863,'.length]=[_28,_29,_2a];if(!(',824,'||(this.needsBody&&!',7,17,'body))){this.Process();}},Process',468,'while(',863,'.length&&!',824,9,'_2b=',863,'[0];var _2c=',863,'.slice(1);',863,'=[];var _2d=_2b[0];var _2e=_2b[1];var _2f=_2b[2];if(_2d){_2d[_2e](_2f);}',31,'if(_2e){_2e(_2f);}}',875,'_2c);}},delayedLoad',390,'url',399,'Push(this,"startLoad",url);},startLoad',390,'url){','this.iframe','=',7,17,23,'("iframe");','this.iframe.s! tyle.',204,'="',206,'";',930,'position="absolute";',930,'width="0px";',930,'height="0px";if(',7,17,'body.firstChild','){',7,17,'body.insertBefore(',924,',',7,17,943,');}',31,7,17,'body','.appendChild(',924,');}',824,'=1;this.','url=url;',0,858,924,'.src=url;}',31,924,'.src=',7,649,3,'-loader.html";}if(url.substr(0,',7,'root.length',')==',7,'root){url=url.substr(',7,976,');}',7,804,'this.cancelTimer','=setTimeout("',7,806,'cancelLoad','()",this.cancelTimeout);},endLoad',390,'_32){if(',985,'){clearTimeout(',985,');',985,'=null;}',7,815,'.Clear();if(_32!="cancel"){',824,'=0;this.Process','();}},Start',468,'this.tmpQueue','=',863,';',863,'=[];},End',468,875,1006,');delete ',1006,';},',989,468,985,601,7,815,'.Set("Can\'t load file");this.endLoad("cancel");},Delay',390,'_33){',824,'=1;setTimeout("',7,806,'endDelay','()",_33);},',1031,468,824,1003,'();},','imageCount',':0,WaitForImage',390,'_34){',824,962,1038,'++;if(this.img==null',399,'img=[];}var img=new Image',568,'img[this.im! g.length]=img;img.onload=function(){if(--',7,806,1038,'==0){',! 7,806,10 31,'();}};img.onerror','=img.onload;img.','onabort',1058,'src=_34;},Uncompress',390,'_36){','for(var k=0;k<','_36.length;k++){var d=_36[k];var n=d.length;',587,'n;i++){',879,'d[i])=="number"){d[i]=d[d[i]];}}_36[k]=d.join("");}',1,869,'_36.join(""));}};',7,815,'={',189,':null,message:null,text:null,clear:null,Init',468,0,7,17,'body||!',7,655,764,486,'if(',7,478,'stylesReady){','this.message','=',7,'Setup.DIV("message",{visibility:"hidden','"});}',31,1091,'=',7,1094,'",position:"absolute",','bottom:"1px",left:"2px",','backgroundColor',':"#E6E6E6",border:"solid 1px #959595",margin:"0px",padding:"1px 8px",zIndex:102,color:"black",fontSize:"small",width:"auto"});}','this.text','=',7,17,'createTextNode','("");',1091,958,1105,');',1091,'.onmousedown=',7,834,'Cancel;},Set',390,'_3b,_3c){','if(this.clear){clearTimeout(this.clear',');','this.clear',998,'if(',7,655,764,'){',0,1105,399,'Init();',0,1105,739,'if(',7,455,'textNodeBug','){',1091,'.innerHTML','=_3b;}',31,1105,'.nodeValue',11! 45,'this.message.style.',204,'="visible";if(_3c){',1150,'cursor="pointer";',0,1150,'cursor){',1150,'cursor="hand";}',1091,'.title=" Cancel Processing of Math ";}',31,1150,'cursor="";',1091,'.title="";}}',31,'if(_3b.substr(0,8)!="Loading "){',7,1,841,'=_3b;}}},Clear',468,1122,');}',1124,'=setTimeout("',7,815,'.doClear()",1000);},doClear',468,'if(',1124,399,'clear',601,7,1,841,'="";if(',1105,'){',1105,1148,'="";}if(',1091,'){',1150,204,'="',206,'";}}},Blank',468,'if(','this.blank','||!',7,17,'body',486,1205,'=',7,478,'DIV("',189,'",{position:(',7,455,'msiePositionFixedBug','?"absolute":"fixed"),top:"0px",left:"0px",bottom:"0px",right:"0px",zIndex:101,',1103,':"white"});if(',7,455,'msieBlankBug','){',1205,1144,'=" ";',1205,'.style.width="110%";',1205,'.style.height="110%";}},UnBlank',468,'if(',1205,'){',7,17,'body.removeChild(',1205,');}',1205,'=null;}};',7,'Setup={',8,':[],DIV',390,'id,_3e',9,'div=',7,17,23,'("div");div.id="jsMath_"+id;','for(var i in ','_3e){div.style[i! ]=_3e[i];}',0,7,17,'body.hasChildNodes){',7,17,'body',958,'div! );}',31, 7,17,947,'div,',7,17,943,');}return div;},Script',390,'_41,_42){if(','this.loaded[_41',']){return;}',31,1280,']=1;}',0,'_41.match("^([a-zA-Z]+:/?)?/")){_41=',7,'root+_41;}',7,806,'Load(_41,_42);},Hidden',468,7,206,'=this.DIV("Hidden",{',204,':"',206,1101,'top:0,left:0,border:0,padding:0,margin:0});',7,'hiddenTop=',7,206,';return;},Source',468,'if(',7,'Autoload&&',7,'Autoload.root','){',7,'root=',7,1311,';}',31,7,'root="";var _43=',7,17,'getElementsByTagName("','script");if(_43){',587,'_43','.length;i++){var ','src=_43[i].src;if(src&&src.match("(^|/)',7,'js$")){',7,1314,'src.','replace(/',7,'js$/,"");i=_43.length;}}}}if(',7,'root.charAt(0)=="/"){',7,1314,7,17,698,'+"//"+',7,17,'location.host+',7,'root;}',31,0,7,'root.match','(/^[a-z]+:/i)){src','=new String(',7,17,640,7,1314,'src','.replace(new RegExp','("[^/]*$"),"")+',7,'root;while(',7,1353,'("/[^/]*/\\\\.\\\\./")){',7,1314,7,'root',1362,'("/[^/]*/\\\\.\\\\./"),"/");}}}',7,'Img.',1314,7,649,'fonts/";',7,189,'=',7,649,189,'.! gif";this.Domain();},Domain',468,'try{',7,17,'domain;}catch(err',486,'var _46="";var _47=',7,17,'domain;if(',7,1353,'("://([^/]*)/")){_46=RegExp.$1;}_46=_46.replace(/:\\d+$/,"");if(_46==""||_46==_47',486,660,'appName=="Microsoft Internet Explorer"&&',7,40,'=="mac"&&',42,'onLine&&',42,725,'&&',7,17,'all',486,'_46=_46.split(/\\./);_47=_47.split(/\\./);if(_46.length<2||_47.length<2||','_46[_46.length-','1',']!=_47[_47.length-','1]||',1417,'2',1419,'2]){this.','DomainWarning','();return;}var _48=',1417,'2]+"."+',1417,'1];for(var i=3;i<=_46.length&&i<=_47',589,1417,'i',1419,'i]){break;}_48=',1417,'i]+"."+_48;}',7,17,746,'_48;this.',798,'=1;},',1425,468,25,'In order for ',3,' to be able to load the additional "+"components that it may need, the ',7,'js file must be "+"',8,' from a server in the same domain as the page that "+"contains it. Because that is not the case for this page, "+"the',27,'displayed here may not appear correctly.");},','EncodeFont',390,'_4a',9,'_4b=',7,'TeX[! _4a];if(_4b[0].c',726,486,1064,'128;k++){var _4d=_4b[k];_4b[k]! =_4d[3]; if(_4b[k]==null){_4b[k]={};}_4b[k].w=_4d[0];_4b[k].h=_4d[1];if(_4d[2]!=null){_4b[k].d=_4d[2];}_4b[k].c=',7,'TeX.encoding[k];}},Fonts',468,587,7,'TeX.fam',1327,'_4f=',7,1472,'[i];if(_4f',399,1456,'(_4f);}}},TeXfont',390,'_50',9,'_51=',7,'TeX[_50];if(_51==null',486,'var WH=',7,'EmBoxFor("<span class=\\""+_50+"\\">"+_51[65].c','+"</span>");_51.hd=WH.h;_51.dh=0.05;_51.d=',7,1490,'+',7,530,'_51.hd)+"</span>").h-_51.hd;_51.h=_51.hd-_51.d;','if(_50=="',145,'"){_51.skewchar=','127;}',31,1498,152,1500,'48;}}},',558,468,587,7,1472,589,7,1472,'[i]){this.TeXfont(',7,1472,'[i]);}}},Sizes',468,7,'TeXparams','=[];for(var j=0;j<',7,'sizes.length;j++){',7,1521,'[j]={};}',1258,7,'TeX){',879,7,'TeX[i])!="object"){for(var j=0;j<',7,'sizes.length;j++){',7,1521,'[j][i]=',7,'sizes[j]*',7,'TeX[i]/100;}}}},Styles',390,'_57){',0,'_57){_57=',7,50,';_57[".',60,' .scale"]="font-size:"+',7,655,'scale+"%";this.stylesReady=1;}',7,809,'this,"','AddStyleSheet','",_57);if(',7,455,'styleChangeDelay','){',7,809! ,7,'Script,"Delay",1);}},',1558,390,'_58',9,'_59=',7,17,1323,'head")[0];var _5a="";for(var id in _58){_5a+=id+" {"+_58[id]+"}\\n";}if(',7,17,'createStyleSheet){_59.insertAdjacentHTML("beforeEnd","<span',' style=\\"','display:none\\">x</span>"+"<style type=\\"text/css\\">"+_5a+"</style>");}',31,'var _5c=',7,17,23,'("style");_5c.type="text/css";_5c',958,7,17,1109,'(_5a));_59',958,'_5c);}},Body',468,'if(this.inited',486,'this.inited=-','1;',7,478,'Hidden();',1598,'2;',7,455,1134,1598,'3;if(',7,655,189,'){',7,815,'.Blank();}',1598,'4;',7,478,'Styles();',1598,'5;',7,646,1134,1598,'6;',7,809,7,'Setup,"User","pre-font");',1598,'7;',7,809,7,'Font,"Check");if(',7,'Font.register.length){',7,809,7,'Font,"LoadRegistered");}this.inited=1;},User',390,'_5d){if(',7,'Setup.UserEvent[_5d',']){(',7,1648,'])();}},UserEvent:{"pre-font":null,"onload":null}};',7,'Update={',558,390,'_5e){for(var ','_5f in ',1657,'_60 in _5e[_5f]){for(var id in _5e[_5f][_60]){',7,'TeX[_5f][_60][id]=_5e[_5f][_60][id! ];}}}},TeXfontCodes',390,'_62){for(var _63 in _62){',587,'_62[! _63].len gth;i++){',7,'TeX[_63][i].c=_62[_63][i];}}}};',7,'Browser={allowAbsolute:1,allowAbsoluteDelim:0,separateSkips:0,valignBug:0,operaHiddenFix:"",','msieCenterBugFix',':"",','msieInlineBlockFix',':"",','msieSpaceFix',':"",imgScale:1,renameOK:1,',1562,':0,delay:1,version:0,','TestSpanHeight',468,7,400,'="<span','><','span style=\\""+this.block+";','height:2em;width:','1px',503,'></span>";var ','_65=',7,'hidden.firstChild;','var img=_65.firstChild;','this.spanHeightVaries','=(_65',411,'>=img',411,'&&_65',411,'>0);','this.spanHeightTooBig','=(_65',411,'>img',411,');',7,400,'="";},','TestInlineBlock',468,'this.block','="display:-moz-inline-box";','this.hasInlineBlock','=',7,'BBoxFor("<span style=\\""+this.block+";','width:10px;height:5px\\"></span>").w>0;if','(',1715,'){',7,'styles[".typeset .',189,'"]=',7,1724,189,'"].replace(/',191,'/,',1713,');',7,1724,200,'"]=',7,1724,200,1730,191,'/,"");}',31,1713,'="',191,'";',1715,'=',7,1718,1719,'(!',1715,739,1713,'+=";',193,'";var h=',7,396! ,'("x").h;this.mozInlineBlockBug=',7,1718,'height:"+h+"px;width:1px',503,'>x"+"<',1685,1767,';vertical-align:-"+h+"px',503,'>").h>2*h;this.widthAddsBorder=',7,1718,193,';height:1px;width:10px',';border-left:','10px solid',503,'>").w>10;','this.msieBorderBug','=',7,1718,1767,503,'>x").h!=',7,1718,1767,1779,186,503,'>x").h;this.blankWidthBug=',1783,'||',7,1718,1686,'0px',503,'>").h==0;},','TestRenameOK',468,7,400,1683,1689,'_68=',7,1692,'_68.setAttribute("name","','jsMath_test','");this.renameOK=(',7,17,'getElementsByName("',1815,'").length>0);',7,400,1710,'TestStyleChange',468,7,400,1683,' ID=\\"',1815,'\\">x</span>";var _69=',7,1692,'var w=_69',408,';',7,478,1558,'({"#',1815,79,'200%"});this.',1562,'=(_69',408,'==w);',7,400,1710,'VersionAtLeast',390,'v',9,'bv',1355,'this.version',').split(".");','v',1355,'v',1859,'if(v[1]==null){v[1]="0";}return bv[0]>v[0]||(bv[0]==v[0]&&bv[1]>=v[1]);},Init',468,7,29,'="unknown";this.',1711,568,1679,568,1805,568,1825,568,'MSIE',568,'Mozilla! ',568,'Opera',568,'OmniWeb',568,'Safari',568,'Konqueror','();i! f(','thi s.allowAbsoluteDelim','){',7,'Box.DelimExtend=',7,'Box.DelimExtendAbsolute;',7,'Box.Layout=',7,'Box.LayoutAbsolute;}',31,7,'Box.DelimExtend=',7,'Box.DelimExtendRelative;',7,'Box.Layout=',7,'Box.LayoutRelative;}if(','this.separateSkips','){',7,'HTML.Place=',7,'HTML.','PlaceSeparateSkips',';',7,'Typeset.prototype.','Place=',7,1917,1914,';}},MSIE',468,'if(',1694,'&&!',1702,'){',7,29,'="MSIE";if(',7,40,'=="pc"){this.','IE7=(',1,767,726,');this.quirks=(',7,17,'compatMode=="BackCompat");',1889,'=1;',1908,'=1',';this.buttonCheck=1;this.',1226,'=1;this.msieDivWidthBug=1;this.',1220,962,'msieIntegralBug',962,'waitForImages',962,'msieAlphaBug=!','this.IE7',';this.','alphaPrintBug','=!',1957,';this.',1671,'="position:relative; ";this.',1673,'=" ',191,';";',0,1957,399,1675,1683,1580,191,503,'>";}',7,'Macro("joinrel","\\\\mathrel{\\\\kern-5mu}"),',7,1724,'arial"]="font-family: \'Arial unicode MS\'";',0,1957,'||this.quirks){',7,'styles["#',214,'"]=',7,1987,214,1730,216,'/,"',208,'").',133! 4,227,'/,"");',7,1987,229,'"]=',7,1987,229,1730,216,'/,"',208,'").',1334,227,'/,"");',7,1987,275,'"]="width:','1px; "+',7,1987,275,1730,216,'/,"',208,'").',1334,227,'/,"");',7,1,'.attachEvent("','onscroll",',7,646,'MoveButton',');if(',1957,'){',7,1,2034,'onresize",',7,646,2038,');}this.msieMoveButtonHack=',1957,';}',7,1987,353,'"]+=" ','display: inline-block',';";',7,1724,200,'"]=',7,1724,200,1730,191,'/,"");',7,50,'[".tex2math_div"]=',7,50,'["div.',60,'"]+"; width: 100%; ',2056,'";if(','screen.deviceXDPI','&&','screen.logicalXDPI','&&',2078,'!=',2080,399,'imgScale*=',2080,'/',2078,';',7,655,'alpha=0;}this.',456,'="<i>x</i>";',7,'EmBoxFor=',7,436,';}',31,'if(',7,40,'=="mac"){this.msieAbsoluteBug',962,'msieButtonBug',1949,1226,962,'quirks=1;',7,478,'Script("',3,'-msie-mac.js");',7,'Parser.prototype.macros.angle=["Replace","ord","<font face=\\"Symbol\\">‹</font>","normal"];',7,1987,229,2019,'42em; "+',7,1987,229,1730,227,'/,"");',7,655,'printwarn=0;}}',7,'Macro("not","\\! \\mathrel{\\\\rlap{\\\\kern3mu','/}}");}},',1879,468,'if(',7,2! 06,'.ATT RIBUTE_NODE){',7,29,'="',1879,'";if(',7,40,1934,1959,'=1;}',1889,'=1;',7,1987,275,'"]=',7,1987,275,1730,'cursor:hand','/,"cursor:pointer");',7,1987,353,'"]=',7,1987,353,1730,2162,'/,"cursor:pointer");',7,2134,'/}}");',660,'vendor=="Firefox"){',1858,'=',42,'vendorSub;}',31,660,'userAgent.match','(" Firefox/([0-9.]+)( |$)")){',1858,'=RegExp.$1;}}}},',1883,468,660,'accentColorName){',7,29,'="',1883,'";','this.allowAbsolute','=',1715,';',1889,'=',2198,';this.valignBug=!',2198,1947,1141,'=1;',7,'noChangeGlobal=1;',0,1715,'){',7,'Setup.Script("jsMath-old-browsers.js','");}}},Opera',468,'if(',1702,'){',7,29,'="Opera";var _6d=',42,2185,'("Opera 7");',2198,'=0;this.delay=10;this.operaHiddenFix="[Processing]";if(_6d){',7,2216,'");}}},Safari',468,660,'appVersion',44,'Safari\\//)){',7,29,'="Safari";var _6e=',42,2185,'("Safari/([0-9]+)");_6e=(_6e)?_6e[1]:400;',587,7,1472,589,7,1472,'[i]&&',7,'TeX[',7,1472,'[i]]){',7,'TeX[',7,1472,'[i]].dh=0.1;}}',7,'TeX.axis_height+=0.05;',7,'TeX.default! _rule_thickness+=0.025;',1889,'=_6e>=125;this.safariIFRAMEbug=_6e>=312&&_6e<412;this.safariButtonBug=_6e<412;this.safariImgBug',962,1141,'=1',1947,1562,'=1;}},',1887,468,660,'product&&',42,'product.match("',1887,'")){',7,29,'="',1887,'";',2198,'=0;',1889,'=0;',660,2185,'(/',1887,'\\/(\\d+)\\.(\\d+)/)){if(RegExp.$1<3||(RegExp.$1==3&&RegExp.$2<3)){',1908,962,'valignBug=1;',7,2216,'");}}',7,'Add(',7,50,',{".',60,' .',118,52,': ','jsMath-cmr10, jsMath cmr10',', serif',73,125,52,': ',3,'-',125,', ',3,' ',125,', ',2311,73,135,52,': ',3,'-',135,', ',3,' ',135,', ',2311,73,145,52,': ',3,'-',145,', ',3,' ',145,73,152,52,': ',3,'-',152,', ',3,' ',152,73,159,52,': ',3,163,', ',3,' ',159,'"});',7,'Font.testFont="',3,163,', ',3,' ',159,'";}}};',7,'Font={testFont:"',3,163,'",fallback:"symbol",register:[],message:"<b>No ',3,' TeX fonts found</b> -- using',' image fonts instead','.<br/>\\n"+"These',' may be slow and might not print well.<br/>\\n"+"Use the jsMath control panel to get additi! onal information.",','extra_message:"Extra TeX fonts not found! : <b><sp an id=\\"jsMath_ExtraFonts',503,'></b><br/>"+"Using',2388,'. This',2390,'print_message:"To print higher-resolution math symbols, click the<br/>\\n"+"<b>Hi-Res Fonts for Printing</b> button on the ',3,' control panel.<br/>\\n",','alpha_message:"If the math symbols print as black boxes, turn off <b>image alpha channels</b><br/>\\n"+"using the <B>Options</B> pane of the ',3,2399,'Test1',390,'_70,n,_72,_73','){if(n==null){n=124;}if(','_72==null){_72=2;}if(_73==null){_73="";}var wh1=',7,'BBoxFor("<span style=\\"font-family',': "+_73+_70+", serif\\">"+',7,'TeX[_70][n].c+"</span>");','var wh2=',7,2409,': serif\\">"+',7,2412,859,'wh1.w>_72*wh2.w&&wh1.h!=0);},Test2',390,'_76,n,_78,_79',2406,'_78==null){_78=2;}if(_79==null){_79="";}var'], - [' wh1=','jsMath.','BBoxFor("<span style=\\"font-family',': "+','_79+_76','+", serif','\\">"+',1,'TeX[_76][n','].c+"</span>");','var wh2=',1,2,': serif',6,1,'TeX[_76][n',9,'return (wh2.w>_78*wh1.w&&wh1.h!=0);},CheckTeX',':function','(){var ','wh=',1,2,3,1,'Font.','testFont',5,6,1,'TeX.cmex10[1',9,1,'nofonts=((wh.w*3>wh.h||wh.h==0)&&!this.Test1("cmr10','",null,null,"jsMath-"));if(',1,'nofonts&&(',1,'platform','!="mac"||',1,'browser!="Mozilla"||!',1,'Browser.','VersionAtLeast(1.5))){wh=',1,2,': cmex10, serif',6,1,'TeX.cmex10[1',9,1,34,'"));if(!',1,'nofonts){',1,'Setup.Script("jsMath-','BaKoMa-fonts.js");}}},Check',19,20,'_7d=',1,'Controls.','cookie;this.CheckTeX();if(',1,57,'if(_7d.autofont','||_7d','.font=="tex"){','_7d.font','=this.','fallback',';if(_7d.warn){',1,'nofontMessage=1;_7d.warn=0;',1,65,'SetCookie(0);if','(',1,'window.NoFontMessage','){',1,83,'();}else{','this.Message(this.','message);}}}}else{',69,'){',72,'="tex";}if(_7d',71,'return;}}if(',1,'noImgFonts){',72,! '="unicode";}if(',72,'=="unicode"){',1,59,74,'-"+',1,39,'+".js");',1,'Box.TeXnonfallback=',1,'Box.TeX',';',1,112,'=',1,'Box.TeXfallback;return;}','if(!_7d.print&&_7d.printwarn){this.','PrintMessage','((',1,44,'alphaPrintBug&&',1,65,'cookie.alpha)?','this.print_message','+this.alpha_message:',128,');}if(',1,44,'waitForImages){',1,'Script.','Push(',1,'Script,"WaitForImage",',1,'blank',131,72,'=="symbol"){',1,59,74,'-symbols.js");',1,'Box.TeXnonfallback=',1,112,';',1,112,'=',1,118,1,'Img.SetFont','({cmr10',':["all"],','cmmi10',162,'cmsy10',162,'cmex10',162,'cmbx10',162,'cmti10:["all"]});',1,'Img.LoadFont','("cm-fonts");},Message',19,'(_7e','){if(',1,'Element("','Warning','")){','return;}var ','div=',1,'Setup.DIV("',180,'",{});div.innerHTML="<center><table><tr><td>"+"<div ','id=\\"jsMath_noFont\\"><div ','class=\\"message\\">"+','_7e+"<div style=\\"text-align:left\\"><span style=\\"float:left; margin: 8px 0px 0px 20px\\">"+"<span onclick=\\"',1,65,'Panel','()\\" title=\\" ','Op! en the ','jsMath Control Panel',' \\" class=\\"link\\">',196,'! </span>" +"</span','><span style=\\"margin: 8px 20px 0px 0px; float:right\\">"+"<span onclick=\\"',1,26,'HideMessage',194,'Remove this font warning message',197,'Hide this Message',199,'></div><div style=\\"height:6px\\"></div><br clear=\\"all\\"/></div></','div>"+"<div style=\\"width:22em; height:1px\\"></div>"+"</td></tr></table></center><hr/>";},',203,19,20,'_80=',1,179,180,'");if(_80){_80','.style.display="none";}},',120,19,'(_81',177,1,179,'PrintWarning',181,182,'div=',1,185,226,187,189,'_81+"</',210,'Register',19,'(_83,_84',177,'typeof (_83)=="string"){_83={name:_83};}if(!',1,'Setup.inited','&&!_84){','this.register','[',245,'.length]=_83;',182,'_85=_83.name;var _86=_85.replace(/10$/,"");var _87=',1,'TeX.fam.length;if(','_83.prefix','==null){',253,'="";}if(!_83.style){_83.style="font-family',3,253,'+_85',5,'";}if(!_83.styles){_83.styles={};}if(!_83.macros){_83.macros={};}',1,'TeX.fam[_87]=_85;',1,'TeX.famName[_85]=_87;_83.macros[_86]=["HandleFont",_87];',1,'Add(',1,'Parser.prot! otype.macros,_83.macros);_83.styles[".typeset ."+_85]=_83.style;',1,'Setup.Styles(_83.styles);if(',1,'initialized){',1,136,'Push(',1,'Setup,"TeXfont",_85);}var _88=',1,65,'cookie;var _89=!',1,'nofonts&&_83.test(_85,_83.testChar,_83.testFactor,',253,');if(_89&&_88',71,'if(_83.','tex){_83.tex(_85,_87,_83);}return;}if(!_89&&_88.warn&&_88.font=="tex"&&!',1,57,'if(!_88.fonts.match("/"+_85+"/")){_88.fonts+=_85+"/";',1,65,80,'(!',1,179,180,181,88,'extra_message);}var _8a=',1,179,'ExtraFonts");if(_8a',177,'_8a.innerHTML','!=""){',306,'+=",";}',306,'+=" "+',253,'+_85;}}}if(_88.font=="unicode"||',1,97,287,74,'){_83.',74,'(_85,_87,_83);}',182,'_8b={};_8b[_85]=["all"];',1,160,'(_8b);',1,173,'(_85);if(',1,'initialized){',1,136,'Push(',1,'Img,"Scale");',1,136,'Push(',1,'Img,"UpdateFonts");}},LoadRegistered',19,20,'i=0;while(i<',245,'.length){this.',237,'(',245,'[i++],1);}',245,'=[];},Load',19,'(_8d){',1,'Setup.Script(this.URL(_8d));},URL',19,'(_8e){return ',1,'Img.root+_8e+"/def.js";}};'! ,1,'Controls={cookie:{scale:100,font:"tex",autofont:1,scaleImg! :0,alpha :1,warn:1,fonts:"/",printwarn:1,stayhires:0,button:1,progress:1,asynch:0,blank:0,print:0,keep:"0D",global:"auto",hiddenGlobal:1},cookiePath:"/",','noCookiePattern',':/^(file|mk):$/,Init',19,'(){this.panel=',1,185,'panel",{display:"none"});if(!',1,44,'msieButtonBug){this.Button',87,'setTimeout("',1,65,'Button()",500);}},',193,19,'(){',1,'Translate.Cancel();if(this.loaded){this.Main',87,1,136,'delayedLoad(',1,'root+"jsMath-controls.html");}},Button',19,20,'_8f=',1,185,'button",{});_8f.title=" Open ',196,' ";_8f.innerHTML="<span onclick=\\"',1,65,193,'()\\">jsMath</span>";if(!',1,'Global.','isLocal&&!',1,'noShowGlobal){_8f.innerHTML+="<span id=\\"jsMath_global\\" title=\\" Open jsMath Global ',193,' \\" "+"onclick=\\"',1,401,'Show(1)\\">Global </span>";}if(_8f.offsetWidth<30){_8f.style.width="auto";}if(!','this.cookie','.button){_8f',219,'MoveButton',19,20,'_90=',1,'Controls;if(!','_90.button','){',419,'=',1,179,'button");}if(',419,'){_90.MoveElement(_90.','button,3,2);}va! r dx=20;var dy=20;if(',419,'){dy=',419,'.offsetHeight','+6;dx=dy+5;}if(_90.panel',427,'panel,dx,dy);}},MoveElement',19,'(obj,dx,dy',177,1,44,'IE7){var _96=document.body;obj.style.right','="auto";obj.style.','bottom',442,'left=_96.clientWidth+_96.scrollLeft-obj.offsetWidth-dx+"px";obj.style.top=_96.clientHeight+_96.scrollTop-obj',432,'-dy+"px";}else{','obj.style.visibility="','hidden";',448,'visible";}},GetCookie',19,'(){if(','this.defaults',254,454,'={};}',1,267,454,',',410,');this.userSet={};var _97=',1,'document.cookie',';if(',1,'window.location','.protocol.match(this.',362,')){_97',73,'localGetCookie','();','this.isLocalCookie','=1;}if(_97.match(/jsMath=([^;]+)/)){var _98=unescape(RegExp.$1).split(/,/);for(var i=0;i<_98.length;i++){var x=_98[i].match(/(.*):(.*)/);if(x[2].match(/^\\d+$/)){x[2]=1*x[2];}',410,'[x[1]]=x[2];this.userSet[x[1]]=1;}}},',473,19,'(){return ',1,468,'.search.substr(1);},SetCookie',19,'(_9b){var _9c=[];','for(var id in ',410,177,454,'[id]==null||',41! 0,'[id]!=',454,'[id]){_9c[_9c.length]=id+":"+',410,'[id];}}_9c! =_9c.joi n(",");if(',475,177,'_9b==2){return "','jsMath="+escape(','_9c);}this.','localSetCookie','(_9c,_9b);}else{_9c=escape(_9c);if(_9c==""){_9b=0;}if(','this.cookiePath','){_9c+="; ','path="+',505,';}if(','this.cookieDomain',506,'domain="+',510,';}if(',410,'.keep!="0D"){var ms={D',':1000*60*60*24',',W',517,'*7,M',517,'*30,Y',517,'*365};var exp=new Date;exp.setTime(exp.getTime()+',410,'.keep.substr(','0,1)*ms[',410,526,'1,1)]);_9c+="; expires="+exp.toGMTString();}if(_9c!=""){',1,465,'="jsMath="+_9c;var _a0=',1,465,';if(_9b&&!_a0.match(/jsMath=/)){alert("Cookies must be enabled in order to save jsMath options");}}}return null;},',503,19,'(_a1,_a2){if(!_a2){',182,'_a3=String(',1,468,').replace(/\\?.*/,"");if(_a1!=""){_a3+="?',501,'_a1',131,'_a3!=',1,468,'.href){this.Reload(_a3);}},Reload',19,'(url){if(!this.loaded){return;}this.loaded=0;',1,243,'=-100;',1,401,'ClearCache();if(url){',1,468,'.replace(url);}else{',1,468,'.reload();}}};',1,'Click={CheckClick',19,'(_a5){if(!_a5){_a5=',1,'! window.event;}if','(_a5.altKey){',1,65,193,'();}},CheckDblClick',19,'(_a6){if(!_a6){_a6=',1,571,'(!',1,'Click.DblClick){',1,'Extension.Require("double-click",1);var _a7=_a6;_a6={};',487,'_a7){_a6[id]=_a7[id];}}',1,136,'Push(',1,'Click,"DblClick",[_a6,this.alt]);}};',1,'TeX={thinmuskip:3/18,medmuskip:4/18,thickmuskip:5/18,x_height:0.430554,quad:1,num1:0.676508,num2:0.393732,num3:0.44373,denom1:0.685951,denom2:0.344841,sup1:0.412892,sup2:0.362892,sup3:0.288888,sub1:0.15,sub2:0.247217,sup_drop:0.386108,sub_drop:0.05,delim1:2.39,delim2:1,axis_height:0.25,default_rule_thickness:0.06,big_op_spacing1:0.111111,big_op_spacing2:0.166666,big_op_spacing3:0.2,big_op_spacing4:0.6,big_op_spacing5:0.1,integer:6553.6,scriptspace:0.05,nulldelimiterspace:0.12,delimiterfactor:901,delimitershortfall:0.5,scale:1,atom:["ord","op","bin","rel","open","close","punct","ord"],fam:["cmr10","cmmi10","cmsy10","cmex10","cmti10","","cmbx10",""],famName:{cmr10:0,cmmi10:1,cmsy10:2,cmex10:3,cmti10:4,cmbx10:6}! ,encoding:["À","Á","Â","Ã","Ä","Å! ;"," 6;","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","°","Ñ","Ò","Ó","Ô","Õ","Ö","·","Ø","Ù","Ú","Û","Ü","µ","¶","ß","ï","!",""","#","$","%","&","'","(",")","*","+",",","-",".","/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","[","\","]","^","_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","ÿ"],cmr10:[[0.625',',0.683],[0.','833',595,'778',595,'694',595,'667',595,'75',595,'722',595,'778',595,'722',595,'778',595,'722',595,'583',',0.694,0,{ic:0.','0778,krn:{"39":0.0778,"63":0.0778,"33":0.0778,"41":0.0778,"93":0.0778},lig:{"105":','14,"108":15}}],[0.','556',',0.694],[0.','556',621,'833',621,'833',621,'278',',0.431],[0.','3! 06,0.431',',0.194],[0.','5',621,'5',621,'5,0.628],[0.5',621,'5,0.568],[0.75',621,'444,0,0.17],[0.5',621,'722',629,'778',629,'5,0.528,0.0972],[0.903,0.683],[1.01',595,'778,0.732,0.0486],[','0.278',',0.431,0,{','krn:{"108":-0.','278,"76":-0.319}}],[',649,',0.694,0,{lig:{"96":','60}}],[0.5',621,'833',',0.694,0.194],[','0.5',',0.75,0.0556],[0.','833',660,'778',621,'278',',0.694,0,{krn:{"','63":0.111,"33":0.111','},lig:{"39":34}}],[0.','389',',0.75,0.25],[0.','389',670,'5,0.75],[','0.778,0.583,0.0833],[','0.278,0.106',631,'333',650,'lig:{"45":','123}}],[',675,'],[0.5',670,'5,0.644','],[0.5,0.644],[0.5,0.','644',685,'644',685,'644',685,'644],[0.5,0.644],[',649,629,'278,0.431',631,'278,0.5',631,'778',',0.367,-0.133],[','0.472,0.5',631,'472',654,'62}}],[0.778',621,'75',',0.683,0,{krn:{"','116','":-0.0278,"','67',710,'79',710,'71',710,'85',710,'81',710,'84":-0.0833,"89":-0.0833,"86":-0.111,"87":-0.111}}],[0.','708',595,'722',595,'764',708,'88',710,'87',710,'65',710,'86',710,'89":-',! '0.0278}}],[0.','681',595,'653',',0.683,0,{','krn:{"111":-0.08! 33,"101" :-0.0833,"117":-0.0833,"114":-0.0833,"97":-0.0833,"65":-0.111,"79":-0.0278,"67":-0.0278,"71":-0.0278,"81":-0.0278}}],[','0.785',595,'75',595,'361',708,'73":',737,'514',595,'778',708,'79',710,'67',710,'71',710,'81":-',737,'625',708,721,'917',595,'75',595,'778',708,'88',710,'87',710,'65',710,'86',710,'89":-',737,'681',708,'65','":-0.0833,"','111',710,'101',710,'97',710,'46',785,'44":-0.','0833}}],[0.','778,0.683',631,'736',708,'116',710,'67',710,'79',710,'71',710,'85',710,'81',710,721,'556',595,'722',708,'121',710,'101',785,'111',785,'114',785,'97',785,'65',785,'117":-0.',795,'75',595,'75',',0.683,0,{ic:0.','0139,',742,'1.03',834,'0139,',742,'0.75',708,'79',710,'67',710,'71',710,'81":-',737,'75',834,'025,krn:{"101',785,'111',785,'114',785,'97',785,'65',785,829,795,'611',595,'278',670,'5',621,'278',670,'5',621,'278,0.668],[0.','278',654,'92}}],[0.5',650,'krn:{"118',710,'106":0.0556,"121',710,'119":-',737,'556,0.','694,0',',{krn:{"101":0.0278,"111":0.0278,"120":-0.0278,"100":0.0! 278,"99":0.0278,"113":0.0278,"118":-0.0278,"106":0.0556,"121":-0.0278,"119":-0.0278}}],[0.','444',650,'krn:{"104',710,'107":-',737,'556',621,'444',629,'306',617,618,'12,"102":11,"108":13}}],[0.5',',0.431,0.194,{','ic:0.','0139,krn:{"','106":',737,'556',666,'116',710,'117',710,'98',710,'121',710,'118',710,'119":-',737,875,'306,0.668',631,'528',666,'97','":-0.0556,"','101',710,'97',710,'111',710,'99":-',737,'278',621,'833',650,'krn:{"116',710,'117',710,'98',710,'121',710,'118',710,'119":-',737,'556',650,'krn:{"116',710,'117',710,'98',710,'121',710,'118',710,'119":-',737,'5,0.431',',0',888,886,'431,0.194',888,'528,0.431',631,'392',629,'394',629,'389,0.615,0,{krn:{"121',710,'119":-',737,'556',650,'krn:{"119":-',737,'528',',0.431,0,{ic:0.',905,'97',928,'101',710,'97',710,'111',710,'99":-',737,'722',988,905,'101',710,'97',710,'111',710,'99":-',737,'528',629,'528',903,'ic:0.',905,'111',710,'101',710,'97',710,'46',785,'44":-0.',795,'444',629,'5',988,'0278,',679,'124}}],[1',988,'027! 8}],[0.','5',621,'5,0.668],[0.5,0.668]],cmmi10:[[0.615',834,'1! 39',',kr n:{"61":-0.0556,"59":-0.111,"58":-0.111,"127":0.',795,'833',708,'127":0.167}}],[0.','763',834,1031,'krn:{"127":0.0833}}],[0.','694',708,1045,'742',834,'0757,',1049,'831',834,'0812,krn:{"61',928,'59":-0.0556,"58":-0.0556,"127":0.','0556}}],[0.','78',834,'0576,',1049,'583',834,'139',1041,1062,'667',741,1049,'612',834,'11,krn:{"61',928,1061,1062,'772',834,'0502,',1049,'64',988,'0037,','krn:{"127":',737,'566',',0.694,0.194,{ic:0.','0528,',1049,'518',903,'ic:0.0556}],[0.444',617,'0378,krn:{"',1061,1062,'406',650,'krn:{"127":0.0556}}],[0.','438',1091,'0738,',1049,'497',903,'ic:0.0359',',',1103,'469',617,1031,1049,'354',650,1103,'576',629,'583',621,'603',903,1088,737,'494',988,'0637,krn:{"',1061,'0278}}],[0.','438',1091,'046,',1088,'0.111}}],[0.','57',988,'0359}],[0.','517',903,1049,'571',988,'0359,krn:{"59',928,'58":-0.',1062,'437',988,'113,krn:{"',1061,1132,'54',988,'0359,',1088,737,'596,0.694,0.194,{',1049,'626',903,1103,'651',1091,'0359,',1088,1137,'622',988,1140,'466',650,1049! ,'591',',0.694,0,{',1049,'828',988,1035,'517',903,1049,'363,0.431,0.0972,{ic:0.0799,',1049,'654',903,1088,'0.0833}}],[1',700,'1',700,'1',700,'1',700,649,',0.464,-0.0363],[',649,1199,'0.5,0.465,-0.0347],[',1202,'0.5',629,'5',629,'5',629,967,631,967,631,967,',0.194',685,971,685,'431',631,'278,0.106],[',675,631,'778',',0.539,0.0391],[','0.5,0.75,0.25,{krn:{"1',928,'65',928,'77',928,'78',928,'89":0.0556,"90":-0.',1062,'778',1225,1202,'0.531',617,'0556,',1049,'75',708,'127":','0.','139}}],[0.','759',834,'0502,',1049,'715',834,'0715,krn:{"61',710,1061,795,'828',834,1031,1103,'738',834,'0576,',1049,'643',834,'139',1041,795,'786',741,1049,'831',834,'0812,krn:{"61',928,1061,1062,'44',834,'0785,',1088,1137,'555',834,'0962',1041,'167}}],[0.','849',834,'0715,krn:{"61',928,1061,1062,'681',708,1245,737,'97',834,'109,krn:{"','61',928,1061,795,'803',834,1302,'61',785,'61',710,1061,795,'763',834,1031,1049,'642',834,'139',1041,795,'791,0.683,0.194,{',1049,'759',834,'00773,',1049,'613',834,'0! 576,krn:{"61',928,1061,795,'584',834,'139,krn:{"61',710,1061,7! 95,'683' ,834,1302,'59":-0.111,"',1148,'111,"61',928,1245,737,'583',834,'222',',krn:{"59":-0.167,"58":-0.167,"61":-0.111}}],[0.','944',834,'139',1355,'828',834,'0785,krn:{"61',785,'61',710,1061,795,'581',834,'222',1355,'683',834,'0715,krn:{"61',928,1061,795,'389,0.75],[0.389',658,'0.389',658,'1,0.358,-0.142],[',1382,'0.417',1177,1088,1137,'529',629,'429',621,'433',650,1103,'52',666,'89":0.0556,"90',928,'106":-0.111,"102":-0.167,"',1045,'466',650,1103,'49',1091,'108,krn:{"',1061,1289,'477',903,1110,',',1088,737,'576',666,'127":-',737,'345,0.66],[0.412,0.66,0.194,{ic:0.0572,krn:{"59',928,1148,1062,'521',617,'0315}],[0.298',617,'0197,',1049,'878',629,'6',629,'485',650,1103,'503',903,1049,'446',903,1110,',',1049,'451',988,1031,'krn:{"',1061,1062,'469',650,1103,'361,0.615,0,{',1049,'572',650,1088,737,'485',988,'0359,',1088,737,'716',988,'0269,',1049,'572',650,1088,737,'49',903,1110,',',1103,'465',988,'044,',1103,'322',650,1088,737,'384',903,1049,'636',903,1088,1137,'5,0.714,0,{ic:0.154}],! [',649,617,'399}]],cmsy10:[[',674,649,',0.444,-0.0556],[',674,1202,674,'0.5,0.444,-0.0556],[',674,674,674,674,674,674,674,'1',658,1502,1502,'0.778',1199,'0.778,0.464,-0.0363','],[0.778,0.636,0.136',1517,1517,1517,1517,1517,'],[0.778',700,'0.778,0.483,-0.0169],[0.778',1225,'0.778',1225,'1',1225,'1',1225,'0.778',1225,'0.778',1225,'1',700,'1',700,'0.5',658,'0.5',658,'1',700,'1',658,'1',658,'0.778',1199,'1',700,'1',700,'0.611',658,'0.611',658,'1',700,'1',658,'1',658,'0.778',629,'275,0.556],[1',629,'667',1225,'0.667',1225,'0.889',658,'0.889',658,'0',658,'0',700,'0.556',621,'556',621,'667',629,'5',660,'722',621,'722',621,'778',621,'778',621,'611',621,'798',708,'48":0.','194}}],[0.657',834,'0304',',krn:{"48":0.',1247,'527',834,'0583',1607,1247,'771',834,'0278',1607,795,'528',834,'0894',1607,'111}}],[0.','719',834,'0993',1607,1623,'595',',0.683,0.0972,{ic:0.','0593',1607,1623,'845',834,'00965',1607,1623,'545',834,'0738,krn:{"48":',737,'678',1630,'185',1607,1289,'762',834,'0144',160! 7,1062,'69',708,1603,'139}}],[1.2',708,1603,1247,'82',834,'147! ',1607,7 95,'796',834,1616,1607,1623,'696',834,'0822',1607,795,'817,0.683,0.0972,{krn:{"48":',1137,'848',708,1603,795,'606',834,'075',1607,1247,'545',834,'254,krn:{"48":',737,'626',834,'0993',1607,795,'613',834,'0822,krn:{"48":',737,'988',834,'0822',1607,795,'713',834,'146',1607,1247,'668',1630,'0822',1607,795,'725',834,'0794',1607,1247,'667,0.556],[0.',1719,1719,1719,1719,'611',621,'611',621,'444',670,'444',670,'444',670,'444',670,'5',670,'5',670,'389',670,'389',670,'278',670,'5',670,'5',670,'611',670,'5',670,'278',658,'0.833,0.04,0.96],[0.75',595,'833',595,'417',1091,'111}],[0.',1719,'667,0.556',1517,1517,'],[0.444',658,'0.444',658,'0.444',658,'0.611',658,'0.778,0.694,0.13','],[',1775,'],[',1775,'],[',1775,']],cmex10:[[0.458',',0.04,1.16,{n:','16}],[0.458',1783,'17}],[0.417',1783,'104}],[0.417',1783,'105','}],[0.472,0.04,1.16,{n:','106',1791,'107',1791,'108',1791,'109}],[0.583',1783,'110}],[0.583',1783,'111',1791,'68',1791,'69}],[0.333',',0,0.6,{delim:{','rep:12}}],[0.556',1807,'re! p:13}}],[0.578',1783,'46}],[0.578',1783,'47}],[0.597',',0.04,1.76,{n:','18}],[0.597',1815,'19}],[0.736',',0.04,2.36,{n:','32}],[0.736',1819,'33}],[0.528',1819,'34}],[0.528',1819,'35}],[0.583',1819,'36}],[0.583',1819,'37}],[0.583',1819,'38}],[0.583',1819,'39}],[0.75',1819,'40}],[0.75',1819,'41}],[0.75',1819,'42}],[0.75',1819,'43}],[1.04',1819,'44}],[1.04',1819,'45}],[0.792',',0.04,2.96,{n:','48}],[0.792',1847,'49}],[0.583',1847,'50}],[0.583',1847,'51}],[0.639',1847,'52}],[0.639',1847,'53}],[0.639',1847,'54}],[0.639',1847,'55}],[0.806',1847,'56}],[0.806',1847,'57}],[0.806',',0.04,2.96],[','0.806',1867,'1.28',1867,'1.28',1867,'0.811',1815,'30}],[0.811',1815,'31}],[0.875',',0.04,1.76,{delim:{top:','48,bot:64,rep:66}}],[0.875',1879,'49,bot:65,','rep:67}}],[0.','667',1879,'50,bot:52,rep:54}}],[0.667',1879,'51,bot:53,rep:55','}}],[0.667,0.04,1.76,{delim:{bot:','52,rep:54',1889,'53,rep:55}}],[0.667',1807,'top:50,rep:54}}],[0.667',1807,'top:51,rep:55','}}],[0.889,0,0.9,{delim:{top:'! ,'56,mid:60,bot:58,rep:62',1897,'57,mid:61,bot:59,rep:62',1897! ,'56,bot :58,rep:62',1897,'57,bot:59,rep:62','}}],[0.889,0,1.8,{delim:{rep:','63',1905,'119}}],[0.889,0,0.3,{delim:{','rep:62}}],[0.','667',1807,'top:120,','bot:121,rep:63}}],[0.','875',1879,'56,bot:59,',1909,'875',1879,'57,bot:58,',1909,'875',1807,'rep:66}}],[0.875',1807,1883,'611',1815,'28}],[0.611',1815,'29','}],[0.833,0,1,{n:','71}],[1.11',',0.1,1.5],[','0.472,0,1.11,{ic:0.194,n:','73}],[','0.556,0,2.22,{ic:0.444}],[1.11,0',',1,{n:75}],[1.51,0','.1,1.5],[1.11,0',',1,{n:77}],[1.51,0',1939,',1,{n:79}],[1.51',1934,'1.06,0,1,{n:88}],[0.944,0,1,{n:89}],[',1935,'90',1932,'91',1932,'92',1932,'93',1932,'94',1932,'95}],[1.44',1934,'1.28',1934,1937,1939,1939,1939,1939,'.1,1.5],[0.944,0,1,{n:97}],[1.28',1934,'0.556,0.722,0,{n:','99','}],[1,0.75,0,{n:','100}],[1.44,0.75],[',1967,'102',1969,'103}],[1.44,0.75],[0.472',1815,'20}],[0.472',1815,'21}],[0.528',1815,'22}],[0.528',1815,'23}],[0.528',1815,'24}],[0.528',1815,'25}],[0.667',1815,'26}],[0.667',1815,'27}],[1',1783,'113}],[1',1815,'114}],[1! ',1819,'115}],[1',1847,'116}],[1.06,0,1.8,{delim:{top:118,bot:116,rep:117}}],[1.06,0,0.6],[1.06,0.04,0.56],[0.778',1807,'top:126,','bot:127,rep:119','}}],[0.667',1807,1912,'rep:63}}],[0.667',1807,1913,'45,0.12],[0.',2008,2008,2008,'778',1807,2000,'rep:119}}],[0.778',1807,2001,'}}]],cmti10:[[0.627',834,'133}],[0.818',595,'767',834,'094}],[0.692',595,'664',834,'153}],[0.743',834,'164}],[0.716',834,'12}],[0.','767',834,1762,'716',834,'0599}],[0.767',834,1762,'716',834,'103}],[0.','613',1091,'212,krn:{"39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":',619,'562',1091,2043,'588',1091,2043,'882',1091,2043,'894',1091,2043,'307',988,'0767}],[0.332',903,'ic:0.0374}],[0.511',621,'511',617,'0969','}],[0.511,0.','628,0,{ic:0.083}],[0.511',617,'108',2069,'562,0,{ic:0.',2043,'831',621,'46,0,0.17],[0.537',1091,'105}],[0.716',988,'0751}],[0.716',988,'0751',2069,'528,0.0972,{ic:0.0919}],[0.883',834,2032,'985',834,2032,'767,0.732,0.0486,{ic:0.094}],[0.256',650,651,'256,"76":! -0.321}}],[0.307',617,'124,lig:{"96":60}}],[0.514',617,'0696}]! ,[0.818' ,1091,'0662}],[0.769',621,'818,0.75,0.0556',',{ic:0.136}],[0.','767',617,'0969}],[0.307',617,'124,krn:{"63":0.102,"33":0.102',668,'409',',0.75,0.25,{ic:0.162}],[0.','409,0.75,0.25,{ic:0.0369',2069,'75,0,{ic:0.149}],[0.767,0.562,0.0567,{ic:0.0369}],[0.307,0.106',631,'358',988,'0283,',679,'123}}],[0.307,0.106],[0.511',2112,'511,0.644,0',',{ic:0.136}],[0.511,0.644,0',2124,2124,2124,'.194',2124,2124,2124,'.194',2124,2124,2104,'307',988,'0582}],[0.307',903,'ic:0.0582}],[0.307,0.5,0.194,{ic:0.0756}],[0.767,0.367,-0.133,{ic:0.0662',2069,'5',631,'511',617,'122,lig:{"96":62}}],[0.767',617,'096}],[0.743',708,'110','":-0.0256,"','108',2151,'114',2151,'117',2151,'109',2151,'116',2151,'105',2151,'67',2151,'79',2151,'71',2151,'104',2151,'98',2151,'85',2151,'107',2151,'118',2151,'119',2151,'81',2151,'84":-0.0767,"89":-0.0767,"86":-0.102,"87":-0.102,"101','":-0.0511,"','97',2185,'111',2185,'100',2185,'99',2185,'103',2185,'113":-0.0511}}],[0.704',834,2043,'716',834,'145}],[0.755',834,'094,kr! n:{"88',2151,'87',2151,'65',2151,'86"'], - [':-0.0256,"89','":-0.0256}}],[0.','678,0.683',',0,{ic:0.','12}],[0.','653,0.683',3,'133',',krn:{"','111','":-0.0767,"','101',10,'117',10,'114',10,'97',10,'65','":-0.102,"','79','":-0.0256,"','67',22,'71',22,'81',1,'774,0.683',3,'0872}],[0.743,0.683',3,'164}],[0.','386,0.683',3,'158}],[0.525,0.683',3,'14}],[0.769,0.683',3,'145',8,'79',22,'67',22,'71',22,'81',1,'627,0.683,0,{krn:{"84',10,'89',10,'86',20,'87',20,'101":-0.0511,"97":-0.0511,"111":-0.0511,"100":-0.0511,"99":-0.0511,"103":-0.0511,"113":-0.0511}}],[0.','897,0.683',3,33,'743,0.683',3,33,'767,0.683',3,'094',8,'88',22,'87',22,'65',22,'86',22,'89',1,'678,0.683',3,'103',8,'65":-0.0767}}],[0.','767,0.683',',0.194,{ic:0.','094}],[0.729,0.683',3,'0387',8,'110',22,'108',22,'114',22,'117',22,'109',22,'116',22,'105',22,'67',22,'79',22,'71',22,'104',22,'98',22,'85',22,'107',22,'118',22,'119',22,'81',22,'84',10,'89',10,'86',20,'87',20,58,'562,0.683',3,4,'716,0.683',3,'133',8,'121',10,'101',10,'111',10,'114',10,'97',10,'117',! 10,83,'743,0.683',3,33,'743,0.683',3,'184',8,'111',10,'101',10,'117',10,'114',10,'97',10,'65',20,'79',22,'67',22,'71',22,'81',1,'999,0.683',3,'184',8,83,'743,0.683',3,'158',8,'79',22,'67',22,'71',22,'81',1,'743,0.683',3,'194',8,'101',10,'111',10,'114',10,'97',10,'117',10,83,'613,0.683',3,'145','}],[0.307,0.','75,0.25',',{ic:0.188}],[0.514',',0.694',3,'169',215,216,',{ic:0.105','}],[0.511,0.','694',3,'0665',215,'668',3,'118',215,'694',3,'124,lig:{"96":92}}],[0.511,0.431',3,'0767}],[0.','46',218,3,'0631',8,58,'46,0.431',3,'0565',8,58,'511',218,3,'103',8,'108":0.0511}}],[0.','46,0.431',3,'0751',8,58,'307',218,85,'212',8,'39":0.104,"63":0.104,"33":0.104,"41":0.104,"93":0.104},lig:{"105":','12,"102":11,"108":13}}],[0.','46,0.431',85,'0885',224,'694',3,'0767',215,'655',3,'102',215,'655',85,'145}],[0.46',218,3,'108}],[0.','256',218,3,'103',8,254,'818,0.431',3,237,'562,0.431',3,273,8,'39":-0.102}}],[0.511,0.431',3,'0631',8,58,'511,0.431',85,'0631',8,58,'46,0.431',85,'0885}],[0.','4! 22,0.431',3,'108',8,58,'409,0.431',3,'0821}],[0.332,0.615',3,'! 0949}],[ 0.537,0.431',3,237,'46,0.431',3,284,'664,0.431',3,'108',8,254,'464,0.431',3,4,'486,0.431',85,310,'409,0.431',3,'123',224,'431',3,'0921',',lig:{"45":124}}],[1.','02,0.431',3,343,224,'694',3,'122',224,'668',3,'116',224,'668',3,'105}]],cmbx10:[[0.692',',0.686],[0.','958',360,'894',360,'806',360,'767',360,'9',360,'831',360,'894',360,'831',360,'894',360,'831',360,'671',218,3,'109,krn:{"39":0.109,"63":0.109,"33":0.109,"41":0.109,"93":0.109},lig:{"105":','14,"108":15}}],[0.639',',0.694],[0.','639',386,'958',386,'958',386,'319',',0.444],[0.','351',',0.444,0.194','],[0.575,0.','694',397,'694',397,'632',397,'694',397,'596],[0.869',386,'511,0,0.17],[0.597',386,'831',394,'894,0.444',397,'542,0.0972],[1.04,0.686],[1.17',360,'894,0.735,0.0486],[0.319',',0.444,0,{krn:{"','108":-0.319,"76":-0.378}}],[0.35',',0.694,0,{lig:{"96":','60}}],[0.603',386,'958',218,',0.194',397,'75,0.0556],[0.','958,0.',426,'894',386,'319',',0.694,0,{krn:{"','63":0.128,"33":0.128},lig:{"39":34}}],[0.447,0.',216,'],! [0.447,0.',216,397,'75],[0.894,0.633,0.133],[','0.319,0.156',',0.194],[0.','383,0.444,0,{lig:{"45":123}}],[',439,397,216,397,'644',397,'644',397,'644',397,'644',397,'644',397,'644',397,'644',397,'644',397,'644',397,'644],[0.319',394,'319',396,'],[0.35,0.5',440,'894,0.391,-0.109],[0.543,0.5',440,'543',419,'62}}],[0.894',386,'869',',0.686,0,{krn:{"','116','":-0.0319,"','67',479,'79',479,'71',479,'85',479,'81',479,'84":-0.0958,"89":-0.0958,"86":-0.128,"87":-0.128}}],[','0.818',360,'831',360,'882',477,'88',479,'87',479,'65',479,'86',479,'89','":-0.0319}}],[0.','756',360,'724,0.686,0,{','krn:{"111":-0.0958,"101":-0.0958,"117":-0.0958,"114":-0.0958,"97":-0.0958,"65":-0.128,"79":-0.0319,"67":-0.0319,"71":-0.0319,"81":-0.0319}}],[','0.904',360,'9',360,'436',477,'73','":0.0319}}],[0.','594',360,'901',477,'79',479,'67',479,'71',479,'81',506,'692',477,490,'1.09',360,'9',360,'864',477,'88',479,'87',479,'65',479,'86',479,'89',506,'786',477,'65":-0.0958,"111',479,'101',479,'97',479,'46":! -0.0958,"44":-0.0958}}],[0.','864,0.686',440,'862',477,'116',4! 79,'67', 479,'79',479,'71',479,'85',479,'81',479,490,'0.639',360,'8',477,'121',479,'101":-0.0958,"111":-0.0958,"114":-0.0958,"97":-0.0958,"65":-0.0958,"117":-0.0958}}],[0.','885',360,'869,0.686',3,'016,',510,'1.19,0.686',3,'016,',510,'0.869',477,'79',479,'67',479,'71',479,'81',506,'869,0.686',3,'0287',8,582,'703',360,'319,0.',216,'],[0.603',386,610,216,397,'694],[0.319',386,'319',419,'92}}],[0.559',417,'118',479,'106":0.0639,"121',479,'119',506,'639',218,',0',',{krn:{"101":0.0319,"111":0.0319,"120":-0.0319,"100":0.0319,"99":0.0319,"113":0.0319,"118":-0.0319,"106":0.0639,"121":-0.0319,"119":-0.0319}}],[0.','511',417,'104',479,'107',506,'639',386,'527',394,'351',218,3,384,266,'575,0.444',85,'016',8,'106',518,'639',432,'116',479,'117',479,'98',479,'121',479,'118',479,'119',506,'319',386,'351',218,440,'607',432,'97":-0.0639,"101',479,'97',479,'111',479,'99',506,'319',386,'958',417,'116',479,'117',479,'98',479,'121',479,'118',479,'119',506,'639',417,'116',479,'117',479,'98',479,'121',479,! '118',479,'119',506,'575,0.444,0',632,'639',396,632,'607',396,'],[0.474',394,'454',394,'447,0.635,0,{krn:{"121',479,'119',506,'639',417,'119',506,'607,0.444',3,'016',8,'97":-0.0639,"101',479,'97',479,'111',479,'99',506,'831,0.444',3,'016',8,'101',479,'97',479,'111',479,'99',506,'607',394,732,85,'016',8,'111',479,'101',479,'97',479,558,'511,0.444',397,'444',3,'0319',344,'15,0.444',3,'0319}],[0.575',218,397,'694',397,'694]]};','jsMath.Img','={fonts:[50,60,70,85,100,120,144,173,207,249,298,358,430],w:{"50":6.9,"60":8.3,"70":9.7,"85":11.8,"100":13.9,"120":16.7,"144":20,"173":24,"207":28.8,"249":34.6,"298":41.4,"358":49.8,"430":59.8},best:4,update:{},factor:1,loaded:0,SetFont',':function(','_a9){for(var _aa in _a9){if(!','this.update[_aa',']){',787,']=[];}',787,']=',787,'].concat(_a9[_aa]);}},AddFont',785,'_ab,def){if(!',783,'[_ab]){',783,'[_ab]={};}','jsMath.Add(jsMath.','Img[_ab],def);},UpdateFonts',':function(){','var _ad=this.update;','if(!this.loaded){','return;}var ','_ae=! this[','jsMath.Img.fonts','[this.best]];','for(var _af in _ad)! {','for( var i=0;i<','_ad[_af].length;i++){var c=_ad[_af][i];if(c=="all"){for(c in ','jsMath.TeX[','_af]){',813,'_af][c].img','={};}}else{',813,816,'={};}}}this.update={};},BestSize',803,'var w=jsMath.em*this.factor;var m=','this.w[this.fonts[','0]];for(var i=1;i<','this.fonts','.length;i++){','if(w<(',823,'i]]+2*m)/3){return i-1;}m=',823,'i]];}return i-1;},Scale',803,805,'return;}this.best=this.BestSize();this.em=',783,'.w[',825,809,'this.scale','=(jsMath.em/this.em);','if(Math.abs(',839,'-1)<0.12){',839,'=1;}},URL',785,'_b5,_b6,C){var _b8=(','jsMath.Controls.cookie.','alpha)?"/alpha/":"/plain/";if(C==null){C="def.js";}else{C="char"+C+".png";}if(_b6!=""){_b6+="/";}','return this.','root+_b5+_b8+_b6+C;},LoadFont',785,'_b9){',805,'this.Init();}jsMath.Setup.Script(this.URL(_b9,""));},Init',803,'if(',848,'print||',848,'stayhires','){',848,'print=',848,861,';this.factor*=3;if(!','jsMath.Controls.','isLocalCookie||!jsMath.Global.isLocal){',868,'SetCookie(0);}if(','jsMath.Browser.','alphaP! rintBug){',848,'alpha=0;}}var _ba="0123456789ABCDEF";this.HexCode=[];',811,'128;i++){var h=Math.floor(i/16);var l=i-16*h;this.HexCode[i]=_ba.charAt(h)+_ba.charAt(l);}this.loaded=1;}};jsMath.HTML={Em',785,'m){var n=5;if(m<0){n++;}',841,'m)<0.000001){m=0;}var s=String(m);s=s.replace(/(\\.\\d\\d\\d).+/,"$1");return s+"em";},Spacer',785,'w){if(w==0){return "";}return ',872,'msieSpaceFix+"<','span class=\\"spacer\\" style=\\"margin-','left:"+this.Em(','w)+"\\"></span>";},Blank',785,'w,h,d,_c5){var _c6="";var _c7="";if(_c5){_c7+="border-',887,'w)+" solid;";if(',872,'widthAddsBorder){w=0;}}if(w==0){if(',872,'blankWidthBug){_c7+="width:1px;";_c6="<',886,'right:-1px\\"></span>";}}else{_c7+="width:"+this.Em(w)+";";}if(d==null){d=0;}if(h){var H=this.Em(h+d);if(_c5&&h*jsMath.em<1.5){H="1px";h=1/jsMath.em;}_c7+="height:"+H+";";}if(',872,'mozInlineBlockBug){d=-h;}if(',872,'msieBorderBug&&!_c5){d-=jsMath.d;}if(d){_c7+="','vertical-align:"+','this.Em(-d);}return _c6+"<span class=\\"blank\\! " ','style=\\""+','_c7+"\\"></span>";},Rule',785,'w,h){if(h==n! ull){h=j sMath.TeX.default_rule_thickness;}',850,'Blank(w,h,0,1);},Class',785,'_cb,_cc){return "<span class=\\""+_cb+"\\">"+_cc+"</span>";},Place',785,'_cd',',x,y){if(Math.abs(x)<0.0001){x=0;}if(Math.abs(y)<0.0001){y=0;}','if(x||y){var _d0','="<span style=\\"position',': relative',';";if(x){_d0+=" margin-',887,'x)+";";}if(y){_d0+=" ','top:"+this.Em','(-y)+";";}_cd=_d0+"\\">"+_cd','+"</span>";}','return _cd;},PlaceSeparateSkips',785,'_d1',915,'if(y){_d1',917,918,'; ',922,'(-y)+";"+"\\">"+_d1',924,'if(x){_d1=this.Spacer(x)+_d1',';}return ','_d1;},PlaceAbsolute',785,'_d4',915,'_d4',917,':absolute','; ',887,'x)+"; "+"',922,'(y)+";\\">"+_d4+" </span>";return _d4;},Absolute',785,'_d7,w,h,d,y,H){if(y!="none"){',841,'y)<0.0001){y=0;}_d7',917,944,'; "+"top:"+','jsMath.HTML.','Em(y)+"; left:0em',';\\">"+_d7','+" "+"</span>";}if(d=="none"){d=0;}_d7+=this.Blank(w,h-d,d);if(',872,'msieAbsoluteBug){_d7',917,':relative',959,924,'if(',872,'spanHeightVaries){','var _dd="";_dd=',872,'msieInl! ineBlockFix+" width:"+',957,'Em(w)+";";if(',872,'quirks){_dd+=" height:"+',957,'Em(H)+";";}','else{_dd+=" height: 0px;"+" ',903,957,978,'_d7',917,964,';"+_dd+"\\">"+_d7',924,'else{_d7',917,964,'\\">"+_d7',924,'return _d7;}};jsMath.Box=function(_de,_df,w,h,d){if(d==null){d=jsMath.d;}this.type="typeset";this.w=w;this.h=h;this.d=d;this.bh=h;this.bd=d;this.x=0;this.y=0;','this.html=','_df;this.format=_de;};',801,'Box,{defaultH:0,Null',803,'return new jsMath.','Box("null","",0,0,0);},Text',785,'_e3,_e4,_e5,_e6,a,d){var _e9=','jsMath.Typeset.AddClass(','_e4,_e3);_e9=','jsMath.Typeset.','AddStyle(','_e5,_e6,_e9);var BB','=jsMath.EmBoxFor(','_e9);var TeX=',1005,'TeX(_e5,_e6);var bd=((_e4=="cmsy10"||_e4=="cmex10")?BB.h-TeX.h:TeX.d*BB.h/TeX.hd);var ','box=new jsMath.Box("','text",_e3,BB.w,BB.h-bd,bd);box.style=_e5;box.size=_e6;','box.tclass','=_e4;if(d!=null){box.d=d','*TeX.scale;}else{box.','d=0;}if(a==null||a==1){box.h=0.9*TeX.M_height;}else{box.h=1.1*TeX.x_height+TeX.scale*a;}','r! eturn box;},','TeX',785,'C,_ef,_f0,_f1){var c=',813,'_ef][C];i! f(','c.d ==null){c.d=0;}','if(c.h','==null){c.','h=0',';}if(c.img!=null','&&c.c!=""){this.TeXIMG(_ef,C,',1005,'StyleSize(_f0,_f1));}var _f3=',1005,'TeX(_f0,_f1).scale;var h=c.h+',813,'_ef].dh;var ',1012,'text",c.c,c.w*_f3,h*_f3,c.d*_f3);box.style=_f0;box.size=_f1;if(c.tclass){',1014,'=c.tclass;if(c.img){box.bh=c.img.bh;box.bd=c.img.bd;}else{box.bh=_f3*jsMath.h;box.bd=_f3*jsMath.d;}}else{',1014,'=_ef;box.bh=_f3*',813,'_ef].h;box.bd=_f3*',813,'_ef].d;if(',872,'msieFontBug&&box.html.match(/&#/)){box.html+="<span style=\\"display:none\\">x</span','>";}}',1018,'TeXfallback',785,'C,_f7,_f8,_f9','){var c=',813,'_f7][C];if(!c.tclass){c.tclass=_f7',1028,'){',850,'TeXnonfallback(',1052,');}if(c.h!=null&&c.a',1026,'a=c.h-1.1*jsMath.TeX.x_height;}var a=c.a;var d=c.d;var box=this.Text(c.c,c.tclass,_f8,_f9,a,d);var _fe=',1005,'TeX(_f8,_f9).scale;if(c.bh!=null){box.bh=c.bh*_fe;box.bd=c.bd*_fe;}else{var h=box.bd+box.bh;','var html=',1003,1014,',box.html);html=',1005,1006,'_f8,_f9,html);box.bd',1008,! 'html+',957,'Blank(1,h)).h-h;box.bh=h-box.bd;if(_fe==1){c.bh=box.bh;c.bd=box.bd;}}if(jsMath.',1047,'>";}',1018,'TeXIMG',785,'font,C,size){var c=',813,'font][C];if(c.img.size!=null&&c.img.size==size&&c.img.best!=null&&c.img.best==','jsMath.Img.best','){',806,'_105=(',783,'.scale!=1);var id=',1085,'+size-4;if(id<0){id=0;_105=1;}else{if(id>=',808,'.length){','id=',808,'.length-1',';_105=1;}}var _107=',783,'[',808,'[id]];var img=_107[font][C];var _109=1/',783,'.w[',808,'[id]];if(id!=',1085,'+size-4){if(c.w!=null){_109=c.w/img[0];}else{_109*=',808,'[size]/',808,'[4]*',808,'[',1085,']/',808,'[id];}}var w=img[0]*_109;var h=img[1]*_109;var d=-img[2]*_109;var v;var _10e=(c.w==null||Math.abs(c.w-w)<0.01)?"":" margin-right:"+',957,'Em(c.w-w)+";";var _10f="";C=',783,'.HexCode[C];if(!_105&&!',848,'scaleImg){if(2*w<h||(',872,'msieAl... [truncated message content] |
From: dpvc v. a. <we...@ma...> - 2007-08-30 02:51:22
|
Log Message: ----------- Make sure Formula's value() method doesn't produce an error when it can't extact the separate entries of the formula. Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.110 retrieving revision 1.111 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.110 -r1.111 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -1988,6 +1988,7 @@ # sub value { my $self = shift; + return $self unless defined $self->{tree}{coords}; my $context = $self->context; my @array = (); if ($self->{tree}->type eq 'Matrix') { |
From: dpvc v. a. <we...@ma...> - 2007-08-30 02:44:14
|
Log Message: ----------- Disassemble Formula's properly when used with multiple variables in substitute(), etc. For example: $f->substitute(['x','y'] => Formula("xy,x^2-y^2")); Modified Files: -------------- pg/lib: Parser.pm Revision Data ------------- Index: Parser.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser.pm,v retrieving revision 1.47 retrieving revision 1.48 diff -Llib/Parser.pm -Llib/Parser.pm -u -r1.47 -r1.48 --- lib/Parser.pm +++ lib/Parser.pm @@ -736,7 +736,9 @@ while (scalar(@_)) { $xref = shift; $value = shift; if (ref($xref) eq "ARRAY") { - $value = [@{$value->{data}}] if Value::isValue($value); + $value = Value::makeValue($value,context=>$context) unless ref($value); + $value = [$value->value] if Value::isValue($value); + $value = @{$value}[0,1] if Value::classMatch("Interval"); $value = [$value] unless ref($value) eq 'ARRAY'; } else { $xref = [$xref]; $value = [$value]; |
From: dpvc v. a. <we...@ma...> - 2007-08-30 02:26:12
|
Log Message: ----------- Allow specification of values for multiple variables a once for eval(), substitute() and so on. You can use $f->eval(['x','y'] => Point(1,2)); or $f->eval(['x','y'] => [1,2]); or $P = Point(1,2); $f->eval(['x','y'] => $P); to do the equivalent of $f->eval(x=>1,y=>2); Note that you must use quotes for the variable names and that they must be enclosed in square brackets, not parentheses, when you supply more than one variable. You can combine single with multiple variables, as in $f->eval(['x','y']=>Point(1,2),z=>3); Modified Files: -------------- pg/lib: Parser.pm Revision Data ------------- Index: Parser.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser.pm,v retrieving revision 1.46 retrieving revision 1.47 diff -Llib/Parser.pm -Llib/Parser.pm -u -r1.46 -r1.47 --- lib/Parser.pm +++ lib/Parser.pm @@ -730,18 +730,28 @@ # Sets the values of variables for evaluation purposes # sub setValues { - my $self = shift; my ($value,$type); my $context = $self->context; + my $self = shift; my ($xref,$value,$type); + my $context = $self->context; my $variables = $context->{variables}; - $self->{values} = {@_}; - foreach my $x (keys %{$self->{values}}) { - $self->Error(["Undeclared variable '%s'",$x]) unless defined $variables->{$x}; - $value = Value::makeValue($self->{values}{$x},context=>$context); - $value = $self->Package("Formula")->new($context,$value) unless Value::isValue($value); - ($value,$type) = Value::getValueType($self,$value); - $self->Error(["Variable '%s' should be of type %s",$x,$variables->{$x}{type}{name}]) - unless Parser::Item::typeMatch($type,$variables->{$x}{type}); - $value->inContext($self->context) if $value->context != $self->context; - $self->{values}{$x} = $value; + while (scalar(@_)) { + $xref = shift; $value = shift; + if (ref($xref) eq "ARRAY") { + $value = [@{$value->{data}}] if Value::isValue($value); + $value = [$value] unless ref($value) eq 'ARRAY'; + } else { + $xref = [$xref]; $value = [$value]; + } + foreach my $i (0..scalar(@$xref)-1) { + my $x = $xref->[$i]; my $v = $value->[$i]; + $self->Error(["Null value can't be assigned to variable '%s'",$x]) unless defined $v; + $self->Error(["Undeclared variable '%s'",$x]) unless defined $variables->{$x}; + $v = Value::makeValue($v,context=>$context); + ($v,$type) = Value::getValueType($self,$v); + $self->Error(["Variable '%s' should be of type %s",$x,$variables->{$x}{type}{name}]) + unless Parser::Item::typeMatch($type,$variables->{$x}{type}); + $v->inContext($self->context) if $v->context != $self->context; + $self->{values}{$x} = $v; + } } } |
From: Mike G. v. a. <we...@ma...> - 2007-08-29 18:44:00
|
Log Message: ----------- Sort classes before presenting them in configuration web page. Modified Files: -------------- wwmoodle/wwassignment: lib.php Revision Data ------------- Index: lib.php =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwassignment/lib.php,v retrieving revision 1.24 retrieving revision 1.25 diff -Lwwassignment/lib.php -Lwwassignment/lib.php -u -r1.24 -r1.25 --- wwassignment/lib.php +++ wwassignment/lib.php @@ -528,7 +528,7 @@ //$result = call_user_func_array(array(&$this->client,$functioncall),$params); if($err = $this->client->getError()) { //print_error(get_string("rpc_fault","wwassignment') . " " . $functioncall. " ". $err); - print_error(get_string('rpc_error','wwassignment')."<BR>$err"); + print_error(get_string('rpc_error','wwassignment')."<BR>$err<BR><BR>Response:<BR>".$this->client->response ); } return $result; } @@ -686,6 +686,7 @@ */ function options_course($silent = true) { $courselist = $this->handler('list_courses'); + sort($courselist); if(isset($courselist)) { $courseoptions = array(); foreach($courselist as $course) { |
From: dpvc v. a. <we...@ma...> - 2007-08-29 12:21:25
|
Log Message: ----------- Added the support files needed for the Union theme (since someone added it to the list of themes without adding the support files). I didn't have a gateway template made up, so I hacked this one together based on the ur page. It is only lightly tested. Added Files: ----------- webwork2/htdocs/css: union.css webwork2/conf/templates/union: gateway.template system.template Revision Data ------------- --- /dev/null +++ htdocs/css/union.css @@ -0,0 +1,190 @@ +/******************************************************************************/ +/* union.css classes: These classes appear in ur.template and are NOT + * emitted by WeBWorK code. They need only appear in this template. */ + +body { margin: 0px; } + +/* left table cell, contains logo and menus */ +td.LeftPanel { background-color: #660033; color: white; white-space: nowrap; width: 8em; zoom: 1; } +td.LeftPanel ul { zoom: 1; margin-left: 0em} +td.LeftPanel ul ul ul li { margin-left: .5em} +td.LeftPanel a:link, +td.LeftPanel a:visited { color: #EEBBBB; } + +div.Logo { } +div.Links { font-size: small; } +div.Links ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Links ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } +div.Siblings { font-size: small; } +div.Siblings ul { list-style: none; margin-left: 0; padding-left: 0; } +div.Siblings ul ul { list-style: none; margin-left: 0.5em; padding-left: 0; } +div.Options { font-size: small; } + +/* top table cell, contains login message and path */ +td.TopPanel { background-color: #660033; color: white; height: 1; } +td.TopPanel a:link, +td.TopPanel a:visited { color: #EEBBBB; } + +.Path { } + +.LoginStatus { text-align: right; font-size: small;} + +.TimeLeft { font-size: small; } +.LittleTimeLeft { font-size: small; color: #FF7777; font-weight: bold; margin: 3px} +.TimeLeftReload { font-size: small; color: #EEBBBB; font-style: italic; } +td.Timestamp { text-align: left; font-size: small; font-style: italic; } + +/* main content panel, contains body */ +td.ContentPanel { background-color: white; color: black; } +td.ContentPanel a:link, +td.ContentPanel a:visited { color: blue; } +td.ContentPanel a:active { color: red; } + +td.SetMakerPanel { background-color: #DDDDDD; color: black; width: 50%; padding: 3px; } + +div.Nav { } +div.Title { font-size: 16pt; padding-bottom: 5px; } +div.SubmitError { color: red; font-style: italic; } +div.Message { font-style: italic; } +div.Body { } +div.Warnings { } + +/* contains info */ +td.InfoPanel { background-color: #DDDDDD; color: black; width: 40%; } +td.InfoPanel a:link, +td.InfoPanel a:visited { color: blue; } +td.InfoPanel a:active { color: red; } +div.Info { } + +/******************************************************************************/ +/* WeBWorK classes: These classes are emitted by WeBWorK code and should + * appear in ANY WeBWorK template. */ + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap; } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; padding: 5px; } +div.AuthorComment { background-color: #00E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +/* ul.LinksMenu { list-style: none; margin: 0 0 0 0.5em; padding: 0; }*/ +/* ul.LinksMenu ul { list-style: none; margin: 0 0 0 0.5em; padding: 0; }*/ + +a.HelpLink { border: 0px; } +a.HelpLink img { vertical-align: -5px; margin: 1px 2px; border: 0px; } + +/* background colors for success and failure messages */ +div.WarningMessage { background-color: #ffcccc; padding: 3px 3px 3px 3px; } +div.ResultsWithoutError { + margin-right: 6px; margin-left: 6px; + border-color: #009900; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + margin-right: 6px; margin-left: 6px; + background-color: #99ffAA; + padding: 3px 3px 3px 10px; +} /* light green */ +div.ResultsWithError { + margin-right: 6px; margin-left: 6px; + border-color: #CC0000; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + background-color: #ffcccc; + padding: 3px 3px 3px 10px; +} /* light red */ +div.ResultsAlert { + margin-right: 6px; margin-left: 6px; + border-color: #CCCC00; border-style: solid; border-width: 1px; + text-align: center; font-weight: bold; font-style: italic; + background-color: #ffffcc; + padding: 3px 3px 3px 10px; +} /* light yellow */ + +/* styles used by WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thin solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/* wraps the View Options form (generated by &optionsMacro) */ +/* FIXME: can't this style information just go in div.Options above? */ +div.viewOptions { border: thin groove; background-color: #882848; + line-height: 75%; margin: .5ex; margin-top: 2ex; + padding: 1ex; align: left; } + +.viewChoices { margin: 5px 0px 5px 5px; } +.viewChoices input {margin-top: 0px} + + +/* messages, attempt results, answer previews, etc. go in this DIV */ +/* this used to be "float:left", but that was suspected of causing MSIE peekaboo bug */ + +div.problemHeader {} + +/* styles for the attemptResults table */ +table.attemptResults { + border-style: outset; + border-width: 1px; +# margin: 0px auto 10pt auto; + margin: 0px 10pt; + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset; + border-width: 1px; + text-align: center; + padding: 2px 5px; + background-color: #DDDDDD; +} +/* override above settings in tables used to display ans_array results */ +table.attemptResults td td, +table.attemptResults td th, +table.ArrayLayout td { + border-style: none; + border-width: 0px; + padding: 0px; +} +table.attemptResults td.Message { text-align: left; width: auto; } +.attemptResultsSummary { font-style:italic; } + +.parsehilight { background-color:yellow; } + +/* the problem TEXT itself does in this box */ +div.problem { + clear: both; padding: 0 5px; color: black; + border-top-style: solid; border-bottom-style: solid; + border-width: 2px; border-color: #DDBBBB; +} + +/* jsMath emits this class when appropriate math fonts aren't available */ +div.NoFontMessage { + padding: 10; + border-style: solid; + border-width:3px; + border-color: #DD0000; + background-color: #FFF8F8; + width: 75%; + text-align: left; + margin: 10px auto 10px 12%; +} + +/* text colors for published and unpublished sets */ +font.Published { font-style: normal; font-weight: normal; color: #000000; } /* black */ +font.Unpublished { font-style: italic; font-weight: normal; color: #aaaaaa; } /* light grey */ + +/* styles used when editing a temporary file */ +.temporaryFile { + margin-right: 6px; margin-left: 6px; + border-color: #AA6600; border-style: solid; border-width: 1px; + text-align: center; font-style: italic; + background-color: #FFBB88; + padding: 3px 3px 3px 10px; +} + +/* text colors for Auditing, Current, and Dropped students */ +.Audit { font-style: normal; color: purple; } +.Enrolled { font-weight: normal; color: black; } +.Drop { font-style: italic; color: grey; } --- /dev/null +++ conf/templates/union/gateway.template @@ -0,0 +1,279 @@ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<!-- +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/templates/union/gateway.template,v 1.1 2007/08/29 12:15:16 dpvc Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ +--> + +<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"> +<head> +<title><!--#path style="text" text=" : " textonly="1"--></title> +<!--#head--> +<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> + +<style type="text/css"> +/******************/ +/* gateway styles */ +/******************/ + +.LoginStatus {position:absolute; right:0px; top:-.5em} + +div.gwMessage { background-color:#dddddd; color: black; } +div.gwTiming { + background-color:#EECCCC; + color: black; + padding: .5em 2em; + float: right; +} +div.gwTiming form {margin:0px} +div.gwWarning { background-color:#ffffdd; color: black; } + +span.resultsWithError { + background-color: #ffcccc; + color: black; + padding: 1px 2em; + margin-left: 1em; +} +span.resultsWithoutError { + background-color: #66ff99; + color: black; + padding: 1px 2em; + margin-left: 1em; +} + +div.gwCorrect { + background-color: #66ff99; + color: black; + padding: 1px 2em; + margin-left: 1em; +} +div.gwIncorrect { + background-color: #ff9999; + color: black; + padding: 1px 2em; + margin-left: 1em; +} + +div.gwProblem { + clear: both; + background-color: white; + color: black; + padding: 1em 2em; + border-top-style: solid; border-bottom-style: solid; + border-width: 2px; border-color: #DDBBBB; +} +hr {display: none} +.div.gwProblem hr {display:block} + +div.gwSoln { +/* background-color: #e0e0ff; */ + background-color: transparent; + color: black; +/* padding: 2px; */, +/* border: dashed black 1px; */ +} +div.gwSoln b { color: navy; } + +p.gwPreview { + font-size: smaller; + text-align: right; + margin-top: 0px; + margin-bottom: 0px; +} + +table.gwAttemptResults { + border-width: 0px; +} +table.gwAttemptResults td.label { + font-weight: bold; + background-color: transparent; + color: navy; +} +table.gwAttemptResults td.output { + padding: 2px; + border: solid black 1px; + background-color: #eeeeee; +} + +</style> +<script language="javascript" type="text/javascript"> +function jumpTo(ref) { // scrolling javascript function + if ( ref ) { + var pn = ref - 1; // we start anchors at 1, not zero + if ( navigator.appName == "Netscape" && + parseFloat(navigator.appVersion) < 5 ) { + var xpos = document.anchors[pn].x; + var ypos = document.anchors[pn].y; + } else { + var xpos = document.anchors[pn].offsetLeft; + var ypos = document.anchors[pn].offsetTop; + } + if ( window.scrollTo == null ) { // cover for anyone + window.scroll(xpos,ypos); // lacking js1.2 + } else { + window.scrollTo(xpos,ypos); + } + } + return false; // prevent link from being followed +} + +// timer for gateway +var theTime = -1; // -1 before we've initialized +var alerted = -1; // -1 = no alert set; 1 = 1st alert set + // this shouldn't really be needed + +function runtimer() { +// aesthetically this is displeasing: we're assuming that the +// ContentGenerator will put the appropriate form elements in that +// page for us to manipulate. even with error checking, it seems sort +// of odd. + if ( document.gwtimer == null ) { // no timer + return; + } else { + var tm = document.gwtimer.gwtime; + var st = document.gwtimer.gwpagetimeleft.value; + + if ( st == 0 ) { // no time limit + return; + } else { + if ( theTime == -1 ) { + theTime = st; + tm.value = toMinSec(theTime); + setTimeout("runtimer()", 1000); // 1000 ms = 1 sec + } else if ( theTime == 0 && alerted != 3 ) { + alert("* You are out of time! *"); + alerted = 3; + } else if ( alerted != 3 ) { + theTime--; + tm.value = toMinSec(theTime); + setTimeout("runtimer()", 1000); // 1000 ms = 1 sec + if ( theTime == 35 && alerted != 2 ) { // time is in seconds + alert("* You have only about 30 seconds to complete " + + "this assignment. Press Grade very soon! *\n" + + "* The timer stops while this alert box is open. *"); + alerted = 2; + theTime -= 5; + } else if ( theTime == 75 && alerted != 1) { + alert("* You have only about a minute left " + + "to complete this assignment! *\n" + + "* The timer stops while this alert box is open. *"); + alerted = 1; + theTime -= 5; + } + } + } + } +} +function toMinSec(t) { +// convert to min:sec + mn = Math.floor(t/60); + sc = t - 60*mn; + if ( mn < 10 && mn > -1 ) { + mn = "0" + mn; + } + if ( sc < 10 ) { + sc = "0" + sc; + } + return mn + ":" + sc; +} +</script> +</head> +<body onload="runtimer();"> +<table width="100%" cellpadding="10" cellspacing="0" border="0"> + <tr valign="top"> +<!-- removed left sidebar --> + <!--#if can="info"--> + <td class="TopPanel" colspan="2"> + <!--#else--> + <td class="TopPanel" > + <!--#endif--> + <div style="position:relative; width:100%;"> + <!--#if can="path"--> + <span class="Path"> + <!--#path style="text" image="/webwork2_files/images/right_arrow.png" text=" > "--> + </span> + <!--#endif--> + + <!--#if loggedin="1"--> + <!--#if can="loginstatus"--> + + <span class="LoginStatus"> + + <!--#loginstatus--> + </span> + <!--#endif--> + <!--#endif--> + </div> + </td> + </tr> + <tr valign="top"> + <!--#if warnings="1"--> + <td class="ContentPanelError" bgcolor="#ffcccc"> + <!--#else--> + <td class="ContentPanel" bgcolor="#ffffff"> + <!--#endif--> +<!-- removed nav button to go up --> + <!--#if can="title"--> + <div class="Title"> + <!--#title--> + </div> + <!--#endif--> + <!--#if can="message"--> + <div class="Message"> + <!--#message--> + </div> + <!--#endif--> + + <!--#if can="submiterror"--> + <div class="SubmitError"> + <!--#submiterror--> + </div> + <!--#endif--> + <!--#if can="body"--> + <div class="Body"> + <!--#body--> + </div> + <!--#endif--> + <!--#if warnings="1"--> + <hr> + <div class="Warnings"> + <!--#warnings--> + </div> + <!--#endif--> + <!--#if can="message"--> + <div class="Message"> + <!--#message--> + </div> + <!--#endif--> + </td> + <!--#if can="info"--> + <td class="InfoPanel"> + <div class="Info"> + <!--#info--> + </div> + </td> + <!--#endif--> + </tr> + <tr> + <td class = "Timestamp", colspan=3> + Updated: <!--#timestamp--> + </td> + </tr> +</table> +</body> +</html> --- /dev/null +++ conf/templates/union/system.template @@ -0,0 +1,159 @@ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<!-- +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/conf/templates/union/system.template,v 1.1 2007/08/29 12:15:16 dpvc Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ +--> + +<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US"> +<head> +<title><!--#path style="text" text=" : " textonly="1"--></title> +<!--#head--> +<style type="text/css" media="all">@import "<!--#url type="webwork" name="stylesheet"-->";</style> +</head> +<body> +<table width="100%" cellpadding="10" cellspacing="0" border="0"> + <tr valign="top"> + <td align="left" valign="top" rowspan="2" class="LeftPanel"> + <a href="http://webwork.math.rochester.edu"> + <img src="/webwork2_files/images/webwork_square.gif" alt="WeBWorK" height="64" width="66" border="0" /><! + ></a> + <br /> + + <!--#if loggedin="1"--> + <!--#if can="links"--> + <hr /> + <div class="Links"> + <!--#links--> + </div> + <!--#if can="siblings"--> + <hr /> + <!--#endif--> + <!--#endif--> + <div class="Siblings"> + <!--#siblings--> + </div> + <!--#if can="options"--> + <div class="Options"> + <!--#options--> + </div> + <!--#endif--> + <!--#endif--> + </td> + <!--#if can="info"--> + <td class="TopPanel" colspan="2"> + <!--#else--> + <td class="TopPanel"> + <!--#endif--> + <table border="0" cellpadding="0" cellspacing="0" width="100%"> + <tr> + + <!--#if can="path"--> + <td align="left" class="TopPanel" nowrap> + <span class="Path"> + <!--#path style="text" image="/webwork2_files/images/right_arrow.png" text=" > "--> + </span> + </td> + <!--#endif--> + + <!--#if can="time_left"--> + <td align="center" class="TopPanel" width="100%" nowrap> + <span class="TimeLeft"> + <!--#time_left--> + </span> + </td> + <!--#endif--> + + <!--#if loggedin="1"--> + <!--#if can="loginstatus"--> + <td align="right" class="TopPanel" nowrap> + <span class="LoginStatus"> + <!--#loginstatus--> + </span> + </td> + <!--#endif--> + <!--#endif--> + + </tr> + </table> + </td> + </tr> + + <tr valign="top"> + <!--#if warnings="1"--> + <td class="ContentPanelError" bgcolor="#ffcccc"> + <!--#else--> + <td class="ContentPanel" bgcolor="#ffffff"> + <!--#endif--> + <!--#if can="nav"--> + <div class="Nav"> + <!--#nav style="images" imageprefix="/webwork2_files/images/nav" imagesuffix=".gif" separator=" "--> + </div> + <!--#endif--> + <!--#if can="title"--> + <div class="Title"> + <!--#title--> + </div> + <!--#endif--> + <!--#if can="message"--> + <div class="Message"> + <!--#message--> + </div> + <!--#endif--> + + <!--#if can="submiterror"--> + <div class="SubmitError"> + <!--#submiterror--> + </div> + <!--#endif--> + <!--#if can="body"--> + <div class="Body"> + <!--#body--> + </div> + <!--#endif--> + <!--#if warnings="1"--> + <hr> + <div class="Warnings"> + <!--#warnings--> + </div> + <!--#endif--> + <!--#if can="message"--> + <div class="Message"> + <!--#message--> + </div> + <!--#endif--> + </td> + + <!--#if can="info"--> + <td class="InfoPanel"> + <div class="Info"> + <!--#info--> + </div> + </td> + <!--#endif--> + </tr> + + <tr> + <td class="Timestamp", colspan="3"> + Updated: <!--#timestamp--> + </td> + </tr> + +</table> +</body> +</html> |
From: dpvc v. a. <we...@ma...> - 2007-08-29 12:19:16
|
Update of /webwork/cvs/system/webwork2/conf/templates/union In directory devel.webwork.rochester.edu:/tmp/cvs-serv74954/union Log Message: Directory /webwork/cvs/system/webwork2/conf/templates/union added to the repository |
From: Matt L. v. a. <we...@ma...> - 2007-08-29 04:41:36
|
Log Message: ----------- New Version Added Files: ----------- wwmoodle/wwquestion: file.php Revision Data ------------- --- /dev/null +++ wwquestion/file.php @@ -0,0 +1,75 @@ +<?php + //This script fetches files from wwquestions directory, and only that directory. + //It is used to display the cached equation images that are copied to moodledata when derived questions are created + + require_once('../../../config.php'); + require_once($CFG->libdir . '/filelib.php'); + + // disable moodle specific debug messages + disable_debugging(); + + $relativepath = get_file_argument('file.php'); + $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL); + + // relative path must start with '/', because of backup/restore!!! + if (!$relativepath) { + error('No valid arguments supplied or incorrect server configuration'); + } else if ($relativepath{0} != '/') { + error('No valid arguments supplied, path does not start with slash!'); + } + + $pathname = $CFG->dataroot.$relativepath; + + // extract relative path components + $args = explode('/', trim($relativepath, '/')); + + //security this should only be used for wwquestions + if ((count($args) < 3) || ($args[0] != 'wwquestions')) { + error('No valid arguments supplied'); + } + + //extra security for users + if($args[1] == 'users') { + //trying to get a user equation image + //BEEF this up with roles etc, so teachers have access to students pics so on. + if($args[2] != $USER->id) { + error('Access Denied to this Picture'); + } + } + + + + if (is_dir($pathname)) { + if (file_exists($pathname.'/index.html')) { + $pathname = rtrim($pathname, '/').'/index.html'; + $args[] = 'index.html'; + } else if (file_exists($pathname.'/index.htm')) { + $pathname = rtrim($pathname, '/').'/index.htm'; + $args[] = 'index.htm'; + } else if (file_exists($pathname.'/Default.htm')) { + $pathname = rtrim($pathname, '/').'/Default.htm'; + $args[] = 'Default.htm'; + } else { + // security: do not return directory node! + not_found($course->id); + } + } + + // check that file exists + if (!file_exists($pathname)) { + not_found($course->id); + } + + // ======================================== + // finally send the file + // ======================================== + session_write_close(); // unlock session during fileserving + $filename = $args[count($args)-1]; + send_file($pathname, $filename); + + function not_found($courseid) { + global $CFG; + header('HTTP/1.0 404 not found'); + error(get_string('filenotfound', 'error'), $CFG->wwwroot.'/course/view.php?id='.$courseid); //this is not displayed on IIS?? + } +?> |
From: Matt L. v. a. <we...@ma...> - 2007-08-29 04:40:33
|
Log Message: ----------- New version see readme Modified Files: -------------- wwmoodle/wwquestion: README.txt display.html edit_webwork_form.php questiontype.php version.php wwmoodle/wwquestion/db: install.xml wwmoodle/wwquestion/lang/en_utf8: qtype_webwork.php Revision Data ------------- Index: version.php =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/version.php,v retrieving revision 1.2 retrieving revision 1.3 diff -Lwwquestion/version.php -Lwwquestion/version.php -u -r1.2 -r1.3 --- wwquestion/version.php +++ wwquestion/version.php @@ -1,5 +1,5 @@ <?PHP // $Id$ -$plugin->version = 2007072600; // TODO. +$plugin->version = 2007082900; // TODO. $plugin->requires = 2006032200; ?> \ No newline at end of file Index: README.txt =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/README.txt,v retrieving revision 1.3 retrieving revision 1.4 diff -Lwwquestion/README.txt -Lwwquestion/README.txt -u -r1.3 -r1.4 --- wwquestion/README.txt +++ wwquestion/README.txt @@ -1,9 +1,28 @@ Webwork Question Type ---------------------- -Version: 0.1 (stable) +Version: 0.2 (stable) Maintainer: Matthew Leventi <mle...@gm...> CVS: cvs.webwork.rochester.edu:/webwork/cvs/system wwmoodle/wwquestion +**If your using this send me an email.** + +Whats coming soon (sept 1): +* Question Importer (alpha at CVS: cvs.webwork.rochester.edu:/webwork/cvs/system wwmoodle/wwquestion_importer) +* Applet & External Dep. support for PG files. + +Whats New: +* DB consistency issues fixed (thanks to Jean-Marc) +* New code checking, makes sure PG code is correct +* Images are now copied locally for faster problem loading +* Minor bug Fixes + + +Upgrading (from 0.1): +A database column was added called codecheck. If you have data that you don't want to lose your going to have to add the new column manually to the db +mysql command: ALTER TABLE mdl_question_webwork ADD COLUMN codecheck int(10) not null default 0; +Then you can delete the webwork directory and recreate it from the CVS +** I am not really sure if this is backward compatible to 0.1. If you are having problems with questions edit them and submit to regenerate derived copies. + Setup: 1) Make a new folder named 'webwork' in the question/type directory. 2) Copy all the files from this directory into . @@ -11,7 +30,6 @@ Configuration: 1) Change the WSDL path variable in the webwork/questiontype.php file to point to your Webwork Problem Server's WSDL file. -2) Modify the displayMode to your preferences (images) Use: A webwork question only has three special fields. Index: questiontype.php =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/questiontype.php,v retrieving revision 1.10 retrieving revision 1.11 diff -Lwwquestion/questiontype.php -Lwwquestion/questiontype.php -u -r1.10 -r1.11 --- wwquestion/questiontype.php +++ wwquestion/questiontype.php @@ -5,8 +5,7 @@ require_once("htmlparser.php"); //Path to the WSDL file on the Webwork Server -define('PROBLEMSERVER_WSDL','http://128.151.231.20/WSDL.wsdl'); -define('PROBLEMSERVER_DISPLAYMODE','images'); +define('PROBLEMSERVER_WSDL','YOUR WSDL PATH HERE'); /** @@ -29,6 +28,15 @@ function name() { return 'webwork'; } + + function _derivedquestions($derivedquestions = null) { + static $temp = null; + if($derivedquestions == null) { + return $temp; + } + $temp = $derivedquestions; + return true; + } /** * @desc Retrieves the seed and decoded code out of the question_webwork table. @@ -41,74 +49,182 @@ notify('Error: Missing question options!'); return false; } + $question->trials = $record->trials; $question->seed = $record->seed; $question->code = base64_decode($record->code); + $question->codecheck = $record->codecheck; + $question->webworkid = $record->id; return true; } /** - * @desc Saves the webwork question code and default seed setting into question_webwork + * @desc Saves the webwork question code and default seed setting into question_webwork. Will recreate all corresponding derived questions. * @return boolean to indicate success of failure. */ function save_question_options($question) { - // TODO code to save the extra data to your database tables from the - // $question object, which has all the post data from editquestion.html - // Save question options in question_webwork table + + //UPDATE OR INSERTION if ($record = get_record("question_webwork", "question", $question->id)) { - //get rid of all the derived questions based on this one - $this->delete_derived_questions($question->id); - - $record->code = base64_encode(stripslashes($question->code)); - $record->seed = $question->seed; - $record->trials = $question->trials; - $result = update_record("question_webwork", $record); - if (!$result) { - $result->error = "Could not update quiz webwork options! (id=$record->id)"; - return $result; - } - + $isupdate = true; } else { + $isupdate = false; unset($record); - $record->question = $question->id; - $record->code = base64_encode(stripslashes($question->code)); - $record->seed = $question->seed; - $record->trials = $question->trials; - $result = insert_record("question_webwork", $record); - if (!$result) { - $result->error = "Could not insert quiz webwork options!"; - return $result; + } + //set new variables for update or insert + $record->question = $question->id; + $record->codecheck = $question->codecheck; + $record->code = base64_encode(stripslashes($question->code)); + $record->seed = $question->seed; + $record->trials = $question->trials; + + //create the derived questions and check for errors + //$results = $question->derivedquestions; + + //do the database action on question_webwork + if($isupdate) { + $errresult = update_record("question_webwork", $record); + if (!$errresult) { + $errresult->error = "Could not update quiz webwork options! (id=$record->id)"; + return $errresult; + } + } else { + $errresult = insert_record("question_webwork", $record); + if (!$errresult) { + $errresult->error = "Could not insert quiz webwork options!"; + return $errresult; } - $record->id = $result; + $record->id = $errresult; } - return $this->create_derived_questions($record); + //delete the derived questions + $this->delete_derived_questions($record->id); + + //do the database action on question_webwork_derived + $err = $this->insert_derived_questions($record->id,$this->_derivedquestions()); + if($err != 0) { + return $err; + } + return true; } - function delete_derived_questions($questionid) { - delete_records("question_webwork_derived", "question_webwork", $questionid); + + /** + * @desc Deletes all derived questions that are children of the ID passed in. + * @param $webworkquestionid integer The ID of the parent question + */ + function delete_derived_questions($webworkquestionid) { + delete_records("question_webwork_derived", "question_webwork", $webworkquestionid); return true; } - function create_derived_questions($question) { - $code = $question->code; - $seed = $question->seed; - $trials = $question->trials; - $parentid = $question->id; + + /** + * @desc Gets derived questions from a webworkquestion record object by calling the SOAP object. + * @param $webworkquestion The record to create from + * + */ + function get_derived_questions($webworkquestion) { + //parameters needed from the webworkquestion object + $code = $webworkquestion->code; + $seed = $webworkquestion->seed; + $trials = $webworkquestion->trials; + + + //problem to be generated + $problem = array(); + $problem['code'] = $code; + $problem['seed'] = $seed; + + //requested # times for generation + $request = array(); + $request['trials'] = $trials; + $request['problem'] = $problem; - $params = array('code' => $code, 'seed' => $seed, 'trials' => $trials); + //SOAP CALL + $params = array($request); $client = new problemserver_client(); - $response = $client->handler('generateProblems',$params); + $response = $client->handler('generateProblem',$params); + return $response; + } + + + /** + * @desc Inserts the derived questions into the DB. + * @param $parentid The parent ID of the derived questions. + * @param $derivedrecordset The recordset to create from. + */ + function insert_derived_questions($parentid,$derivedrecordset) { - $record->question_webwork = $parentid; - foreach($response as $problem) { - $record->html = $problem['html']; + foreach($derivedrecordset as $problem) { + unset($record); + //set the parent id for the derived questions + $record->question_webwork = $parentid; + $record->html = $problem['output']; $record->seed = $problem['seed']; + //initial insert $result = insert_record("question_webwork_derived",$record); if (!$result) { $result->error = "Could not insert quiz webwork derived options!"; return $result; } + $record->id = $result; + + //brings image files to local drive + //THIS SHOULD ALSO DO APPLETS SOON + $err = $this->copy_derived_question_data($record); + if($err != 0) { + return $err; + } + + $result = update_record("question_webwork_derived",$record); + if(!$result) { + $result->error = "Could not update quiz webwork derived options! (id=$record->id)"; + return $result; + } + } - return true; + return false; } + + function copy_derived_question_data(&$derivedrecord) { + global $CFG; + //make the base directory if needed + $dir = $CFG->dataroot . '/wwquestions'; + mkdir($dir); + //make the directory for this question + $dir = $CFG->dataroot . '/wwquestions/'.$derivedrecord->id; + mkdir($dir); + + //first we need to find the image paths + $imagestocopy = array(); + $problemhtml = ""; + $unparsedhtml = base64_decode($derivedrecord->html); + $parser = new HtmlParser($unparsedhtml); + while($parser->parse()) { + if ($parser->iNodeType == NODE_TYPE_ELEMENT) { + $nodename = $parser->iNodeName; + //rewrite the images + if(($nodename == "IMG") || ($nodename == "img")) { + //found one + $srcpath = $parser->iNodeAttributes['src']; + $srcfilename = strrchr($srcpath,'/'); + $parser->iNodeAttributes['src'] = $CFG->wwwroot . "/question/type/webwork/file.php/wwquestions/" . $derivedrecord->id . '/' . $srcfilename; + //NOTE explore the possibility of having an existence check here, filenames hashed? + //copy it + $err = copy($srcpath,$CFG->dataroot.'/wwquestions/'.$derivedrecord->id.$srcfilename); + if($err == false) { + $err->error = 'Copy Failed for: '.$srcpath; + return $err; + } + + + } + } + $problemhtml .= $parser->printTag(); + } + $html = base64_encode($problemhtml); + $derivedrecord->html = $html; + return false; + } + /** * @desc Deletes question from the question_webwork table @@ -116,7 +232,12 @@ * @return boolean to indicate success of failure. */ function delete_question($questionid) { - $this->delete_derived_questions($questionid); + //Deleting the webwork derived questions + $records = get_records('question_webwork','question',$questionid,'','id'); + foreach($records as $record) { + $this->delete_derived_questions($record->id); + } + //Deleting the webwork questions delete_records("question_webwork", "question", $questionid); return true; } @@ -124,9 +245,17 @@ /** * @desc Creates an empty response before student answers a question. This contains the possibly randomized seed for that particular student. Sticky seeds are created here. */ - function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { + function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { + global $CFG,$USER; + + //directory housekeeping (insure directories are setup) + mkdir($CFG->dataroot.'/wwquestions/users'); + mkdir($CFG->dataroot.'/wwquestions/users/'.$USER->id); + + + //here we get the derived results for this question - $results = get_records("question_webwork_derived","question_webwork",$question->id,'','id'); + $results = get_records("question_webwork_derived","question_webwork",$question->webworkid,'','id'); if(!$results) { print_error(get_string('error_db_webwork_derived','qtype_webwork')); return false; @@ -136,13 +265,17 @@ print_error(get_string('error_no_webwork_derived','qtype_webwork')); return false; } + //pick a random question based on time srand(time()); $random = rand(0,count($results)-1); $values = array_values($results); $derivedid = $values[$random]->id; + //more directory housekeeping + mkdir($CFG->dataroot.'/wwquestions/users/'.$USER->id.'/'.$derivedid); + //get the actual data - $results = get_record('question_webwork_derived',"id",$derivedid); + $results = get_record('question_webwork_derived','id',$derivedid); $state->responses['seed'] = $results->seed; $state->responses['derivedid'] = $derivedid; @@ -190,7 +323,7 @@ * @desc Prints the question. Calls the Webwork Server for appropriate HTML output and image paths. */ function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { - global $CFG; + global $CFG,$USER; $readonly = empty($options->readonly) ? '' : 'disabled="disabled"'; //Formulate question image and text $questiontext = $this->format_text($question->questiontext, @@ -243,6 +376,8 @@ $qid = $question->id; $seed = $state->responses['seed']; + //if the student has answered + include("$CFG->dirroot/question/type/webwork/display.html"); } @@ -250,22 +385,33 @@ * @desc Assigns a grade for a student response. Currently a percentage right/total questions. Calls the Webwork Server to evaluate answers */ function grade_responses(&$question, &$state, $cmoptions) { - // TODO assign a grade to the response in state. - //get code - //echo "GRADE"; - //var_dump($state); + global $CFG,$USER; + //get code and seed of the students problem $code = base64_encode($question->code); $seed = $state->responses['seed']; - //get answers + $derivedid = $state->responses['derivedid']; + + //get answers & build answer request $answerarray = array(); foreach($state->responses as $key => $value) { array_push($answerarray, array('field' => $key, 'answer'=> $value)); } - $params = array('code'=>$code, 'seed'=>$seed, 'answers'=>$answerarray); + //build problem request + $problem = array(); + $problem['code'] = $code; + $problem['seed'] = $seed; + + //SOAP request + $params = array(); + $params['request'] = $problem; + $params['answers'] = $answerarray; + + //SOAP Call $client = new problemserver_client(); $response = $client->handler('checkAnswers',$params); + //process output from soap & calculate score $answers = $response; $state->raw_grade = 0; $total = 0; @@ -284,11 +430,33 @@ // mark the state as graded $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE; + //put the responses into the state to remember $state->responses['answers'] = array(); foreach ($answers as $answer) { + //parse and change the preview paths + $unparsedhtml = base64_decode($answer['preview']); + $ansparser = new HtmlParser($unparsedhtml); + $parsedhtml = ""; + while($ansparser->parse()) { + if($ansparser->iNodeType == NODE_TYPE_ELEMENT) { + $nodename = $ansparser->iNodeName; + if(($nodename == 'img') || ($nodename == 'IMG')) { + $srcpath = $ansparser->iNodeAttributes['src']; + $srcfilename = strrchr($srcpath,'/'); + $newpath = "/wwquestions/users/" . $USER->id . '/' . $derivedid . '' . $srcfilename; + $ansparser->iNodeAttributes['src'] = $CFG->wwwroot . "/question/type/webwork/file.php$newpath"; + //copy it + $err = copy($srcpath,$CFG->dataroot . $newpath); + if($err == false) { + print_error("copy operation failed src:'$srcpath' dest:'$newpath'"); + } + } + } + $parsedhtml .= $ansparser->printTag(); + $answer['preview'] = $parsedhtml; + } $state->responses['answers'][$answer['field']] = $answer; } - return true; //var_dump($state); } @@ -314,19 +482,31 @@ * @desc Gets the correct answers from the server for the seed in state. Places them into the state->responses array. */ function get_correct_responses(&$question, &$state) { - + //get code and seed of response $code = base64_encode($question->code); - $seed = $state->responses['seed']; - echo $seed; - //tricks checkAnswer into believing we are sending an anwer - $answerarray = array(array('field'=>'','answer'=>'')); - + //get empty answers & build answer request + $answerarray = array(); + foreach($state->responses as $key => $value) { + array_push($answerarray, array('field' => $key, 'answer'=> $value)); + } + + //build problem request + $problem = array(); + $problem['code'] = $code; + $problem['seed'] = $seed; + + //SOAP request + $params = array(); + $params['request'] = $problem; + $params['answers'] = $answerarray; - $params = array('code'=>$code, 'seed'=>$seed, 'answers'=>$answerarray); + //SOAP Call $client = new problemserver_client(); $response = $client->handler('checkAnswers',$params); + + //process correct answers into fields $answers = $response; $ret = array(); $ret['answers'] = array(); @@ -334,6 +514,7 @@ $ret['answers'][$answer['field']] = $answer; $ret['answers'][$answer['field']]['answer'] = $answer['correct']; } + //push the seed onto the answer array, keep track of what seed these are for. $ret['seed'] = $state->responses['seed']; $ret['derivedid'] = $state->responses['derivedid']; Index: edit_webwork_form.php =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/edit_webwork_form.php,v retrieving revision 1.3 retrieving revision 1.4 diff -Lwwquestion/edit_webwork_form.php -Lwwquestion/edit_webwork_form.php -u -r1.3 -r1.4 --- wwquestion/edit_webwork_form.php +++ wwquestion/edit_webwork_form.php @@ -9,6 +9,7 @@ *//** */ require_once($CFG->dirroot.'/question/type/edit_question_form.php'); +require_once($CFG->dirroot.'/question/type/webwork/questiontype.php'); /** * webwork editing form definition. @@ -22,6 +23,16 @@ //HEADER $mform->addElement('header', 'generalheader', get_string("edit_header", 'qtype_webwork')); + //CODECHECK + $codecheckoptions = array( + 0 => get_string('edit_codecheck0','qtype_webwork'), + 1 => get_string('edit_codecheck1','qtype_webwork'), + 2 => get_string('edit_codecheck2','qtype_webwork')); + $mform->addElement('select','codecheck', get_string('edit_codecheck', 'qtype_webwork'),$codecheckoptions); + $mform->setType('codecheck',PARAM_INT); + $mform->setHelpButton('codecheck', array('webwork', get_string('edit_codecheck', 'qtype_webwork'), 'webwork')); + $mform->setDefault('codecheck',2); + //CODE $mform->addElement('textarea', 'code', get_string('edit_code', 'qtype_webwork'), array('rows' => 10,'cols' => 60)); @@ -46,17 +57,99 @@ $mform->addRule('trials', null, 'required', null, 'client'); } - function set_data($question) { + function set_data($question) { parent::set_data($question); } - + function validation($data) { - $errors = array(); - if ($errors) { - return $errors; - } else { - return true; + //check that the code is valid + $err = $this->codecheck($data); + if($err != false) { + return $err; + } + } + + function codecheck($data) { + //codechecklevel + $codechecklevel = $data['codecheck']; + //here we construct a temp question object + $question = new stdClass; + $question->code = base64_encode(stripslashes($data['code'])); + $question->seed = $data['seed']; + $question->trials = $data['trials']; + + //one call to the server will return response for this code and keep it static in the function + $results = webwork_qtype::get_derived_questions($question); + //no code check + if($codechecklevel == 0) { + webwork_qtype::_derivedquestions($results); + return false; + } + + //init error array + $errorresults = array(); + $goodresults = array(); + + //see if we got errors (split) + foreach($results as $record) { + if((isset($record['errors'])) && ($record['errors'] != '') && ($record['errors'] != null)) { + array_push($errorresults,$record); + } else { + array_push($goodresults,$record); + } + } + + //if there are good seeds we use those + if((count($goodresults) > 0) && ($codechecklevel == 1)) { + webwork_qtype::_derivedquestions($goodresults); + return false; + } + + //if code check is strict + if(count($goodresults) == count($results)) { + webwork_qtype::_derivedquestions($results); + return false; + } + + $errormsgs = array(); + //at this point we are going to be invalid + + //this correlates seeds with certain error messages for better output + + foreach($errorresults as $record) { + $found = 0; + $candidate = $record['errors']; + $candidateseed = $record['seed']; + for($i=0;$i<count($errormsgs);$i++) { + if($candidate == $errormsgs[$i]['errors']) { + $found = 1; + $errormsgs[$i]['seeds'][] = $candidateseed; + } + } + if($found == 0) { + //new error message + $msg = array(); + $msg['errors'] = $candidate; + $msg['seeds'] = array(); + $msg['seeds'][] = $candidateseed; + $errormsgs[] = $msg; + } + } + $output = "Errors in PG Code on: " . count($errorresults) . " out of " . count($results) . " seeds tried:<br>"; + //construct error statement + $counter = 1; + foreach($errormsgs as $msg) { + $output .= "$counter) "; + $output .= "Seeds ("; + foreach ($msg['seeds'] as $seed) { + $output .= $seed . " "; + } + $output .= ") gave Error:" . $msg['errors'] . "<br><br>"; + $counter++; } + $returner =array(); + $returner['code'] = $output; + return $returner; } function qtype() { Index: display.html =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/display.html,v retrieving revision 1.5 retrieving revision 1.6 diff -Lwwquestion/display.html -Lwwquestion/display.html -u -r1.5 -r1.6 --- wwquestion/display.html +++ wwquestion/display.html @@ -39,7 +39,7 @@ <?php echo $answerobj['answer']; ?> </td> <td class="c0" style="width: 15%;"> - <?php echo base64_decode($answerobj['preview']); ?> + <?php echo $answerobj['preview']; ?> </td> <td class="c0" style="width: 15%;"> <?php echo $answerobj['evaluated']; ?> Index: install.xml =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/db/install.xml,v retrieving revision 1.2 retrieving revision 1.3 diff -Lwwquestion/db/install.xml -Lwwquestion/db/install.xml -u -r1.2 -r1.3 --- wwquestion/db/install.xml +++ wwquestion/db/install.xml @@ -4,8 +4,9 @@ <TABLE NAME="question_webwork" COMMENT="Options for webwork questions" NEXT="question_webwork_derived"> <FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" ENUM="false" NEXT="question"/> - <FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="id" NEXT="code"/> - <FIELD NAME="code" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="question" NEXT="seed"/> + <FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="id" NEXT="codecheck"/> + <FIELD NAME="codecheck" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" ENUM="false" PREVIOUS="question" NEXT="code"/> + <FIELD NAME="code" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" ENUM="false" PREVIOUS="codecheck" NEXT="seed"/> <FIELD NAME="seed" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" ENUM="false" PREVIOUS="code" NEXT="trials"/> <FIELD NAME="trials" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" ENUM="false" PREVIOUS="seed"/> </FIELDS> Index: qtype_webwork.php =================================================================== RCS file: /webwork/cvs/system/wwmoodle/wwquestion/lang/en_utf8/qtype_webwork.php,v retrieving revision 1.2 retrieving revision 1.3 diff -Lwwquestion/lang/en_utf8/qtype_webwork.php -Lwwquestion/lang/en_utf8/qtype_webwork.php -u -r1.2 -r1.3 --- wwquestion/lang/en_utf8/qtype_webwork.php +++ wwquestion/lang/en_utf8/qtype_webwork.php @@ -8,13 +8,17 @@ * @package YOURPACKAGENAME *//** */ -$string['editingwebwork'] = 'Editing WeBWorK.'; -$string['webwork'] = 'WeBWorK.'; +$string['editingwebwork'] = 'Editing WeBWorK'; +$string['webwork'] = 'WeBWorK'; $string['error_client_construction'] = 'Error constructing client to Problem Server. Perhaps your WSDL Path is wrong?'; $string['error_client_call'] = 'Error in communication with the Problem Server.'; $string['edit_header'] = 'WeBWorK'; +$string['edit_codecheck'] = 'Code Checking'; + $string['edit_codecheck0'] = 'Turn Off (unadvised)'; + $string['edit_codecheck1'] = 'Reject Problem Seeds w/ Errors (silent)'; + $string['edit_codecheck2'] = 'Reject Question if any Errors (strict)'; $string['edit_code'] = 'Code'; $string['edit_seed'] = 'Seed'; $string['edit_trials'] = 'Trials'; |
From: Matt L. v. a. <we...@ma...> - 2007-08-29 04:39:26
|
Log Message: ----------- New version of Server Added Files: ----------- ww_question_server/lib/ProblemServer: GeneratorRequest.pm Revision Data ------------- --- /dev/null +++ lib/ProblemServer/GeneratorRequest.pm @@ -0,0 +1,16 @@ +package ProblemServer::GeneratorRequest; + +=pod +=begin WSDL + _ATTR trials $string The number of attempts at generation + _ATTR problem $ProblemServer::ProblemRequest The problem to be generated +=end WSDL +=cut +sub new { + my $self = shift; + $self={}; + bless $self; + return $self; +} + +1; |
From: Matt L. v. a. <we...@ma...> - 2007-08-29 04:38:06
|
Log Message: ----------- New version, see readme. Modified Files: -------------- ww_question_server: README ww_question_server/bin/setup: global.conf.base problemserver.apache-config.base setup.pl ww_question_server/lib: ProblemServer.pm ww_question_server/lib/ProblemServer: AnswerRequest.pm AnswerResponse.pm Environment.pm GeneratorResponse.pm ProblemRequest.pm ProblemResponse.pm Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/ww_question_server/README,v retrieving revision 1.5 retrieving revision 1.6 diff -LREADME -LREADME -u -r1.5 -r1.6 --- README +++ README @@ -1,31 +1,41 @@ -Webwork Problem Server ------------------------------------ +Webwork Question Server +---------------------- +Version: 0.2 (stable) +Maintainer: Matthew Leventi <mle...@gm...> +CVS: cvs.webwork.rochester.edu:/webwork/cvs/system ww_question_server + +** If your using this send me an email ** + +Note: You don't need to install the WeBWorK system for this server to function. All you need are the PG libraries. + +Whats new: +* Simple install with script. +* Code refactoring +* One translator per child, (speed boost) +* Fixed WSDL errors +* Minor bug fixes + +Prerequisites: +* Pod::WSDL CPAN Module +* Apache 1 or 2 +* mod_perl +* Apache::SOAP CPAN Module for apache 1, or Apache2::SOAP CPAN Module for apache2 +* latex and dvipng installed +* WeBWorK PG libraries installed -1) Setup - a) Change permissions for directories writable by server +Setup: + 1) Change permissions for directories writable by server chmod -R 777 htdocs/tmp chmod -R 777 tmp + 2) Run the setup program in bin/setup + perl setup.pl + Write down the WSDL path that the setup program gives you. It is what you use in the Moodle - WeBWorK Question Type. + 3) Copy the following files to the following places + bin/setup/WSDL.wsdl -> htdocs/WSDL.wsdl + bin/setup/global.conf -> conf/global.conf + bin/setup/problemserver.apache-config -> conf/problemserver.apache-config + 4) Include problemserver.apache-config in your apache configuration file. + Note: The problemserver.apache-config works for both apache 1 and 2. + ex) Include /home/you/problemserver/conf/problemserver.apache-config + 6) Restart Apache - -2) Configuration - The main changes in configuration are in the top of global.conf and in problemserver.apache2-config - In problemserver.apache2-config: - $problemserver_dir = The root directory where the problemserver is installed. - $pg_dir = The root directory where the pg is installed. - The Alias command should be changed to point to the root directory of problemserver + "/htdocs" - ex) Alias /problemserver_files /home/you/problemserver/htdocs - The Directory line should be changed to be the same as the second part of the Alias command above - ex) <Directory /home/you/problemserver/htdocs> - In global.conf - $problemServer{host} = The host the problem server is installed on - ex) "http://www.example.org"; - $pg_dir = Same as the $pg_dir for problemserver.apache2-config - $problemServerDirs{root}= Same as the $problemserver_dir for problemserver.apache2-config - - Note: Make sure the paths to the external programs are set correctly for your system. - Optionally: On line 163 you can add more paths to look for macro files. - - Finally you need to add an Include statement to your apache2.conf file. This should be located in /etc/apache2/apache2.conf under most systems. - ex) Include /home/you/problemserver/conf/problemserver.apache2-config - -3) Restart Apache2 Index: global.conf.base =================================================================== RCS file: /webwork/cvs/system/ww_question_server/bin/setup/global.conf.base,v retrieving revision 1.1 retrieving revision 1.2 diff -Lbin/setup/global.conf.base -Lbin/setup/global.conf.base -u -r1.1 -r1.2 --- bin/setup/global.conf.base +++ bin/setup/global.conf.base @@ -31,10 +31,6 @@ ################################################################################ -# The root URL (usually /webwork2), set by <Location> in Apache configuration. -$problemServerURLs{root} = "$problemServer{host}/problemserver"; - - # Contains non-web-accessible temporary files, such as TeX working directories. $problemServerDirs{tmp} = "$problemServerDirs{root}/tmp"; @@ -66,29 +62,8 @@ # equation rendering/hardcopy utiltiies -$externalPrograms{latex} = "/usr/bin/latex"; -$externalPrograms{pdflatex} = "/usr/bin/pdflatex --shell-escape"; -$externalPrograms{dvipng} = "/usr/bin/dvipng"; -$externalPrograms{tth} = "/usr/bin/tth"; - -# NetPBM - basic image manipulation utilities -# Most sites only need to configure $netpbm_prefix. -my $netpbm_prefix = "/usr/bin"; -$externalPrograms{giftopnm} = "$netpbm_prefix/giftopnm"; -$externalPrograms{ppmtopgm} = "$netpbm_prefix/ppmtopgm"; -$externalPrograms{pnmtops} = "$netpbm_prefix/pnmtops"; -$externalPrograms{pnmtopng} = "$netpbm_prefix/pnmtopng"; -$externalPrograms{pngtopnm} = "$netpbm_prefix/pngtopnm"; - -# url checker -$externalPrograms{checkurl} = "/usr/local/bin/lwp-request -mHEAD "; # or "/usr/local/bin/w3c -head " - -# image conversions utiltiies -# the source file is given on stdin, and the output expected on stdout. -$externalPrograms{gif2eps} = "$externalPrograms{giftopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; -$externalPrograms{png2eps} = "$externalPrograms{pngtopnm} | $externalPrograms{ppmtopgm} | $externalPrograms{pnmtops} -noturn 2>/dev/null"; -$externalPrograms{gif2png} = "$externalPrograms{giftopnm} | $externalPrograms{pnmtopng}"; - +$externalPrograms{latex} = "MARKER_FOR_LATEX"; +$externalPrograms{dvipng} = "MARKER_FOR_DVIPNG"; ################################################################################ Index: problemserver.apache-config.base =================================================================== RCS file: /webwork/cvs/system/ww_question_server/bin/setup/problemserver.apache-config.base,v retrieving revision 1.1 retrieving revision 1.2 diff -Lbin/setup/problemserver.apache-config.base -Lbin/setup/problemserver.apache-config.base -u -r1.1 -r1.2 --- bin/setup/problemserver.apache-config.base +++ bin/setup/problemserver.apache-config.base @@ -30,6 +30,7 @@ #Load the Module push @PerlModule, 'ProblemServer'; +$ProblemServer::Host = $hostname; $ProblemServer::RootDir = $root_dir; $ProblemServer::RootPGDir = $root_pg_dir; $ProblemServer::RPCURL = $rpc_url; Index: setup.pl =================================================================== RCS file: /webwork/cvs/system/ww_question_server/bin/setup/setup.pl,v retrieving revision 1.1 retrieving revision 1.2 diff -Lbin/setup/setup.pl -Lbin/setup/setup.pl -u -r1.1 -r1.2 --- bin/setup/setup.pl +++ bin/setup/setup.pl @@ -7,24 +7,28 @@ print "Continue? (y,n):"; $input = <STDIN>; chop $input; -if($input eq "n") { - exit; -} +if($input eq "n") {exit;} -$filename = "problemserver.apache-config"; +#APACHE 1 OR 2 print "Will you be using Apache 1 or 2\n"; print "(1,2)>"; $apache = <STDIN>; chop $apache; if($apache eq "1") { - $apache = "Apache::SOAP"; + $apachecpan = "Apache::SOAP"; } elsif ($apache eq "2") { - $apache = "Apache2::SOAP"; + $apachecpan = "Apache2::SOAP"; +} else { + exit; } + +#HOSTNAME +$hostnameExample = "http://www.example.com/"; + print "Please enter the http hostname of the computer.\n"; -print "This should be a value like 'http://www.example.com'\n"; +print "This should be a value like '$hostnameExample'\n"; print ">"; $hostname = <STDIN>; chop $hostname; @@ -41,35 +45,31 @@ $pg = <STDIN>; chop $pg; -print "Do you want to configure optional components.\n"; -print "(y,n)>"; -$input = <STDIN>; -chop $input; -if($input eq "y") { - - print "Please enter a rpc URL path. Leave blank for default.\n"; - print "Default: '/problemserver_rpc'\n"; - print ">"; - $rpc = <STDIN>; - chop $rpc; - if($rpc eq "") { - $rpc = "/problemserver_rpc"; - } - - print "Please enter a URL path where equation files will be stored. Leave blank for default.\n"; - print "Default: '/problemserver_files'\n"; - print ">"; - $files = <STDIN>; - chop $files; - if($files eq "") { - $files = "/problemserver_files"; - } -} else { - $rpc = "/problemserver_rpc"; - $files = "/problemserver_files"; +print "Please enter the path to 'latex' command. Leave blank for default. \n"; +print "Default '/usr/bin/latex'\n"; +print ">"; +$latex = <STDIN>; +chop $latex; +if($latex eq "") { + $latex = "/usr/bin/latex"; +} + +print "Please enter the path to 'dvipng' command. Leave blank for default. \n"; +print "Default '/usr/bin/dvipng'\n"; +print ">"; +$dvipng = <STDIN>; +chop $dvipng; +if($dvipng eq "") { + $dvipng = "/usr/bin/dvipng"; } + + +$rpc = "/problemserver_rpc"; +$files = "/problemserver_files"; + + #WSDL FILE CREATION print "Creating WSDL File...\n"; eval "use lib '$root/lib'"; die "Your root directory is wrong." if $@; @@ -80,8 +80,8 @@ pretty => 1, withDocumentation => 0 ); + $wsdlfilename = "WSDL.wsdl"; -$filename = "problemserver.apache-config"; open(OUTP, ">$wsdlfilename") or die("Cannot open file '$wsdlfilename' for writing.\n"); print OUTP $pod->WSDL; close OUTP; @@ -90,6 +90,8 @@ #APACHE CONFIGURATION FILE CREATION print "Creating Apache Configuration File...\n"; +$conffilename = "problemserver.apache-config"; + print " Setting Variables...\n"; $additionalconf = "my \$hostname = '$hostname';\n"; $additionalconf .= "my \$root_dir = '$root';\n"; @@ -109,10 +111,10 @@ } close INPUT; $content =~ s/MARKER_FOR_CONF/$additionalconf/; -$content =~ s/MARKER_FOR_APACHE/$apache/; +$content =~ s/MARKER_FOR_APACHE/$apachecpan/; print " Writing...\n"; -open(OUTP2, ">$filename") or die("Cannot open file '$filename' for writing.\n"); +open(OUTP2, ">$conffilename") or die("Cannot open file '$conffilename' for writing.\n"); print OUTP2 $content; close OUTP2; print "Done\n"; @@ -129,17 +131,14 @@ $content .= $line; } close INPUT2; +$content =~ s/MARKER_FOR_DVIPNG/$dvipng/; +$content =~ s/MARKER_FOR_LATEX/$latex/; print " Writing...\n"; open(OUTP3, ">global.conf") or die("Cannot open file 'global.conf' for writing.\n"); print OUTP3 $content; close OUTP3; print "Done\n"; +print "Your WSDL path: '" . $hostname . $files . '/'.$wsdlfilename."'\n"; + #POST CONFIGURATION -print "\n\n\n"; -print "####POST SETUP#####\n"; -print "You need the '$apache' CPAN Module installed.\n"; -print "You need to move 'global.conf' into the conf/ directory.\n"; -print "You need to move '$filename' into the conf/ directory.\n"; -print "You need to include '$filename' in the apache configuration file.\n"; -print "You need to move '$wsdlfilename' into the htdocs/ directory. \n"; Index: ProblemServer.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer.pm,v retrieving revision 1.8 retrieving revision 1.9 diff -Llib/ProblemServer.pm -Llib/ProblemServer.pm -u -r1.8 -r1.9 --- lib/ProblemServer.pm +++ lib/ProblemServer.pm @@ -15,6 +15,7 @@ use ProblemServer::ProblemRequest; use ProblemServer::ProblemResponse; +use ProblemServer::GeneratorRequest; use ProblemServer::GeneratorResponse; use WeBWorK::PG::Translator; @@ -39,8 +40,12 @@ $self = {}; #Base Conf $main::VERSION = "2.3.2"; + + #Warnings are passed into self + + #Construct the Server Environment - my $serverEnviron = new ProblemServer::Environment($ProblemServer::RootDir); + my $serverEnviron = new ProblemServer::Environment(); #Keep the Default Server Environment @@ -48,6 +53,7 @@ #Keep the Default Problem Environment $self->{problemEnviron} = ($self->{serverEnviron}{problemEnviron}); + #Create Safe Compartment $self->{safe} = new Safe; @@ -55,18 +61,8 @@ return $self; } -BEGIN { -$ProblemServer::theServer = new ProblemServer(); -} - -sub translation { - my ($self,$request) = @_; - - #Install a local warn handler to collect warnings - my $warnings = ""; - local $SIG{__WARN__} = sub { $warnings .= shift } - if $self->{serverEnviron}{pg}{options}{catchWarnings}; - +sub setupTranslator { + my $self = shift; #Create Translator Object my $translator = WeBWorK::PG::Translator->new; @@ -88,469 +84,130 @@ $translator->load_extra_packages(@extra_packages); } - #DEFINE SPECIFIC CHANGES TO PROBLEM ENVIRONMENT - $self->{problemEnviron}{problemSeed} = $request->{seed}; - $self->{problemEnviron}{displayMode} = translateDisplayModeNames($request->{displayMode}); + #Only type of output is images + $self->{problemEnviron}{displayMode} = "HTML_dpng"; $self->{problemEnviron}{languageMode} = $self->{problemEnviron}{displayMode}; $self->{problemEnviron}{outputMode} = $self->{problemEnviron}{displayMode}; - #PREP IMAGE GENERATOR - my $image_generator; - if ($request->{displayMode} eq "images") { - my %imagesModeOptions = %{$self->{serverEnviron}->{pg}{displayModeOptions}{images}}; - $image_generator = WeBWorK::PG::ImageGenerator->new( - tempDir => $self->{serverEnviron}->{problemServerDirs}->{tmp}, # global temp dir - latex => $self->{serverEnviron}->{externalPrograms}->{latex}, - dvipng => $self->{serverEnviron}->{externalPrograms}->{dvipng}, - useCache => 1, - cacheDir => $self->{serverEnviron}->{problemServerDirs}{equationCache}, - cacheURL => $self->{serverEnviron}->{problemServerURLs}{equationCache}, - cacheDB => $self->{serverEnviron}->{problemServerFiles}{equationCacheDB}, - useMarkers => ($imagesModeOptions{dvipng_align} && $imagesModeOptions{dvipng_align} eq 'mysql'), - dvipng_align => $imagesModeOptions{dvipng_align}, - dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, - ); - #DEFINE CLOSURE CLASS FOR IMAGE GENERATOR - $self->{problemEnviron}{imagegen} = new ProblemServer::Utils::RestrictedClosureClass($image_generator, "add"); - } - - - - #ATTACH THE PROBLEM ENVIRONMENT TO TRANSLATOR - $translator->environment($self->{problemEnviron}); - - #INITIALIZING THE TRANSLATOR - $translator->initialize(); - - #PRE-LOAD MACRO FILES - eval{$translator->pre_load_macro_files( - $self->{safe}, - $self->{serverEnviron}->{pg}->{directories}->{macros}, - 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl' - )}; - warn "Error while preloading macro files: $@" if $@; - - #LOAD MACROS INTO TRANSLATOR - foreach (qw(PG.pl dangerousMacros.pl IO.pl)) { - my $macroPath = $self->{serverEnviron}->{pg}->{directories}->{macros} . "/$_"; - my $err = $translator->unrestricted_load($macroPath); - warn "Error while loading $macroPath: $err" if $err; - } - - #SET OPCODE MASK - $translator->set_mask(); - - #Retrieve Source - my $source = decode_base64($request->{code}); - #INSERT PROBLEM SOURCE CODE INTO TRANSLATOR - eval { $translator->source_string( $source ) }; - $@ and die("bad source"); - - #CREATE SAFETY FILTER - $translator->rf_safety_filter(\&ProblemServer::nullSafetyFilter); - - #RUN - $translator->translate(); - - #PROCESS ANSWERS - my ($result, $state); # we'll need these on the other side of the if block! - my $answerArray = $request->{answers}; - #die($self->{problemEnviron}->{functAbsTolDefault}); - #die($request->{answer}); - if (defined $answerArray and @{$answerArray}) { - # process student answers - #warn "PG: processing student answers\n"; - - #take array of answers and make hash - my $answerHash = {}; - for(my $i=0;$i<@{$answerArray};$i++) { - $answerHash->{$answerArray->[$i]{field}} = $answerArray->[$i]{answer}; - } - $translator->process_answers($answerHash); - - # retrieve the problem state and give it to the translator - #warn "PG: retrieving the problem state and giving it to the translator\n"; - $translator->rh_problem_state({ - recorded_score => "0", - num_of_correct_ans => "0", - num_of_incorrect_ans => "0", - }); - - # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by - # the PG macro package (PG.pl) - #warn "PG: determining an entry order\n"; - my @answerOrder = $translator->rh_flags->{ANSWER_ENTRY_ORDER} - ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } - : keys %{ $translator->rh_evaluated_answers }; - - - # install a grader -- use the one specified in the problem, - # or fall back on the default from the course environment. - # (two magic strings are accepted, to avoid having to - # reference code when it would be difficult.) - #warn "PG: installing a grader\n"; - my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} || $self->{serverEnviron}->{pg}->{options}->{grader}; - $grader = $translator->rf_std_problem_grader if $grader eq "std_problem_grader"; - $grader = $translator->rf_avg_problem_grader if $grader eq "avg_problem_grader"; - die "Problem grader $grader is not a CODE reference." unless ref $grader eq "CODE"; - $translator->rf_problem_grader($grader); - - # grade the problem - #warn "PG: grading the problem\n"; - ($result, $state) = $translator->grade_problem( - answers_submitted => 1, - ANSWER_ENTRY_ORDER => \@answerOrder, - %{$answerHash} #FIXME? this is used by sequentialGrader is there a better way? - ); - - - } - my $displayMode = $request->{displayMode}; - - #ANSWER EVALUATION AND PREVIEW GENERATOR - my $answers = $translator->rh_evaluated_answers; - my $key; - my $preview; - my $answerResponse = {}; - my @answersArray; - - foreach $key (keys %{$answers}) { - #PREVIEW GENERATOR - $preview = $answers->{"$key"}->{"preview_latex_string"}; - $preview = "" unless defined $preview and $preview ne ""; - if ($displayMode eq "plainText") { - $preview = $preview; - } elsif ($displayMode eq "formattedText") { - #FIX THIS TO USE TTH - $preview = $preview; - } elsif ($displayMode eq "images") { - $preview = $image_generator->add($preview); - } elsif ($displayMode eq "jsMath") { - $preview =~ s/</</g; - $preview =~ s/>/>/g; - $preview = '<SPAN CLASS="math">\\displaystyle{'.$preview.'}</SPAN>'; - } - - #ANSWER STRUCT - $answerResponse = new ProblemServer::AnswerResponse; - $answerResponse->{field} = $key; - $answerResponse->{answer} = $answers->{"$key"}->{"original_student_ans"}; - $answerResponse->{answer_msg} = $answers->{"$key"}->{"ans_message"}; - $answerResponse->{correct} = $answers->{"$key"}->{"correct_ans"}; - $answerResponse->{score} = $answers->{"$key"}->{"score"}; - $answerResponse->{evaluated} = $answers->{"$key"}->{"student_ans"}; - $answerResponse->{preview} = encode_base64($preview); - push(@answersArray, $answerResponse); - } - - #GENERATE IMAGES AS NECESSARY - if ($image_generator) { - $image_generator->render(refresh => 1); - $image_generator->render( - body_text => $translator->r_text - ); - } - - - - - #WeBWorK::PG::Translator::pretty_print_rh($answers->{"0AnSwEr1"}); - #die($warnings); - - #CREATE A RESPONSE OBJECT - my $response = new ProblemServer::ProblemResponse; - $response->{id} = $request->{id}; - $response->{errors} = $translator->errors; - $response->{warnings} = $warnings; - $response->{answers} = \@answersArray; - $response->{seed} = $request->{seed}; - $response->{body_text} = encode_base64(${$translator->r_text}); - $response->{head_text} = encode_base64(${$translator->r_header}); - $response->{state} = "0"; - $response->{result} = $request->{displayMode}; - - return $response; + $self->{translator} = $translator; } -sub generator { - my ($self,$code,$seed,$trials) = @_; - - my @derivedProblems; - my @alreadyCreated; - - #Create Translator Object - my $translator = WeBWorK::PG::Translator->new; - - #Attach log modules - my @modules = @{ $self->{serverEnviron}{pg}{modules} }; - # HACK for apache2 - if (MP2) { - push @modules, ["Apache2::Log"], ["APR::Table"]; - } else { - push @modules, ["Apache::Log"]; - } - - #Evaulate all module packs - foreach my $module_packages_ref (@modules) { - my ($module, @extra_packages) = @$module_packages_ref; - # the first item is the main package - $translator->evaluate_modules($module); - # the remaining items are "extra" packages - $translator->load_extra_packages(@extra_packages); - } - - #DEFINE SPECIFIC CHANGES TO PROBLEM ENVIRONMENT - $self->{problemEnviron}{displayMode} = translateDisplayModeNames("images"); - $self->{problemEnviron}{languageMode} = $self->{problemEnviron}{displayMode}; - $self->{problemEnviron}{outputMode} = $self->{problemEnviron}{displayMode}; +sub setupImageGenerator { + my $self = shift; - #PREP IMAGE GENERATOR my $image_generator; - if ("images" eq "images") { - my %imagesModeOptions = %{$self->{serverEnviron}->{pg}{displayModeOptions}{images}}; - $image_generator = WeBWorK::PG::ImageGenerator->new( - tempDir => $self->{serverEnviron}->{problemServerDirs}->{tmp}, # global temp dir - latex => $self->{serverEnviron}->{externalPrograms}->{latex}, - dvipng => $self->{serverEnviron}->{externalPrograms}->{dvipng}, - useCache => 1, - cacheDir => $self->{serverEnviron}->{problemServerDirs}{equationCache}, - cacheURL => $self->{serverEnviron}->{problemServerURLs}{equationCache}, - cacheDB => $self->{serverEnviron}->{problemServerFiles}{equationCacheDB}, - useMarkers => ($imagesModeOptions{dvipng_align} && $imagesModeOptions{dvipng_align} eq 'mysql'), - dvipng_align => $imagesModeOptions{dvipng_align}, - dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, - ); - #DEFINE CLOSURE CLASS FOR IMAGE GENERATOR - $self->{problemEnviron}{imagegen} = new ProblemServer::Utils::RestrictedClosureClass($image_generator, "add"); - } - - my $itr; - for($itr = $seed;$itr < $trials + $seed;$itr++) { - - #NEW SEED TEST - $self->{problemEnviron}{problemSeed} = $itr; + my %imagesModeOptions = %{$self->{serverEnviron}->{pg}{displayModeOptions}{images}}; + $image_generator = WeBWorK::PG::ImageGenerator->new( + tempDir => $self->{serverEnviron}->{problemServerDirs}->{tmp}, # global temp dir + latex => $self->{serverEnviron}->{externalPrograms}->{latex}, + dvipng => $self->{serverEnviron}->{externalPrograms}->{dvipng}, + useCache => 1, + cacheDir => $self->{serverEnviron}->{problemServerDirs}{equationCache}, + cacheURL => $self->{serverEnviron}->{problemServerURLs}{equationCache}, + cacheDB => $self->{serverEnviron}->{problemServerFiles}{equationCacheDB}, + useMarkers => ($imagesModeOptions{dvipng_align} && $imagesModeOptions{dvipng_align} eq 'mysql'), + dvipng_align => $imagesModeOptions{dvipng_align}, + dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, + ); + #DEFINE CLOSURE CLASS FOR IMAGE GENERATOR + $self->{problemEnviron}{imagegen} = new ProblemServer::Utils::RestrictedClosureClass($image_generator, "add"); - #ATTACH THE PROBLEM ENVIRONMENT TO TRANSLATOR - $translator->environment($self->{problemEnviron}); - - #INITIALIZING THE TRANSLATOR - $translator->initialize(); - - #PRE-LOAD MACRO FILES - eval{$translator->pre_load_macro_files( - $self->{safe}, - $self->{serverEnviron}->{pg}->{directories}->{macros}, - 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl' - )}; - warn "Error while preloading macro files: $@" if $@; - - #LOAD MACROS INTO TRANSLATOR - foreach (qw(PG.pl dangerousMacros.pl IO.pl)) { - my $macroPath = $self->{serverEnviron}->{pg}->{directories}->{macros} . "/$_"; - my $err = $translator->unrestricted_load($macroPath); - warn "Error while loading $macroPath: $err" if $err; - } - - #SET OPCODE MASK - $translator->set_mask(); - - #Retrieve Source - my $source = decode_base64($code); - #INSERT PROBLEM SOURCE CODE INTO TRANSLATOR - eval { $translator->source_string( $source ) }; - $@ and die("bad source"); - - #CREATE SAFETY FILTER - $translator->rf_safety_filter(\&ProblemServer::nullSafetyFilter); - - #RUN - $translator->translate(); - - #ANSWERS - #take array of answers and make hash - my $answerHash = {}; - $translator->process_answers($answerHash); - - my $answers = $translator->rh_evaluated_answers; - - #CREATE STRING REP - my $unique = ""; - my $key; - #foreach $key (keys %{$answers}) { - # $unique = $unique . "$key" . $answers->{"$key"}->{"correct_ans"}; - #} - $unique = encode_base64(${$translator->r_text}); - #IS IT UNIQUE - my $found = 0; - foreach(@alreadyCreated) { - if($_ eq $unique) { - $found = 1; - } - } - - #ADD HTML IF IT ISNT - if($found == 0) { - #GENERATE IMAGES AS NECESSARY - if ($image_generator) { - $image_generator->render( - body_text => $translator->r_text - ); - } - my $response = new ProblemServer::GeneratorResponse; - $response->{html} = encode_base64(${$translator->r_text}); - $response->{seed} = $itr; - #NEW PROBLEM... PLACE INTO ARRAY - push(@derivedProblems,$response); - push(@alreadyCreated,$unique); - } - #push(@derivedProblems,encode_base64(${$translator->r_text})); - - } - return \@derivedProblems; + $self->{imageGenerator} = $image_generator; } -sub checker{ - my ($self,$code,$seed,$answersEntry) = @_; - - #Create Translator Object - my $translator = WeBWorK::PG::Translator->new; - - #Attach log modules - my @modules = @{ $self->{serverEnviron}{pg}{modules} }; - # HACK for apache2 - if (MP2) { - push @modules, ["Apache2::Log"], ["APR::Table"]; - } else { - push @modules, ["Apache::Log"]; - } - - #Evaulate all module packs - foreach my $module_packages_ref (@modules) { - my ($module, @extra_packages) = @$module_packages_ref; - # the first item is the main package - $translator->evaluate_modules($module); - # the remaining items are "extra" packages - $translator->load_extra_packages(@extra_packages); - } +BEGIN { + $ProblemServer::theServer = new ProblemServer(); + $ProblemServer::theServer->setupTranslator(); + $ProblemServer::theServer->setupImageGenerator(); +} - #DEFINE SPECIFIC CHANGES TO PROBLEM ENVIRONMENT - $self->{problemEnviron}{displayMode} = translateDisplayModeNames("images"); - $self->{problemEnviron}{languageMode} = $self->{problemEnviron}{displayMode}; - $self->{problemEnviron}{outputMode} = $self->{problemEnviron}{displayMode}; - $self->{problemEnviron}{problemSeed} = $seed; +sub runTranslator { + my ($self,$source,$seed) = @_; - #PREP IMAGE GENERATOR - my $image_generator; - if ("images" eq "images") { - my %imagesModeOptions = %{$self->{serverEnviron}->{pg}{displayModeOptions}{images}}; - $image_generator = WeBWorK::PG::ImageGenerator->new( - tempDir => $self->{serverEnviron}->{problemServerDirs}->{tmp}, # global temp dir - latex => $self->{serverEnviron}->{externalPrograms}->{latex}, - dvipng => $self->{serverEnviron}->{externalPrograms}->{dvipng}, - useCache => 1, - cacheDir => $self->{serverEnviron}->{problemServerDirs}{equationCache}, - cacheURL => $self->{serverEnviron}->{problemServerURLs}{equationCache}, - cacheDB => $self->{serverEnviron}->{problemServerFiles}{equationCacheDB}, - useMarkers => ($imagesModeOptions{dvipng_align} && $imagesModeOptions{dvipng_align} eq 'mysql'), - dvipng_align => $imagesModeOptions{dvipng_align}, - dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, - ); - #DEFINE CLOSURE CLASS FOR IMAGE GENERATOR - $self->{problemEnviron}{imagegen} = new ProblemServer::Utils::RestrictedClosureClass($image_generator, "add"); - } + $source = decode_base64($source); + #Assigning Seed + $self->{problemEnviron}{problemSeed} = $seed; - #ATTACH THE PROBLEM ENVIRONMENT TO TRANSLATOR - $translator->environment($self->{problemEnviron}); + #Setting Environment + $self->{translator}->environment($self->{problemEnviron}); - #INITIALIZING THE TRANSLATOR - $translator->initialize(); + #Initializing + $self->{translator}->initialize(); #PRE-LOAD MACRO FILES - eval{$translator->pre_load_macro_files( + eval{$self->{translator}->pre_load_macro_files( $self->{safe}, $self->{serverEnviron}->{pg}->{directories}->{macros}, 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl' )}; - warn "Error while preloading macro files: $@" if $@; #LOAD MACROS INTO TRANSLATOR foreach (qw(PG.pl dangerousMacros.pl IO.pl)) { my $macroPath = $self->{serverEnviron}->{pg}->{directories}->{macros} . "/$_"; - my $err = $translator->unrestricted_load($macroPath); + my $err = $self->{translator}->unrestricted_load($macroPath); warn "Error while loading $macroPath: $err" if $err; } #SET OPCODE MASK - $translator->set_mask(); + $self->{translator}->set_mask(); - #Retrieve Source - my $source = decode_base64($code); #INSERT PROBLEM SOURCE CODE INTO TRANSLATOR - eval { $translator->source_string( $source ) }; + eval { $self->{translator}->source_string( $source ) }; $@ and die("bad source"); #CREATE SAFETY FILTER - $translator->rf_safety_filter(\&ProblemServer::nullSafetyFilter); + $self->{translator}->rf_safety_filter(\&ProblemServer::nullSafetyFilter); #RUN - $translator->translate(); - - #PROCESS ANSWERS - my ($result, $state); # we'll need these on the other side of the if block! - my $answerArray = $answersEntry; - - if (defined $answerArray and @{$answerArray}) { - # process student answers - #warn "PG: processing student answers\n"; - - #take array of answers and make hash - my $answerHash = {}; - for(my $i=0;$i<@{$answerArray};$i++) { - $answerHash->{$answerArray->[$i]{field}} = $answerArray->[$i]{answer}; - } - $translator->process_answers($answerHash); - - # retrieve the problem state and give it to the translator - #warn "PG: retrieving the problem state and giving it to the translator\n"; - $translator->rh_problem_state({ - recorded_score => "0", - num_of_correct_ans => "0", - num_of_incorrect_ans => "0", - }); - - # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by - # the PG macro package (PG.pl) - #warn "PG: determining an entry order\n"; - my @answerOrder = $translator->rh_flags->{ANSWER_ENTRY_ORDER} - ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } - : keys %{ $translator->rh_evaluated_answers }; - - - # install a grader -- use the one specified in the problem, - # or fall back on the default from the course environment. - # (two magic strings are accepted, to avoid having to - # reference code when it would be difficult.) - #warn "PG: installing a grader\n"; - my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} || $self->{serverEnviron}->{pg}->{options}->{grader}; - $grader = $translator->rf_std_problem_grader if $grader eq "std_problem_grader"; - $grader = $translator->rf_avg_problem_grader if $grader eq "avg_problem_grader"; - die "Problem grader $grader is not a CODE reference." unless ref $grader eq "CODE"; - $translator->rf_problem_grader($grader); - - # grade the problem - #warn "PG: grading the problem\n"; - ($result, $state) = $translator->grade_problem( - answers_submitted => 1, - ANSWER_ENTRY_ORDER => \@answerOrder, - %{$answerHash} #FIXME? this is used by sequentialGrader is there a better way? - ); + $self->{translator}->translate(); +} +sub runChecker { + my $self = shift; + my $answerArray = shift; - } - my $displayMode = "images"; + my $answerHash = {}; + for(my $i=0;$i<@{$answerArray};$i++) { + $answerHash->{$answerArray->[$i]{field}} = $answerArray->[$i]{answer}; + } + $self->{translator}->process_answers($answerHash); + + # retrieve the problem state and give it to the translator + #warn "PG: retrieving the problem state and giving it to the translator\n"; + $self->{translator}->rh_problem_state({ + recorded_score => "0", + num_of_correct_ans => "0", + num_of_incorrect_ans => "0", + }); + + # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by + # the PG macro package (PG.pl) + #warn "PG: determining an entry order\n"; + my @answerOrder = $self->{translator}->rh_flags->{ANSWER_ENTRY_ORDER} + ? @{ $self->{translator}->rh_flags->{ANSWER_ENTRY_ORDER} } + : keys %{ $self->{translator}->rh_evaluated_answers }; + + + # install a grader -- use the one specified in the problem, + # or fall back on the default from the course environment. + # (two magic strings are accepted, to avoid having to + # reference code when it would be difficult.) + #warn "PG: installing a grader\n"; + my $grader = $self->{translator}->rh_flags->{PROBLEM_GRADER_TO_USE} || $self->{serverEnviron}->{pg}->{options}->{grader}; + $grader = $self->{translator}->rf_std_problem_grader if $grader eq "std_problem_grader"; + $grader = $self->{translator}->rf_avg_problem_grader if $grader eq "avg_problem_grader"; + die "Problem grader $grader is not a CODE reference." unless ref $grader eq "CODE"; + $self->{translator}->rf_problem_grader($grader); + + # grade the problem + #warn "PG: grading the problem\n"; + my ($result, $state) = $self->{translator}->grade_problem( + answers_submitted => 1, + ANSWER_ENTRY_ORDER => \@answerOrder, + %{$answerHash} #FIXME? this is used by sequentialGrader is there a better way? + ); - #ANSWER EVALUATION AND PREVIEW GENERATOR - my $answers = $translator->rh_evaluated_answers; + my $answers = $self->{translator}->rh_evaluated_answers; my $key; my $preview; my $answerResponse = {}; @@ -560,18 +217,8 @@ #PREVIEW GENERATOR $preview = $answers->{"$key"}->{"preview_latex_string"}; $preview = "" unless defined $preview and $preview ne ""; - if ($displayMode eq "plainText") { - $preview = $preview; - } elsif ($displayMode eq "formattedText") { - #FIX THIS TO USE TTH - $preview = $preview; - } elsif ($displayMode eq "images") { - $preview = $image_generator->add($preview); - } elsif ($displayMode eq "jsMath") { - $preview =~ s/</</g; - $preview =~ s/>/>/g; - $preview = '<SPAN CLASS="math">\\displaystyle{'.$preview.'}</SPAN>'; - } + + $preview = $self->{imageGenerator}->add($preview); #ANSWER STRUCT $answerResponse = new ProblemServer::AnswerResponse; @@ -585,18 +232,34 @@ push(@answersArray, $answerResponse); } - #GENERATE IMAGES AS NECESSARY - if ($image_generator) { - $image_generator->render(refresh => 1); - } return \@answersArray; } -sub translateDisplayModeNames($) { - my $name = shift; - return DISPLAY_MODES()->{$name}; +sub runImageGenerator { + my $self = shift; + $self->{imageGenerator}->render(body_text => $self->{translator}->r_text); +} + +sub runImageGeneratorAnswers { + my $self = shift; + $self->{imageGenerator}->render(); +} + +sub buildProblemResponse { + my $self = shift; + my $response = new ProblemServer::ProblemResponse; + $response->{errors} = $self->{translator}->errors; + $response->{warnings} = $self->{warnings}; + $response->{output} = encode_base64(${$self->{translator}->r_text}); + $response->{seed} = $self->{translator}->{envir}{problemSeed}; + return $response; } +sub clean { + my $self = shift; + $self->{translator}->{errors} = undef; + +} sub nullSafetyFilter { return shift, 0; # no errors } @@ -605,6 +268,8 @@ #SOAP CALLABLE FUNCTIONS #################################################################################### + + =pod =begin WSDL _RETURN $string Hello World! @@ -613,45 +278,141 @@ return "hello world!"; } + =pod =begin WSDL -_IN request $ProblemServer::ProblemRequest -_RETURN $ProblemServer::ProblemResponse +_IN request $ProblemServer::ProblemRequest The Problem Request +_RETURN $ProblemServer::ProblemResponse The Problem Response =end WSDL =cut sub renderProblem { my ($self,$request) = @_; my $server = $ProblemServer::theServer; - return ProblemServer::translation($server,$request); + + $server->runTranslator($request->{code},$request->{seed}); + $server->runImageGenerator(); + + my $response = $server->buildProblemResponse(); + $server->clean(); + return $response; } =pod =begin WSDL -_IN code $string -_IN seed $string -_IN trials $string -_RETURN @string +_IN requests @ProblemServer::ProblemRequest The Problem Requests +_RETURN @ProblemServer::ProblemResponse The Problem Response +=end WSDL +=cut +sub renderProblems { + my ($self,$requests) = @_; + my $server = $ProblemServer::theServer; + + my @problems; + + foreach($requests) { + my $request = $_; + $server->runTranslator($request->{code},$request->{seed}); + $server->runImageGenerator(); + my $response = $server->buildProblemResponse(); + push(@problems,$response); + $server->clean(); + } + + return \@problems; +} + +=pod +=begin WSDL +_IN request $ProblemServer::GeneratorRequest +_RETURN $ProblemServer::GeneratorResponse +=end WSDL +=cut +sub generateProblem { + my ($self,$request) = @_; + my $server = $ProblemServer::theServer; + + my $trials = $request->{trials}; + my $problem = $request->{problem}; + my @derivedProblems; + my $found; + my $problemResponse; + for(my $itr = 0; $itr < $trials ; $itr++ ) { + $found = 0; + $server->runTranslator($problem->{code},$itr + $problem->{seed}); + $problemResponse = $server->buildProblemResponse(); + foreach(@derivedProblems) { + if($_->{output} eq $problemResponse->{output}) { + $found = 1; + } + } + if($found == 0) { + $server->runImageGenerator(); + push(@derivedProblems,$problemResponse); + } + $server->clean(); + } + + return \@derivedProblems; +} + +=pod +=begin WSDL +_IN requests @ProblemServer::GeneratorRequest +_RETURN @ProblemServer::GeneratorResponse =end WSDL =cut sub generateProblems { - my ($self,$code,$seed,$trials) = @_; + my ($self,$requests) = @_; my $server = $ProblemServer::theServer; - return ProblemServer::generator($server,$code,$seed,$trials); + + my @genResponse; + foreach($requests) { + my $request = $_; + my $trials = $request->{trials}; + my $problem = $request->{problem}; + my @derivedProblems; + my $found; + for(my $itr = 0; $itr < $trials ; $itr++ ) { + $found = 0; + $server->runTranslator($problem->{code},$trials + $problem->{seed}); + my $problemResponse = $server->buildProblemResponse(); + foreach(@derivedProblems->{output}) { + if($_ eq $problemResponse->{output}) { + $found = 1; + } + } + if($found != 1) { + $server->runImageGenerator(); + push(@derivedProblems,$problemResponse); + } + $server->clean(); + } + my $response = new ProblemServer::GeneratorResponse(); + $response->{problems} = @derivedProblems; + push(@genResponse,$response); + } + + return \@genResponse; } =pod =begin WSDL -_IN code $string -_IN seed $string +_IN request $ProblemServer::ProblemRequest _IN answers @ProblemServer::AnswerRequest _RETURN @ProblemServer::AnswerResponse =end WSDL =cut sub checkAnswers { - my ($self,$code,$seed,$answers) = @_; + my ($self,$request,$answers) = @_; my $server = $ProblemServer::theServer; - return ProblemServer::checker($server,$code,$seed,$answers); + + $server->runTranslator($request->{code},$request->{seed}); + my $result = $server->runChecker($answers); + $server->runImageGeneratorAnswers(); + + $server->clean(); + return $result; } 1; Index: GeneratorResponse.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/GeneratorResponse.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/ProblemServer/GeneratorResponse.pm -Llib/ProblemServer/GeneratorResponse.pm -u -r1.2 -r1.3 --- lib/ProblemServer/GeneratorResponse.pm +++ lib/ProblemServer/GeneratorResponse.pm @@ -2,16 +2,13 @@ =pod =begin WSDL - _ATTR html $string - _ATTR seed $string + _ATTR problems @ProblemServer::ProblemResponse An array of problems that were generated. =end WSDL =cut sub new { my $self = shift; - my $data = shift; $self = {}; - $self->{html} = $data->{html}; - $self->{seed} = $data->{seed}; + bless $self; return $self; } Index: AnswerResponse.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/AnswerResponse.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/ProblemServer/AnswerResponse.pm -Llib/ProblemServer/AnswerResponse.pm -u -r1.2 -r1.3 --- lib/ProblemServer/AnswerResponse.pm +++ lib/ProblemServer/AnswerResponse.pm @@ -2,18 +2,19 @@ =pod =begin WSDL - _ATTR field $string - _ATTR score $string - _ATTR answer $string - _ATTR answer_msg $string - _ATTR correct $string - _ATTR evaluated $string - _ATTR preview $string + _ATTR field $string The answer field + _ATTR score $string The score + _ATTR answer $string The students answer + _ATTR answer_msg $string A message about the students answer + _ATTR correct $string The correct answer + _ATTR evaluated $string The evaluated answer + _ATTR preview $string A link to the students answer image =end WSDL =cut sub new { my $self = shift; my $data = shift; + $self = {}; $self->{field} = $data->{field}; $self->{answer} = $data->{answer}; @@ -22,6 +23,7 @@ $self->{score} = $data->{score}; $self->{evaluated} = $data->{evaluated}; $self->{preview} = $data->{preview}; + bless $self; return $self; } Index: AnswerRequest.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/AnswerRequest.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/ProblemServer/AnswerRequest.pm -Llib/ProblemServer/AnswerRequest.pm -u -r1.2 -r1.3 --- lib/ProblemServer/AnswerRequest.pm +++ lib/ProblemServer/AnswerRequest.pm @@ -2,15 +2,15 @@ =pod =begin WSDL - _ATTR field $string - _ATTR answer $string + _ATTR field $string The Answer field + _ATTR answer $string The Answer fields value =end WSDL =cut sub new { my $self = shift; my $data = shift; $self = {}; - $self->{field} = $data->{field}; + $self->{field} = $data->{field}; $self->{answer} = $data->{answer}; bless $self; return $self; Index: ProblemResponse.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/ProblemResponse.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/ProblemServer/ProblemResponse.pm -Llib/ProblemServer/ProblemResponse.pm -u -r1.2 -r1.3 --- lib/ProblemServer/ProblemResponse.pm +++ lib/ProblemServer/ProblemResponse.pm @@ -2,32 +2,20 @@ =pod =begin WSDL - _ATTR id $string - _ATTR head_text $string - _ATTR body_text $string - _ATTR seed $string - _ATTR answers @ProblemServer::AnswerResponse - _ATTR warnings $string - _ATTR errors $string - _ATTR flags $string - _ATTR result $string - _ATTR state $string + _ATTR output $string The HTML output of the question + _ATTR warnings $string Any warnings from translation + _ATTR errors $string Any errors from translation + _ATTR seed $string The seed of the question. =end WSDL =cut sub new { my $self; my $data; $self = {}; - $self->{id} = "0"; - $self->{head_text} = "Default Header"; - $self->{body_text} = "Default Body"; - $self->{seed} = "0"; - $self->{answers} = {}; + $self->{output} = ""; $self->{warnings} = ""; $self->{errors} = ""; - $self->{flags} = ""; - $self->{result} = ""; - $self->{state} = ""; + $self->{seed} = ""; bless $self; return $self; } Index: ProblemRequest.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/ProblemRequest.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/ProblemServer/ProblemRequest.pm -Llib/ProblemServer/ProblemRequest.pm -u -r1.2 -r1.3 --- lib/ProblemServer/ProblemRequest.pm +++ lib/ProblemServer/ProblemRequest.pm @@ -3,23 +3,16 @@ =pod =begin WSDL - _ATTR id $string - _ATTR code $string - _ATTR seed $string - _ATTR displayMode $string - _ATTR answers @ProblemServer::AnswerRequest + _ATTR code $string The PG code to be translated + _ATTR seed $string The seed to be used for randomization =end WSDL =cut sub new { my $self = shift; my $data = shift; $self = {}; - $self->{id} = $data->{id}; $self->{code} = $data->{code}; $self->{seed} = $data->{seed}; - $self->{displayMode} = $data->{displayMode}; - $self->{answers} = $data->{answers}; - bless $self; return $self; } Index: Environment.pm =================================================================== RCS file: /webwork/cvs/system/ww_question_server/lib/ProblemServer/Environment.pm,v retrieving revision 1.4 retrieving revision 1.5 diff -Llib/ProblemServer/Environment.pm -Llib/ProblemServer/Environment.pm -u -r1.4 -r1.5 --- lib/ProblemServer/Environment.pm +++ lib/ProblemServer/Environment.pm @@ -8,13 +8,13 @@ BEGIN { $main::VERSION = "2.3.2"; } sub new { - my ($self,$path) = @_; + my ($self) = @_; my $safe = Safe->new; # Compile the "include" function with all opcodes available. my $include = q[ sub include { my ($file) = @_; - my $fullPath = "].$path.q[/$file"; + my $fullPath = "].$ProblemServer::RootDir.q[/$file"; # This regex matches any string that begins with "../", # ends with "/..", contains "/../", or is "..". if ($fullPath =~ m!(?:^|/)\.\.(?:/|$)!) { @@ -40,9 +40,15 @@ #die $preps; #$safe->reval($preps); - my $globalEnvironmentFile = "$path/conf/global.conf"; + my $globalEnvironmentFile = $ProblemServer::RootDir . "/conf/global.conf"; my $globalFileContents = readFile($globalEnvironmentFile); + my $settings = "\$ProblemServer::RootDir = '" . $ProblemServer::RootDir . "';\n"; + $settings .= "\$ProblemServer::RootPGDir = '" . $ProblemServer::RootPGDir . "';\n"; + $settings .= "\$ProblemServer::Host = '" . $ProblemServer::Host . "';\n"; + $settings .= "\$ProblemServer::FilesURL = '" . $ProblemServer::FilesURL . "';\n"; + $safe->reval($settings); + #die $globalFileContents; #$globalFileContents = $preps.'\n'.$globalFileContents; $safe->reval($globalFileContents); |
From: dpvc v. a. <we...@ma...> - 2007-08-29 02:12:02
|
Log Message: ----------- Fix substitute() so that if the variable is being substituted for a constant, only the branch that contains that value is returned. If it is substituted by another variable, the variables for the inequalities will also be changed. (Otherwise the variable is replaced only in the functions; not perfect, but the best that I could come up with.) Modified Files: -------------- pg/macros: contextPiecewiseFunction.pl Revision Data ------------- Index: contextPiecewiseFunction.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextPiecewiseFunction.pl,v retrieving revision 1.1 retrieving revision 1.2 diff -Lmacros/contextPiecewiseFunction.pl -Lmacros/contextPiecewiseFunction.pl -u -r1.1 -r1.2 --- macros/contextPiecewiseFunction.pl +++ macros/contextPiecewiseFunction.pl @@ -513,16 +513,29 @@ # # Substitute into each branch individually. -# -# ### FIXME: only allow the variable to be substituted -# by another variable, and change the intervals as well. -# If it is a constant, then evaluate? +# If the function's variable is substituted, then +# if it is a constant, find the branch for that value +# and substitute into that, otherwise if it is +# just another variable, replace the variable +# in the inequalities as well as the formulas. +# Otherwise, just replace in the formulas. # sub substitute { - my $self = shift; my @cases = (); + my $self = shift; + my @cases = (); my $x = $self->{varName}; + $self->setValues(@_); my $a = $self->{values}{$x}; $self->unsetValues(@_); + if (defined $a) { + if (!Value::isFormula($a)) { + my $f = $self->getFunctionFor($a); + die "undefined value" unless defined $f; + return $f->substitute(@_); + } + $x = $a->{tree}{name} if $a->{tree}->class eq 'Variable'; + } foreach my $If (@{$self->{data}}) { my ($I,$f) = @{$If}; - push(@cases,$I->copy => $f->substitute(@_)); + $I = $I->copy; if ($x ne $I->{varName}) {$I->{varName} = $x; $I->updateParts} + push(@cases,$I => $f->substitute(@_)); } push(@cases,$self->{otherwise}->substitute(@_)) if defined $self->{otherwise}; return $self->make(@cases); @@ -580,6 +593,41 @@ } # +# Look up the function for the nth branch (or the "otherwise" +# function if n is omitted or too big or too small). +# +sub getFunction { + my $self = shift; my $n = shift; + return $self->{otherwise} if !defined $n || $n < 1 || $n > $self->length; + return $self->{data}[$n-1][1]; +} + +# +# Look up the domain for the nth branch (or the "otherwise" +# domain if n is omitted or too big or too small). +# +sub getDomain { + my $self = shift; my $n = shift; + return $self->Package("Inequality")->new($self->context, + $self->domainR - $self->domainUnion,$self->{varName}) + if !defined $n || $n < 1 || $n > $self->length; + return $self->{data}[$n-1][0]; +} + +# +# Get the function for the given value of the variable +# (or undef if there is none). +# +sub getFunctionFor { + my $self = shift; my $x = shift; + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @$If; + return $f if $I->contains($x); + } + return $self->{otherwise}; +} + +# # Implements the <=> operator (really only handles equality ir not) # sub compare { |
From: dpvc v. a. <we...@ma...> - 2007-08-29 02:07:44
|
Log Message: ----------- Check that "and" and "or" combine inequalities that use the same variable. Modified Files: -------------- pg/macros: contextInequalities.pl Revision Data ------------- Index: contextInequalities.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextInequalities.pl,v retrieving revision 1.9 retrieving revision 1.10 diff -Lmacros/contextInequalities.pl -Lmacros/contextInequalities.pl -u -r1.9 -r1.10 --- macros/contextInequalities.pl +++ macros/contextInequalities.pl @@ -275,8 +275,10 @@ my $self = shift; $self->Error("The operands of '%s' must be inequalities",$self->{bop}) unless $self->{lop}{isInequality} && $self->{rop}{isInequality}; + $self->Error("Inequalities combined by '%s' must both use the same variable",$self->{bop}) + unless $self->{lop}{varName} eq $self->{rop}{varName}; $self->{type} = Value::Type("Interval",2); - $self->{varName} = $self->{lop}{varName} || $self->{rop}{varName}; + $self->{varName} = $self->{lop}{varName}; $self->{isInequality} = 1; } @@ -293,8 +295,10 @@ my $self = shift; $self->Error("The operands of '%s' must be inequalities",$self->{bop}) unless $self->{lop}{isInequality} && $self->{rop}{isInequality}; + $self->Error("Inequalities combined by '%s' must both use the same variable",$self->{bop}) + unless $self->{lop}{varName} eq $self->{rop}{varName}; $self->{type} = Value::Type("Interval",2); - $self->{varName} = $self->{lop}{varName} || $self->{rop}{varName}; + $self->{varName} = $self->{lop}{varName}; $self->{isInequality} = 1; } |
From: dpvc v. a. <we...@ma...> - 2007-08-29 00:13:51
|
Log Message: ----------- Implementation of a PiecewiseFunction class. Both students and problem authors can enter peicewise functions, and they will display nicely in TeX mode. They can be evaluated, reduced, substituted, and so on, just like other functions. There are undoubtedly more features that it needs, but it's a start. Added Files: ----------- pg/macros: contextPiecewiseFunction.pl Revision Data ------------- --- /dev/null +++ macros/contextPiecewiseFunction.pl @@ -0,0 +1,769 @@ +loadMacros("MathObjects.pl"); +loadMacros("contextInequalities.pl"); + +sub _contextPiecewiseFunction_init {PiecewiseFunction::Init()} + +=head1 Context("PiecewiseFunction"); + +###################################################################### +# +# This file implements a context in which piecewise-defined functions +# can be specified by students and problem authors. To use it, add +# +# loadMacros("contextPiecewieFunction.pl"); +# +# and then use +# +# Context("PiecewiseFuntion"); +# +# to select the context for piecewise functions. There are several +# ways to produce a piecewise function. For example: +# +# $f = Compute("x if x >= 0 else -x"); +# $f = Compute("x if x >= 0 else -x if x < 0"); +# $f = Formula("x+1 if x > 2 else 4 if x = 2 else 1-x"); +# $f = PiecewiseFunction("x^2 if 1 < x <= 2 else 2x+1"); +# $f = PiecewiseFunction("1 < x <= 2" => "x^2", "2x+1"); +# $f = PiecewiseFunction("(1,2]" => "x^2", "2x+1"); +# $f = PiecewiseFunction(Interval("(1,2]") => "x^2", "2x+1"); +# +# You can use either Compute() or Formula() interchangeably to +# convert a string containing "if" and "else" to the corresponding +# PiecewiseFunction. The PiecewiseFunction() constructor can +# also do this, or you can pass it a list of interval=>formula +# pairs that specify the various branches. If there is an +# unpaired final formula, it represents the "otherwise" portion +# of the function (the formula to use of the input is not in +# any of the given intervals). +# +# Note that you can use Inveral, Set, or Union objects in place of +# the intervals in the specification of a piecewise function. +# +# The PiecewiseFunction object TeXifies using a LaTeX "cases" +# environment, so you can use these objects to produce nice +# output even if you are not asking a student to enter one. +# For example: +# +# Context("PiecewiseFunction"); +# +# $f = Formula("1-x if x > 0 else 4 if x = 0 else 1+x if x < 0"); +# $a = random(-2,2,.1); +# +# Context()->texStrings; +# BEGIN_TEXT +# Suppose \(f(x)=$f\). Then \(f($a)\) = \{ans_rule(20)\}. +# END_TEXT +# Context()->normalStrings; +# +# ANS($f->eval(x=>$a)->cmp); +# +###################################################################### + +=cut + +package PiecewiseFunction; + +# +# Create the needed context and the constructor function +# +sub Init { + my $context = $main::context{PiecewiseFunction} = Parser::Context->getCopy("Inequalities"); + $context->{value}{PiecewiseFunction} = 'PiecewiseFunction::Function'; + $context->operators->add( + "if " => { + precedence =>.31, associativity => 'left', type => 'binary', + string => ' if ', TeX => '\hbox{ if }', class => 'PiecewiseFunction::BOP::if', + }, + + "for " => { + precedence =>.31, associativity => 'left', type => 'binary', + string => ' for ', TeX => '\hbox{ for }', class => 'PiecewiseFunction::BOP::if', + }, + + "else" => { + precedence =>.3, associativity => 'right', type => 'binary', + string => " else\n", TeX => '\hbox{ else }', class => 'PiecewiseFunction::BOP::else', + }, + + "in " => { + precedence => .35, asscoiativity => 'right', type => 'binary', + string => ' in ', TeX => '\in ', class => 'PiecewiseFunction::BOP::in', + }, + ); + $context->{value}{InequalityIn} = 'PiecewiseFunction::Interval'; + $context->{value}{'Formula()'} = 'PiecewiseFunction::Formula'; + $context->{cmpDefaults}{PiecewiseFunction} = {reduceSets => 1, requireParenMatch => 1}; + + main::PG_restricted_eval('sub PiecewiseFunction {Value->Package("PiecewiseFunction")->new(@_)}'); +} + +################################################## +################################################## + +# +# A class to implement undefined values (points that +# are not in the domain of the function) +# +package PiecewiseFunction::undefined; +our @ISA = ('Value'); + +sub new { + my $self = shift; my $class = ref($self) || $self; + my $equation = shift; + bless {data => [], isUndefined => 1, equation => $equation}, $class; +} + +sub value {undef} + +sub string {die "undefined value"} +sub TeX {die "undefined value"} +sub perl {"PiecewiseFunction::undefined->new()"} + +################################################## +# +# Implement the "if" operator to specify a branch +# of the piecewise function. +# +package PiecewiseFunction::BOP::if; +our @ISA = ('Parser::BOP'); + +# +# Only allow inequalities on the right. +# Mark the object with identifying values +# +sub _check { + my $self = shift; + $self->Error("The condition should be an inequality") unless $self->{rop}{isInequality}; + $self->{type} = {%{$self->{lop}->typeRef}}; + $self->{isIf} = $self->{canCompute} = 1; + $self->{varName} = $self->{rop}{varName} || ($self->context->variables->names)[0]; +} + +# +# Return the function's value if the variable is within +# the inequality for this branch (otherwise return +# and undefined value). +# +sub eval { + my $self = shift; + my $I = $self->{rop}->eval; + return PiecewiseFunction::undefined->new unless $I->contains($self->{equation}{values}{$self->{varName}}); + return $self->{lop}->eval; +} + +# +# Make a piecewise function from this branch +# +sub Compute { + my $self = shift; my $context = shift || $self->context; my $method = shift || "new"; + return $context->Package("PiecewiseFunction")->$method($context,$self->flatten($context)); +} + +# +# Make an interval=>formula pair from this item +# +sub flatten { + my $self = shift; my $context = shift || $self->context; + my $I = $self->{rop}->eval; + my $f = $context->Package("Formula")->new($context,$self->{lop}); + return ($I => $f); +} + +# +# Print using the TeX method of the PiecewiseFunction object +# +sub TeX {(shift)->Compute(undef,"make")->TeX} + +# +# Make an if-then-else statement that returns the function's +# value or an undefined value (depending on whether the +# variable is in the interval or not). +# +sub perl { + my $self = shift; my $parens = shift; + my $I = $self->{rop}->eval; my $x = "\$".$self->{varName}; + my $condition = $I->perl.'->contains('.$x.')'; + my $lop = $self->{lop}->perl; my $rop = 'PiecewiseFunction::undefined->new'; + return '('.$condition.' ? '.$lop.' : '.$rop.')' +} + +################################################## +# +# Implement the "else" operator to join the +# different branches of the function. +# +package PiecewiseFunction::BOP::else; +our @ISA = ('Parser::BOP'); + +# +# Make sure there is an "if" that goes with this else. +# +sub _check { + my $self = shift; + $self->Error("You must have an 'if' to the left of 'else'") unless $self->{lop}{isIf}; + $self->{type} = {%{$self->{lop}->typeRef}}; + $self->{isElse} = $self->{canCompute} = 1; +} + +# +# Use the result of the "if" to decide which value to return. +# +sub eval { + my $self = shift; my $lop = $self->{lop}->eval; + return (ref($lop) eq 'PiecewiseFunction::undefined' ? $self->{rop}->eval : $lop); +} + +# +# Make a PiecewiseFunction from the (nested) if-then-else values. +# +sub Compute { + my $self = shift; my $context = shift || $self->context; my $method = shift || "new"; + return $context->Package("PiecewiseFunction")->$method($context,$self->flatten($context)) +} + +# +# Recursively flatten the if-then-else tree to a list +# of interval=>formula pairs. +# +sub flatten { + my $self = shift; my $context = shift || $self->context; + my $flatten = $self->{rop}->can("flatten"); + return ($self->{lop}->flatten($context),&$flatten($self->{rop},$context)) if $flatten; + my $f = $context->Package("Formula")->new($context,$self->{rop}); + return ($self->{lop}->flatten($context),$f); +} + +# +# Don't do extra parens for nested else's. +# +sub string { + my ($self,$precedence,$showparens,$position,$outerRight) = @_; + my $string; my $bop = $self->{def}; + $position = '' unless defined($position); + $showparens = '' unless defined($showparens); + my $addparens = defined($precedence) && ($showparens eq 'all' || $precedence > $bop->{precedence}); + $outerRight = !$addparens && ($outerRight || $position eq 'right'); + + $string = $self->{lop}->string($bop->{precedence},$bop->{leftparens},'left',$outerRight). + $bop->{string}. + $self->{rop}->string($bop->{precedence}); + + $string = $self->addParens($string) if $addparens; + return $string; +} + +# +# Use the PiecewiseFunction TeX method. +# +sub TeX {(shift)->Compute(undef,"make")->TeX} + +# +# Use an if-then-else to determine the value to use. +# +sub perl { + my $self = shift; my $parens = shift; + my $I = $self->{lop}{rop}->eval; my $x = "\$".$self->{lop}{varName}; + my $condition = $I->perl.'->contains('.$x.')'; + my $lop = $self->{lop}{lop}->perl; my $rop = $self->{rop}->perl; + return '('.$condition.' ? '.$lop.' : '.$rop.')'; +} + + +################################################## +# +# Implement an "in" operator for "x in (a,b)" as an +# alternative to inequality notation. +# +package PiecewiseFunction::BOP::in; +our @ISA = ('Parser::BOP'); + +# +# Make sure the variable is to the left and an interval, +# set, or union is to the right. +# +sub _check { + my $self = shift; + $self->{type} = Value::Type("Interval",2); + $self->{isInequality} = 1; + $self->Error("There should be a variable to the left of '%s'",$self->{bop}) + unless $self->{lop}->class eq 'Variable'; + $self->Error("There should be a set of numbers to the right of '%s'",$self->{bop}) + unless $self->{rop}->isSetOfReals; + $self->{varName} = $self->{lop}{name}; + delete $self->{equation}{variables}{$self->{lop}{name}} if $self->{lop}{isNew}; + $self->{lop} = Inequalities::DummyVariable->new($self->{equation},$self->{lop}{name},$self->{lop}{ref}); +} + +# +# Call this an Inequality so it will be allowed to the +# right of "if" operators. +# +sub _eval { + my $self = shift; + bless $self->Package("Inequality")->new($_[1],$self->{varName}), + $self->Package("InequalityIn"); +} + +################################################## +# +# This implements the "in" operator as in inequality. +# We inherit all the inequality methods, and simply +# need to handle the string and TeX output. The +# underlying type is still an Inerval. +# +package PiecewiseFunction::Interval; +our @ISA = ("Inequalities::Interval"); + +sub string { + my $self = shift; my $equation = shift; + my $x = $self->{varName} || ($self->context->variables->names)[0]; + $x = $context->{variables}{$x}{string} if defined $context->{variables}{$x}{string}; + $x . ' in ' . $self->demote->string; +} + +sub TeX { + my $self = shift; my $equation = shift; + my $x = $self->{varName} || ($self->context->variables->names)[0]; + $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX}; + $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/; + $x . '\in ' . $self->demote->TeX; +} + +################################################## +################################################## +# +# This implements the PiecewiseFunction. It is an unusual mix +# of a Value object and a Formula object. It looks like a +# Formula for the most part, but doesn't have the same internal +# structure. Most of the Formula methods have been provided +# so that eval, substitute, reduce, etc will be applied to all +# the branches. +# +package PiecewiseFunction::Function; +our @ISA = ('Value', 'Value::Formula'); + +# +# Create the PiecewiseFunction object, with error reporting +# for problems in the data. +# +# Usage: PiecewiseFunction("formula") +# PiecewiseFunction(I1 => f1, I2 => f2, ... , fn); +# +# In the first case, the formula is parsed for "if" and "else" values +# to produce the function. In the second, the function is given +# by interval/formula pairs that associate what function to map over +# interval. If there is an unpaired formula at the end, it is +# the "otherwise" formula that will be used whenever the input +# does not fall into one of the given intervals. +# +# Note that the intervals above actually can be Interval, Set, +# or Union objects, not just plain intervals. +# +sub new { + my $self = shift; my $class = ref($self) || $self; + my $context = (Value::isContext($_[0]) ? shift : $self->context); + Value->Error("You must provide at least one Formula for a Piecewise Function") unless scalar(@_); + my $F = shift; $F = [$F,@_] if scalar(@_); + return $F if ref($F) eq $class; + unless (ref($F) eq 'ARRAY') { + $F = $context->Package("Formula")->new($context,$F); + if ($F->{tree}->can("Compute")) { + $F = $F->{tree}->Compute($context); + return $F if ref($F) eq $class; + } + $F = [$F]; + } + my $pf = bless {data => [], context => $context, isPiecewiseFunction => 1}, $class; + my $x = ''; $pf->{variables} = {}; + while (scalar(@$F) > 1) { + my $I = shift(@$F); my $f = shift(@$F); + $I = $context->Package("Interval")->new($context,$I) unless Value::classMatch($I,"Interval","Set","Union"); + $f = $context->Package("Formula")->new($context,$f) unless Value::isFormula($f); + $I->{equation} = $f->{equation} = $pf; ### Transfer equation flag? + push(@{$pf->{data}},[$I,$f]); + $x = $I->{varName} unless $x; + foreach my $v (keys %{$f->{variables}}) {$pf->{variables}{$v} = 1} + } + if (scalar(@$F)) { + $pf->{otherwise} = $context->Package("Formula")->new($context,shift(@$F)); + $pf->{otherwise}{equation} = $pf; ### transfer? + foreach my $v (keys %{$pf->{otherwise}{variables}}) {$pf->{variables}{$v} = 1} + } + $pf->{varName} = ($x || ($context->variables->names)[0]); + $pf->{variables}{$pf->{varName}} = 1; + $pf->check; + return $pf; +} + +# +# Create a PiecewiseFunction without error checking (so overlapping intervals, +# incorrect variables, and so on could appear). +# +sub make { + my $self = shift; my $class = ref($self) || $self; + my $context = (Value::isContext($_[0]) ? shift : $self->context); + my $pf = bless {data => [], context => $context, isPiecewiseFunction => 1}, $class; + my $x = ''; + while (scalar(@_) > 1) { + my $I = shift; my $f = shift; + $I->{equation} = $f->{equation} = $pf; ### Transfer equation flag? + $x = $I->{varName} unless $x; + push(@{$pf->{data}},[$I,$f]); + $self->{typeRef} = $f->typeRef unless defined $self->{typeRef}; + foreach my $v (keys %{$f->{variables}}) {$pf->{variables}{$v} = 1} + } + if (scalar(@_)) { + $pf->{otherwise} = shift; + $pf->{otherwise}{equation} = $pf; ### transfer? + foreach my $v (keys %{$f->{otherwise}{variables}}) {$pf->{variables}{$v} = 1} + } + $pf->{varName} = ($x || ($context->variables->names)[0]); + $pf->{variables}{$pf->{varName}} = 1; + return $pf; +} + +# +# Do the consistency checks for the separate branches. +# +sub check { + my $self = shift; + $self->checkVariable; + $self->checkMultipleValued; + $self->checkTypes; +} + +# +# Check that all the inequalities are for the same variable. +# +sub checkVariable { + my $self = shift; my $context = $self->context; + my $x = $self->{varName}; + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @$If; + $I = $If->[0] = $context->Package("Inequality")->new($context,$I,$x) + unless $I->classMatch("Inequality"); + Value->Error("All the intervals must use the same variable") if $I->{varName} ne $x; + } +} + +# +# Check that no domain intervals overlap. +# +sub checkMultipleValued { + my $self = shift; + my @D = $self->domainUnion->sort->value; + foreach my $i (0..scalar(@D)-2) { + my ($I,$J) = @D[$i,$i+1]; + Value->Error("A piecewise function can't have overlapping domain intervals") + if $I->intersects($J); + } +} + +# +# Check that all the branches return the same type of result. +# +sub checkTypes { + my $self = shift; + foreach my $If (@{$self->{data}}) {$self->checkType($If->[1])} + $self->checkType($self->{otherwise}) if defined $self->{otherwise}; +} + +sub checkType { + my $self = shift; my $f = shift; + if (defined $self->{typeRef}) { + Value->Error("All the formulas must produce the same type of answer") + unless Parser::Item::typeMatch($self->{typeRef},$f->typeRef); + } else {$self->{typeRef} = $f->typeRef} +} + +# +# This is always considered a formula. +# +sub isConstant {0} + +# +# Look through the branches for the one that contains +# the variable's value, and evaluate it. If not in +# any of the intervals, use the "otherwise" value, +# or die with no value if there isn't one. +# +sub eval { + my $self = shift; + $self->setValues(@_); my $x = $self->{values}{$self->{varName}}; $self->unsetValues; + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + return $f->eval(@_) if $I->contains($x); + } + return $self->{otherwise}->eval(@_) if defined $self->{otherwise}; + die "undefined value"; +} + +# +# Reduce each branch individually. +# +sub reduce { + my $self = shift; my @cases = (); + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + push(@cases,$I->copy => $f->reduce(@_)); + } + push(@cases,$self->{otherwise}->reduce(@_)) if defined $self->{otherwise}; + return $self->make(@cases); +} + +# +# Substitute into each branch individually. +# +# ### FIXME: only allow the variable to be substituted +# by another variable, and change the intervals as well. +# If it is a constant, then evaluate? +# +sub substitute { + my $self = shift; my @cases = (); + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + push(@cases,$I->copy => $f->substitute(@_)); + } + push(@cases,$self->{otherwise}->substitute(@_)) if defined $self->{otherwise}; + return $self->make(@cases); +} + + +# +# Return the domain of the function (will be (-inf,inf) if +# there is an "otherwise" formula. +# +sub domain { + my $self = shift; + return $self->domainR if defined $self->{otherwise}; + return $self->domainUnion->reduce; +} + +# +# The set (-inf,inf). +# +sub domainR { + my $self = shift; my $context = $self->context; + my $Infinity = $context->Package("Infinity")->new($context); + return $context->Package("Interval")->make($context,'(',-$Infinity,$Infinity,')'); +} + +# +# The domain formed by the explicitly given intervals +# (excludes the "otherwise" portion, if any) +# +sub domainUnion { + my $self = shift; my $context = $self->context; + my @cases = (); foreach my $If (@{$self->{data}}) {push(@cases,$If->[0])} + return $context->Package("Union")->make($context,@cases); +} + +# +# Creates a copy of the PiecewiseFunction where the "otherwise" +# formula has been given explicit intervals within the object. +# (This makes it easier to compare two PiecewiseFormulas +# interval by interval.) +# +sub noOtherwise { + my $self = (shift)->copy; my $context = $self->context; + return $self unless defined $self->{otherwise}; + my $otherwise = $self->domainR - $self->domainUnion->reduce; + return $self if $otherwise->isEmpty; + $otherwise = $context->Package("Union")->new($context,$otherwise) unless $otherwise->type eq 'Union'; + foreach my $I ($otherwise->value) { + my $D = $context->Package("Inequality")->new($context,$I,$self->{varName}); + push(@{$self->{data}},[$D,$self->{otherwise}]); + } + delete $self->{otherwise}; + foreach my $If (@{$self->{data}}) {$If->[0]{equation} = $If->[1]{equation} = $self} + return $self; +} + +# +# Implements the <=> operator (really only handles equality ir not) +# +sub compare { + my ($l,$r,$flag) = @_; my $self = $l; + my $context = $self->context; my $result; + $r = $context->Package("PiecewiseFunction")->new($context,$r) unless Value::classMatch($r,"PiecewiseFunction"); + Value::Error("Formulas from different contexts can't be compared") + unless $l->{context} == $r->{context}; + $l = $l->noOtherwise; $r = $r->noOtherwise; + $result = $l->compareDomains($r); return $result if $result; + $result = $l->compareFormulas($r); return $result if $result; + return 0; +} + +# +# Check that the funciton domains have the same number of +# components, and that those components agree, interval by interval. +# +sub compareDomains { + my $self = shift; my $other = shift; + my @D0 = $self->domainUnion->sort->value; + my @D1 = $other->domainUnion->sort->value; + return scalar(@D0) <=> scalar(@D1) unless scalar(@D0) == scalar(@D1); + foreach my $i (0..$#D0) { + my $result = ($D0[$i] <=> $D1[$i]); + return $result if $result; + } + return 0; +} + +# +# Now that the intervals are known to agree, compare +# the individual functions on each interval. Do an +# appropriate check depending on the type of each +# branch: Interval, Set, or Union. +# +sub compareFormulas { + my $self = shift; my $other = shift; + my @D0 = main::PGsort(sub {$_[0][0] < $_[1][0]}, $self->value); + my @D1 = main::PGsort(sub {$_[0][0] < $_[1][0]}, $other->value); + foreach my $i (0..$#D0) { + my ($D,$f0,$f1) = (@{$D0[$i]},$D1[$i][1]); + my $method = "compare".$D->type; + my $result = $self->$method($D,$f0,$f1); + return $result if $result; + } + return 0; +} + +# +# Use the Interval to determine the limits for use +# in comparing the two functions. +# +sub compareInterval { + my $self = shift; my ($D,$f0,$f1) = @_; + my ($a,$b) = $D->value; $a = $a->value; $b = $b=>value; + return $f0 == $f1 if $D->{leftInfinite} && $D->{rightInfinite}; + $a = $b - 2 if $D->{leftInfinite}; + $b = $a + 2 if $D->{rightInfinite}; + return $f0->with(limits=>[$a,$b]) <=> $f1; +} + +# +# For a set, check that the functions agree on every point. +# +sub compareSet { + my $self = shift; my ($D,$f0,$f1) = @_; + my $x = $self->{varName}; + foreach my $a ($self->value) { + my $result = $f0->eval($x=>$a) <=> $f1->eval($x=>$a); + return $result if $result; + } + return 0; +} + +# +# For a union, do the appropriate check for +# each object in the union. +# +sub compareUnion { + my $self = shift; my ($D,$f0,$f1) = @_; + foreach my $S ($self->value) { + my $method = "compare".$S->type; + my $result = $self->$method($D,$f0,$f1); + return $result if $result; + } + return 0; +} + + +# +# Stringify using newlines at after each "else". +# (Otherwise the student and correct answer can +# get unacceptably long.) +# +sub string { + my $self = shift; my @cases = (); + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + push(@cases,$f->string." if ".$I->string); + } + push(@cases,$self->{otherwise}->string) if defined $self->{otherwise}; + join(" else\n",@cases); +} + +# +# TeXify using a "cases" LaTeX environment. +# +sub TeX { + my $self = shift; my @cases = (); + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + push(@cases,'\displaystyle{'.$f->TeX."}&\\text{if \$".$I->TeX."\$}"); + } + if (scalar(@cases)) { + push(@cases,'\displaystyle{'.$self->{otherwise}->TeX.'}&\text{otherwise}') if defined $self->{otherwise}; + return '\begin{cases}'.join('\cr'."\n",@cases).'\end{cases}'; + } else { + return $self->{otherwise}->TeX; + } +} + +# +# Create a code segment that returns the correct value depending on which +# interval contains the variable's value (or an undefined value). +# +sub perl { + my $self = shift; my $x = "\$".$self->{varName}; + my @cases = (); + foreach my $If (@{$self->{data}}) { + my ($I,$f) = @{$If}; + push(@cases,'return '.$f->perl.' if '.$I->perl.'->contains('.$x.');'); + } + if (defined($self->{otherwise})) {push(@cases,'return '.$self->{otherwise}->perl.';')} + else {push(@cases,'die "undefined value";')} + return join("\n",@cases); +} + + +# +# Handle the types correctly for error messages and such. +# +sub class {"PiecewiseFunction"} +sub showClass { + my $self = shift; + my $f = $self->{data}[0][1]; $f = $self->{otherwise} unless defined $f; + 'a Formula that returns '.Value::showType($f->{tree}); +} + +sub type {(shift)->{typeRef}{name}} +sub typeRef {(shift)->{typeRef}} + +# +# Allow comparison only when the two functions return +# the same type of result. +# +sub typeMatch { + my $self = shift; my $other = shift; my $ans = shift; + return $self->type eq $other->type; +} + +################################################## +# +# Overrides the Formula() command so that if +# the result is a PiecewiseFunction, it is +# turned into one automatically. Conversely, +# if a PiecewiseFunction is put into Formula(), +# this will turn it into a Formula. +# +package PiecewiseFunction::Formula; +our @ISA = ('Value::Formula'); + +sub new { + my $self = shift; my $f; + if (scalar(@_) == 1 && Value::classMatch($_[0],"PiecewiseFunction")) { + $f = $_[0]->string; $f =~ s/\n/ /g; + $f = $self->Package("Formula")->new($f); + } else { + $f = $self->Package("Formula")->new(@_); + $f = $f->{tree}->Compute if $f->{tree}{canCompute}; + } + return $f; +} + +###################################################################### + +1; |
From: dpvc v. a. <we...@ma...> - 2007-08-29 00:09:30
|
Log Message: ----------- Make the TeX output for <= be \le rather than <= Modified Files: -------------- pg/macros: contextInequalities.pl Revision Data ------------- Index: contextInequalities.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextInequalities.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/contextInequalities.pl -Lmacros/contextInequalities.pl -u -r1.8 -r1.9 --- macros/contextInequalities.pl +++ macros/contextInequalities.pl @@ -539,8 +539,8 @@ my $x = $self->{varName} || ($context->variables->names)[0]; $x = $context->{variables}{$x}{TeX} if defined $context->{variables}{$x}{TeX}; $x =~ s/^([^_]+)_?(\d+)$/$1_{$2}/; - my $left = ($open eq '(' ? ' < ' : ' <= '); - my $right = ($close eq ')' ? ' < ' : ' <= '); + my $left = ($open eq '(' ? ' < ' : ' \le '); + my $right = ($close eq ')' ? ' < ' : ' \le '); my $inequality = ""; $inequality .= $a->string.$left unless $self->{leftInfinite}; $inequality .= $x; |
From: Mike G. v. a. <we...@ma...> - 2007-08-28 23:14:48
|
Log Message: ----------- Backporting changes from HEAD to rel-2-4-dev Modified Files: -------------- pg/lib/Value: AnswerChecker.pm pg/macros: PG.pl PGstandard.pl Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.109 retrieving revision 1.110 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.109 -r1.110 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -1,3 +1,4 @@ + =head1 DESCRIPTION ############################################################# @@ -25,10 +26,19 @@ # $Value::defaultContext->{cmpDefaults} = {}; +=head5 $mathObject->cmp_defaults() + +# Internal use. +# Set default flags for the answer checker in this object +# showTypeWarnings => 1 +# showEqualErrors => 1 +# ignoreStrings => 1 +# studentsMustReduceUnions => 1 +# show UnionReduceWarnings => 1 +# + +=cut -# -# Default flags for the answer checkers -# sub cmp_defaults {( showTypeWarnings => 1, showEqualErrors => 1, @@ -37,9 +47,11 @@ showUnionReduceWarnings => 1, )} + # # Special Context flags to be set for the student answer # + sub cmp_contextFlags { my $self = shift; my $ans = shift; return ( @@ -62,6 +74,7 @@ # # Create an answer checker for the given type of object # + sub cmp { my $self = shift; my $ans = new AnswerEvaluator; Index: PG.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PG.pl,v retrieving revision 1.32 retrieving revision 1.33 diff -Lmacros/PG.pl -Lmacros/PG.pl -u -r1.32 -r1.33 --- macros/PG.pl +++ macros/PG.pl @@ -390,8 +390,8 @@ When PG problems are evaluated, the result of evaluating the entire problem is interpreted as the return value of ENDDOCUMENT(). Therefore, ENDDOCUMENT() must -the last executable statement of every problem. It can only appear once. It -returns an list consisting of: +be the last executable statement of every problem. It can only appear once. It +returns a list consisting of: =over Index: PGstandard.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGstandard.pl,v retrieving revision 1.2 retrieving revision 1.3 diff -Lmacros/PGstandard.pl -Lmacros/PGstandard.pl -u -r1.2 -r1.3 --- macros/PGstandard.pl +++ macros/PGstandard.pl @@ -1,3 +1,4 @@ + =head1 NAME PGstandard.pl - Load standard PG macro packages. |
From: dpvc v. a. <we...@ma...> - 2007-08-28 22:46:17
|
Log Message: ----------- Add context names for the context(s) created here. Modified Files: -------------- pg/macros: contextABCD.pl contextCurrency.pl contextInequalities.pl contextIntegerFunctions.pl contextLimitedComplex.pl contextLimitedNumeric.pl contextLimitedPoint.pl contextLimitedPolynomial.pl contextLimitedVector.pl contextScientificNotation.pl contextString.pl contextTF.pl parserImplicitEquation.pl parserImplicitPlane.pl parserParametricLine.pl Revision Data ------------- Index: parserImplicitEquation.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitEquation.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/parserImplicitEquation.pl -Lmacros/parserImplicitEquation.pl -u -r1.8 -r1.9 --- macros/parserImplicitEquation.pl +++ macros/parserImplicitEquation.pl @@ -135,6 +135,7 @@ sub Init { my $context = $main::context{ImplicitEquation} = Parser::Context->getCopy("Numeric"); + $context->{name} = "ImplicitEquation"; $context->variables->are(x=>'Real',y=>'Real'); $context{precedence}{ImplicitEquation} = $context->{precedence}{special}; Parser::BOP::equality->Allow($context); Index: contextABCD.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextABCD.pl,v retrieving revision 1.9 retrieving revision 1.10 diff -Lmacros/contextABCD.pl -Lmacros/contextABCD.pl -u -r1.9 -r1.10 --- macros/contextABCD.pl +++ macros/contextABCD.pl @@ -34,6 +34,7 @@ sub _contextABCD_init { my $context = $main::context{ABCD} = Parser::Context->getCopy("String"); + $context->{name} = "ABCD"; $context->strings->are( "A" => {}, "B" => {}, Index: parserParametricLine.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserParametricLine.pl,v retrieving revision 1.11 retrieving revision 1.12 diff -Lmacros/parserParametricLine.pl -Lmacros/parserParametricLine.pl -u -r1.11 -r1.12 --- macros/parserParametricLine.pl +++ macros/parserParametricLine.pl @@ -49,6 +49,7 @@ sub Init { my $context = $main::context{ParametricLine} = Parser::Context->getCopy("Vector"); + $context->{name} = "ParametricLine"; $context->variables->are(t=>'Real'); $context->{precedence}{ParametricLine} = $context->{precedence}{special}; $context->reduction->set('(-x)-y'=>0); Index: contextLimitedPoint.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPoint.pl,v retrieving revision 1.9 retrieving revision 1.10 diff -Lmacros/contextLimitedPoint.pl -Lmacros/contextLimitedPoint.pl -u -r1.9 -r1.10 --- macros/contextLimitedPoint.pl +++ macros/contextLimitedPoint.pl @@ -116,6 +116,7 @@ # my $context = $main::context{LimitedPoint} = Parser::Context->getCopy("Point"); + $context->{name} = "LimitedPoint"; $context->operators->set( '+' => {class => 'LimitedPoint::BOP::add'}, '-' => {class => 'LimitedPoint::BOP::subtract'}, Index: contextString.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextString.pl,v retrieving revision 1.10 retrieving revision 1.11 diff -Lmacros/contextString.pl -Lmacros/contextString.pl -u -r1.10 -r1.11 --- macros/contextString.pl +++ macros/contextString.pl @@ -62,6 +62,7 @@ sub Init { my $context = $main::context{String} = Parser::Context->getCopy("Numeric"); + $context->{name} = "String"; $context->parens->clear(); $context->variables->clear(); $context->constants->clear(); Index: contextLimitedNumeric.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedNumeric.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/contextLimitedNumeric.pl -Lmacros/contextLimitedNumeric.pl -u -r1.6 -r1.7 --- macros/contextLimitedNumeric.pl +++ macros/contextLimitedNumeric.pl @@ -25,6 +25,7 @@ sub _contextLimitedNumeric_init { my $context = $main::context{"LimitedNumeric-List"} = Parser::Context->getCopy("LimitedNumeric"); + $context->{name} = "LimitedNumeric-List"; $context->operators->redefine(','); main::Context("LimitedNumeric"); ### FIXME: probably should require the author to set this explicitly Index: parserImplicitPlane.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitPlane.pl,v retrieving revision 1.15 retrieving revision 1.16 diff -Lmacros/parserImplicitPlane.pl -Lmacros/parserImplicitPlane.pl -u -r1.15 -r1.16 --- macros/parserImplicitPlane.pl +++ macros/parserImplicitPlane.pl @@ -59,6 +59,7 @@ sub Init { my $context = $main::context{ImplicitPlane} = Parser::Context->getCopy("Vector"); + $context->{name} = "ImplicitPlane"; $context->{precedence}{ImplicitPlane} = $context->{precedence}{special}; #$context->{value}{Equality} = "ImplicitPlane::equality"; Parser::BOP::equality->Allow($context); Index: contextLimitedVector.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedVector.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/contextLimitedVector.pl -Lmacros/contextLimitedVector.pl -u -r1.8 -r1.9 --- macros/contextLimitedVector.pl +++ macros/contextLimitedVector.pl @@ -213,6 +213,7 @@ # my $context = $main::context{LimitedVector} = Parser::Context->getCopy("Vector"); + $context->{name} = "LimitedVector"; $context->operators->set( '+' => {class => 'LimitedVector::BOP::add'}, '-' => {class => 'LimitedVector::BOP::subtract'}, Index: contextTF.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextTF.pl,v retrieving revision 1.10 retrieving revision 1.11 diff -Lmacros/contextTF.pl -Lmacros/contextTF.pl -u -r1.10 -r1.11 --- macros/contextTF.pl +++ macros/contextTF.pl @@ -25,6 +25,7 @@ sub _contextTF_init { my $context = $main::context{TF} = Parser::Context->getCopy("String"); + $context->{name} = "TF"; $context->strings->are( "T" => {value => 1}, "F" => {value => 0}, Index: contextIntegerFunctions.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextIntegerFunctions.pl,v retrieving revision 1.7 retrieving revision 1.8 diff -Lmacros/contextIntegerFunctions.pl -Lmacros/contextIntegerFunctions.pl -u -r1.7 -r1.8 --- macros/contextIntegerFunctions.pl +++ macros/contextIntegerFunctions.pl @@ -45,6 +45,7 @@ sub Init { my $context = $main::context{IntegerFunctions} = Parser::Context->getCopy("Numeric"); + $context->{name} = "IntegerFunctions"; $context->functions->add( C => {class => 'context::IntegerFunction2'}, Index: contextLimitedPolynomial.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPolynomial.pl,v retrieving revision 1.11 retrieving revision 1.12 diff -Lmacros/contextLimitedPolynomial.pl -Lmacros/contextLimitedPolynomial.pl -u -r1.11 -r1.12 --- macros/contextLimitedPolynomial.pl +++ macros/contextLimitedPolynomial.pl @@ -322,6 +322,7 @@ # my $context = $main::context{LimitedPolynomial} = Parser::Context->getCopy("Numeric"); + $context->{name} = "LimitedPolynomial"; $context->operators->set( '+' => {class => 'LimitedPolynomial::BOP::add'}, '-' => {class => 'LimitedPolynomial::BOP::subtract'}, Index: contextLimitedComplex.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedComplex.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/contextLimitedComplex.pl -Lmacros/contextLimitedComplex.pl -u -r1.8 -r1.9 --- macros/contextLimitedComplex.pl +++ macros/contextLimitedComplex.pl @@ -230,6 +230,7 @@ # my $context = $main::context{LimitedComplex} = Parser::Context->getCopy("Complex"); + $context->{name} = "LimitedComplex"; $context->operators->set( '+' => {class => 'LimitedComplex::BOP::add'}, '-' => {class => 'LimitedComplex::BOP::subtract'}, Index: contextInequalities.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextInequalities.pl,v retrieving revision 1.7 retrieving revision 1.8 diff -Lmacros/contextInequalities.pl -Lmacros/contextInequalities.pl -u -r1.7 -r1.8 --- macros/contextInequalities.pl +++ macros/contextInequalities.pl @@ -65,6 +65,7 @@ # sub Init { my $context = $main::context{Inequalities} = Parser::Context->getCopy("Interval"); + $context->{name} = "Inequalities"; $context->operators->add( '<' => {precedence => .5, associativity => 'left', type => 'bin', string => ' < ', class => 'Inequalities::BOP::inequality', eval => 'evalLessThan', combine => 1}, Index: contextScientificNotation.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextScientificNotation.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/contextScientificNotation.pl -Lmacros/contextScientificNotation.pl -u -r1.6 -r1.7 --- macros/contextScientificNotation.pl +++ macros/contextScientificNotation.pl @@ -103,6 +103,7 @@ # Create the Scientific Notation context # my $context = $main::context{ScientificNotation} = Parser::Context->getCopy("Numeric"); + $context->{name} = "ScientificNotation"; # # Make numbers include the leading + or - and not allow E notation Index: contextCurrency.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextCurrency.pl,v retrieving revision 1.5 retrieving revision 1.6 diff -Lmacros/contextCurrency.pl -Lmacros/contextCurrency.pl -u -r1.5 -r1.6 --- macros/contextCurrency.pl +++ macros/contextCurrency.pl @@ -132,6 +132,8 @@ # sub Init { my $context = $main::context{Currency} = new Currency::Context(); + $context->{name} = "Currency"; + main::PG_restricted_eval('sub Currency {Value->Package("Currency")->new(@_)}'); } |
From: dpvc v. a. <we...@ma...> - 2007-08-28 22:45:04
|
Log Message: ----------- Add error messages when student's answer is not linear in the constant he or she has used. Modified Files: -------------- pg/macros: parserFormulaUpToConstant.pl Revision Data ------------- Index: parserFormulaUpToConstant.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserFormulaUpToConstant.pl,v retrieving revision 1.3 retrieving revision 1.4 diff -Lmacros/parserFormulaUpToConstant.pl -Lmacros/parserFormulaUpToConstant.pl -u -r1.3 -r1.4 --- macros/parserFormulaUpToConstant.pl +++ macros/parserFormulaUpToConstant.pl @@ -71,6 +71,14 @@ # # ANS($f->cmp(showHints => 0)); # + # One of the hints is about whether the student's answer is linear + # in the arbitrary constant. This test requires differentiating + # the student answer. Since there are times when that could be + # problematic, you can disable that test via the showLinearityHints + # flag. (Note: setting showHints to 0 also disables these hints.) + # + # ANS($f->cmp(showLinearityHints => 0)); + # ###################################################################### =cut @@ -157,10 +165,18 @@ return abs($context->variables->get("n0")->{value}) < $context->flag("zeroLevelTol"); } +################################################## +# +# Here we override part of the answer comparison +# routines in order to be able to generate +# helpful error messages for students when +# they leave off the + C. +# + # # Show hints by default # -sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1)}; +sub cmp_defaults {((shift)->SUPER::cmp_defaults,showHints => 1, showLinearityHints => 1)}; # # Add useful messages, if the author requested them @@ -170,12 +186,16 @@ $self->SUPER::cmp_postprocess($ans); return unless $ans->{score} == 0 && !$ans->{isPreview}; return if $ans->{ans_message} || !$self->getFlag("showHints"); - my $result = $ans->{correct_value} <=> $ans->{student_value}; # compare encodes the reason in the result + my $student = $ans->{student_value}; + my $result = $ans->{correct_value} <=> $student; # compare encodes the reason in the result $self->cmp_Error($ans,"Note: there is always more than one posibility") if $result == 2 || $result == 3; $self->cmp_Error($ans,"Your answer is not the most general solution") - if $result == 1 || ($result == 3 && $self->removeConstant == $ans->{student_value}); + if $result == 1 || ($result == 3 && $self->removeConstant == $student); + $self->cmp_Error($ans,"Your formula should be linear in the constant '$student->{constant}'") + if $result == -1 && $self->getFlag("showLinearityHints") && !$student->D($student->{constant})->isConstant; } +################################################## # # Get the name of the constant # @@ -239,5 +259,4 @@ $self->SUPER::new($equation,$name,$ref); } - 1; |
From: dpvc v. a. <we...@ma...> - 2007-08-28 21:59:47
|
Log Message: ----------- Updated contexts to include a "name" field that at least tracks what context you started with (though it can be modified and no longer be the same as the original context). Remove the individual named variables in the Default.pm file; they are now available only through the %Parser::Context::Default::context hash. Remove the >< and . operators, the <...> parentheses, the norm and unit functions, and the i, j, and k constants from the Point context. So the Point context no longer includes vectors and vector operaterations. Modified Files: -------------- pg/lib/Value: Context.pm pg/lib/Parser: Context.pm pg/lib/Parser/Context: Default.pm Functions.pm pg/lib/Parser/Legacy: LimitedComplex.pm LimitedNumeric.pm Numeric.pm pg/macros: answerVariableList.pl contextLimitedPoint.pl Revision Data ------------- Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Context.pm,v retrieving revision 1.19 retrieving revision 1.20 diff -Llib/Value/Context.pm -Llib/Value/Context.pm -u -r1.19 -r1.20 --- lib/Value/Context.pm +++ lib/Value/Context.pm @@ -88,6 +88,7 @@ $context->{error}{msg} = {%{$self->{error}{msg}}}; $context->{error}{convert} = $self->{error}{convert} if defined $self->{error}{convert}; + $context->{name} = $self->{name}; $context->{_initialized} = 1; return $context; } Index: Context.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context.pm,v retrieving revision 1.22 retrieving revision 1.23 diff -Llib/Parser/Context.pm -Llib/Parser/Context.pm -u -r1.22 -r1.23 --- lib/Parser/Context.pm +++ lib/Parser/Context.pm @@ -156,7 +156,7 @@ $contextTable->{current} = $context; $Value::context = \$contextTable->{current}; } elsif (!defined($contextTable->{current})) { - $contextTable->{current} = $Parser::Context::Default::numericContext->copy; + $contextTable->{current} = $Parser::Context::Default::context{Numeric}->copy; $Value::context = \$contextTable->{current}; } return $contextTable->{current}; @@ -174,7 +174,9 @@ my $name = shift; my $context = $contextTable->{$name}; $context = $Parser::Context::Default::context{$name} unless $context; return unless $context; - return $context->copy; + $context = $context->copy; + $context->{name} = $name; + return $context; } # Index: Functions.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Functions.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/Parser/Context/Functions.pm -Llib/Parser/Context/Functions.pm -u -r1.10 -r1.11 --- lib/Parser/Context/Functions.pm +++ lib/Parser/Context/Functions.pm @@ -81,7 +81,7 @@ $list = $Category{$name}; $list = [$name] if !$list && $context->{functions}{$name}; unless (defined($list)) {warn "Undefined function or category '$name'"; next} - if ($list->[0] eq '_alias_') + if ($list->[0] eq '_alias_') {unshift @names, @{$list}[1..scalar(@{$list})-1]; next} $context->functions->undefine(@{$list}); } @@ -90,16 +90,16 @@ sub enable {Enable(@_)} sub Enable { my $context = Parser::Context->current; - my $functions = $Parser::Context::Default::fullContext->{functions}; + my $functions = $Parser::Context::Default::context{Full}->{functions}; if (ref($_[0]) ne "") {$context = (shift)->{context}} my @names = @_; my ($list,$name); while ($name = shift(@names)) { $list = $Category{$name}; $list = [$name] if !$list && $context->{functions}{$name}; unless (defined($list)) {warn "Undefined function or category '$name'"; next} - if ($list->[0] eq '_alias_') + if ($list->[0] eq '_alias_') {unshift @names, @{$list}[1..scalar(@{$list})-1]; next} - my @fn; foreach my $f (@{$list}) + my @fn; foreach my $f (@{$list}) {push @fn, $f => {class => $functions->{$f}{class}}} $context->functions->set(@fn); } Index: Default.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Context/Default.pm,v retrieving revision 1.40 retrieving revision 1.41 diff -Llib/Parser/Context/Default.pm -Llib/Parser/Context/Default.pm -u -r1.40 -r1.41 --- lib/Parser/Context/Default.pm +++ lib/Parser/Context/Default.pm @@ -242,13 +242,12 @@ # use vars qw(%context); -use vars qw($fullContext $numericContext $complexContext $pointContext - $vectorContext $vector2DContext $matrixContext $intervalContext); +my $context; # # The default Context # -$fullContext = new Parser::Context( +$context = $context{Full} = new Parser::Context( operators => $operators, functions => $functions, constants => $constants, @@ -260,116 +259,111 @@ reduction => $Parser::reduce, ); -$fullContext->constants->set( +$context->constants->set( pi => {TeX => '\pi ', perl => 'pi'}, i => {isConstant => 1, perl => 'i'}, j => {TeX => '\boldsymbol{j}', perl => 'j'}, k => {TeX => '\boldsymbol{k}', perl => 'k'}, ); -$fullContext->usePrecedence('Standard'); +$context->usePrecedence('Standard'); +$context->{name} = "Full"; # # Numeric context (no vectors, matrices or complex numbers) # -$numericContext = $fullContext->copy; -$numericContext->variables->are(x=>'Real'); -$numericContext->operators->undefine('><','.'); -$numericContext->functions->undefine('norm','unit','arg','mod','Re','Im','conj'); -$numericContext->constants->remove('i','j','k'); -$numericContext->parens->remove('<'); -$numericContext->parens->set( +$context = $context{Numeric} = $context{Full}->copy; +$context->variables->are(x=>'Real'); +$context->operators->undefine('><','.'); +$context->functions->undefine('norm','unit','arg','mod','Re','Im','conj'); +$context->constants->remove('i','j','k'); +$context->parens->remove('<'); +$context->parens->set( '(' => {type => 'List', formMatrix => 0}, '[' => {type => 'List', formMatrix => 0}, '{' => {type => 'List'}, ); +$context->{name} = "Numeric"; # # Complex context (no vectors or matrices) # -$complexContext = $fullContext->copy; -$complexContext->variables->are(z=>'Complex'); -$complexContext->operators->undefine('><','.'); -$complexContext->functions->undefine('norm','unit'); -$complexContext->constants->remove('j','k'); -$complexContext->parens->remove('<'); -$complexContext->parens->set( +$context = $context{Complex} = $context{Full}->copy; +$context->variables->are(z=>'Complex'); +$context->operators->undefine('><','.'); +$context->functions->undefine('norm','unit'); +$context->constants->remove('j','k'); +$context->parens->remove('<'); +$context->parens->set( '(' => {type => 'List', formMatrix => 0}, '[' => {type => 'List', formMatrix => 0}, '{' => {type => 'List'}, ); -$complexContext->operators->set( +$context->operators->set( '^' => {class => 'Parser::Function::complex_power', negativeIsComplex => 1}, '**' => {class => 'Parser::Function::complex_power', negativeIsComplex => 1}, ); -$complexContext->functions->set( +$context->functions->set( 'sqrt' => {class => 'Parser::Function::complex_numeric', negativeIsComplex => 1}, 'log' => {class => 'Parser::Function::complex_numeric', negativeIsComplex => 1}, ); +$context->{name} = "Complex"; # # Vector context (no complex numbers) # -$vectorContext = $fullContext->copy; -$vectorContext->variables->are(x=>'Real',y=>'Real',z=>'Real'); -$vectorContext->functions->undefine('arg','mod','Re','Im','conj'); -$vectorContext->constants->replace(i=>Value::Vector->new(1,0,0)); -$vectorContext->constants->set(i=>{TeX=>'\boldsymbol{i}', perl=>'i'}); -$vectorContext->parens->set('(' => {formMatrix => 0}); +$context = $context{Vector} = $context{Full}->copy; +$context->variables->are(x=>'Real',y=>'Real',z=>'Real'); +$context->functions->undefine('arg','mod','Re','Im','conj'); +$context->constants->replace(i=>Value::Vector->new(1,0,0)); +$context->constants->set(i=>{TeX=>'\boldsymbol{i}', perl=>'i'}); +$context->parens->set('(' => {formMatrix => 0}); -$vector2DContext = $vectorContext->copy; -$vector2DContext->constants->replace( +$context = $context{Vector2D} = $context{Vector}->copy; +$context->constants->replace( i => Value::Vector->new(1,0), j => Value::Vector->new(0,1), ); -$vector2DContext->constants->set(i => {TeX=>'\boldsymbol{i}', perl=>'i'}); -$vector2DContext->constants->remove("k"); +$context->constants->set(i => {TeX=>'\boldsymbol{i}', perl=>'i'}); +$context->constants->remove("k"); +$context->{name} = "Vector2D"; # # Point context (for symmetry) # -$pointContext = $vectorContext->copy; +$context = $context{Point} = $context{Vector}->copy; +$context->operators->undefine("><","."); +$context->functions->undefine('norm','unit'); +$context->constants->remove('i','j','k'); +$context->parens->remove("<"); +$context->{name} = "Point"; # # Matrix context (square brackets make matrices in preference to points or intervals) # -$matrixContext = $vectorContext->copy; -$matrixContext->parens->set( +$context = $context{Matrix} = $context{Vector}->copy; +$context->parens->set( '(' => {formMatrix => 1}, '[' => {type => 'Matrix', removable => 0}, ); +$context->{name} = "Vector"; # # Interval context (make intervals rather than lists) # -$intervalContext = $numericContext->copy; -$intervalContext->parens->set( +$context = $context{Interval} = $context{Numeric}->copy; +$context->parens->set( '(' => {type => 'Interval'}, '[' => {type => 'Interval'}, '{' => {type => 'Set', removable => 0, emptyOK => 1}, ); my $infinity = Value::Infinity->new(); -$intervalContext->constants->add( +$context->constants->add( R => Value::Interval->new('(',-$infinity,$infinity,')'), ); -$intervalContext->constants->set(R => {TeX => '{\bf R}'}); - -######################################################################### - -# -# list of all default contexts (users can add more) -# -%context = ( - Full => $fullContext, - Numeric => $numericContext, - Complex => $complexContext, - Point => $pointContext, - Vector => $vectorContext, - Vector2D => $vector2DContext, - Matrix => $matrixContext, - Interval => $intervalContext, -); +$context->constants->set(R => {TeX => '{\bf R}'}); +$context->{name} = "Interval"; ######################################################################### Index: LimitedNumeric.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/LimitedNumeric.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -Llib/Parser/Legacy/LimitedNumeric.pm -Llib/Parser/Legacy/LimitedNumeric.pm -u -r1.3 -r1.4 --- lib/Parser/Legacy/LimitedNumeric.pm +++ lib/Parser/Legacy/LimitedNumeric.pm @@ -16,8 +16,10 @@ # # Context("LimitedNumeric-StrictFraction"); # +########################################################## +################################################## # # Minus can only appear in front of a number # @@ -35,6 +37,7 @@ sub class {'MINUS'}; +################################################## # # Divides can only appear between numbers or a negative # number and a number @@ -53,6 +56,7 @@ sub class {'DIVIDE'}; +################################################## # # Distinguish integers from decimals # @@ -66,6 +70,8 @@ } +################################################## + package Parser::Legacy::LimitedNumeric; # @@ -74,6 +80,8 @@ # my $context = $Parser::Context::Default::context{Numeric}->copy; $Parser::Context::Default::context{'LimitedNumeric'} = $context; +$context->{name} = 'LimitedNumeric'; + $context->operators->set('u-' => {class => 'Parser::Legacy::LimitedNumeric::UOP::minus'}); $context->operators->undefine( '+', '-', '*', '* ', ' *', ' ', '/', '/ ', ' /', '^', '**', @@ -82,12 +90,15 @@ $context->parens->undefine('|','{','(','['); $context->functions->disable('All'); +################################################## # # For the Fraction versions, allow the modified division, and # make sure numbers used in fractions are just integers # $context = $Parser::Context::Default::context{Numeric}->copy; $Parser::Context::Default::context{'LimitedNumeric-Fraction'} = $context; +$context->{name} = "LimitedNumeric-Fraction"; + $context->operators->set( 'u-' => {class => 'Parser::Legacy::LimitedNumeric::UOP::minus'}, '/' => {class => 'Parser::Legacy::LimitedNumeric::BOP::divide'}, @@ -102,11 +113,15 @@ $context->functions->disable('All'); $context->{parser}{Number} = "Parser::Legacy::LimitedNumeric::Number"; +################################################## # # For strict fractions, don't allow decimal numbers # $context = $context->copy; $Parser::Context::Default::context{'LimitedNumeric-StrictFraction'} = $context; Parser::Number::NoDecimals($context); +$context->{name} = "LimitedNumeric-StrictFractions"; + +###################################################################### 1; Index: LimitedComplex.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/LimitedComplex.pm,v retrieving revision 1.2 retrieving revision 1.3 diff -Llib/Parser/Legacy/LimitedComplex.pm -Llib/Parser/Legacy/LimitedComplex.pm -u -r1.2 -r1.3 --- lib/Parser/Legacy/LimitedComplex.pm +++ lib/Parser/Legacy/LimitedComplex.pm @@ -220,6 +220,11 @@ my $context = $Parser::Context::Default::context{Complex}->copy; $Parser::Context::Default::context{LimitedComplex} = $context; +$context->{name} = "LimtedComplex"; + +# +# Override operator classes +# $context->operators->set( '+' => {class => 'Parser::Legacy::LimitedComplex::BOP::add'}, '-' => {class => 'Parser::Legacy::LimitedComplex::BOP::subtract'}, @@ -249,27 +254,44 @@ # $context->flags->set(complex_format => 'either'); +################################################## + $context = $context->copy; $Parser::Context::Default::context{'LimitedComplex-cartesian'} = $context; $context->flags->set(complex_format => 'cartesian'); +$context->{name} = "LimtedComplex-cartesian"; + +################################################## $context = $context->copy; $Parser::Context::Default::context{'LimitedComplex-cartesian-strict'} = $context; $context->flags->set(strict_numeric => 1); $context->functions->disable('All'); +$context->{name} = "LimtedComplex-cartesian-strinct"; + +################################################## $context = $Parser::Context::Default::context{'LimitedComplex'}->copy; $Parser::Context::Default::context{'LimitedComplex-polar'} = $context; $context->flags->set(complex_format => 'polar'); +$context->{name} = "LimtedComplex-polar"; + +################################################## $context = $context->copy; $Parser::Context::Default::context{'LimitedComplex-polar-strict'} = $context; $context->flags->set(strict_numeric => 1); $context->functions->disable('All'); +$context->{name} = "LimtedComplex-polar-strict"; + +################################################## $context = $Parser::Context::Default::context{'LimitedComplex'}->copy; $Parser::Context::Default::context{'LimitedComplex-strict'} = $context; $context->flags->set(strict_numeric => 1); $context->functions->disable('All'); +$context->{name} = "LimtedComplex-strict"; + +################################################## 1; Index: Numeric.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Parser/Legacy/Numeric.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -Llib/Parser/Legacy/Numeric.pm -Llib/Parser/Legacy/Numeric.pm -u -r1.3 -r1.4 --- lib/Parser/Legacy/Numeric.pm +++ lib/Parser/Legacy/Numeric.pm @@ -25,3 +25,6 @@ step => {class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_step'}, fact => {class => 'Parser::Legacy::Numeric', perl => 'Parser::Legacy::Numeric::do_fact'}, ); +$context->{name} = "LegacyNumeric"; + +1; Index: contextLimitedPoint.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/contextLimitedPoint.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/contextLimitedPoint.pl -Lmacros/contextLimitedPoint.pl -u -r1.8 -r1.9 --- macros/contextLimitedPoint.pl +++ macros/contextLimitedPoint.pl @@ -141,7 +141,6 @@ '(' => {formMatrix => 0}, '[' => {formMatrix => 0}, ); - $context->parens->remove('<'); $context->variables->are(x=>'Real'); $context->constants->remove('i','j','k'); Index: answerVariableList.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/answerVariableList.pl,v retrieving revision 1.8 retrieving revision 1.9 diff -Lmacros/answerVariableList.pl -Lmacros/answerVariableList.pl -u -r1.8 -r1.9 --- macros/answerVariableList.pl +++ macros/answerVariableList.pl @@ -36,7 +36,7 @@ # A new context for variable lists # $main::context{VariableList} = Parser::Context->new( - operators => {',' => $Parser::Context::Default::fullContext->operators->get(',')}, + operators => {',' => $Parser::Context::Default::context{Full}->operators->get(',')}, lists => {'List' => {class =>'Parser::List::List'}}, parens => { '(' => {close => ')', type => 'List', formList => 1}, |
From: Mike G. v. a. <we...@ma...> - 2007-08-28 18:29:17
|
Log Message: ----------- Added blank line to top of file to allow POD documentation to work properly. Tags: ---- rel-2-4-dev Modified Files: -------------- pg/macros: PGstandard.pl Revision Data ------------- Index: PGstandard.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGstandard.pl,v retrieving revision 1.1.2.1 retrieving revision 1.1.2.2 diff -Lmacros/PGstandard.pl -Lmacros/PGstandard.pl -u -r1.1.2.1 -r1.1.2.2 --- macros/PGstandard.pl +++ macros/PGstandard.pl @@ -1,3 +1,4 @@ + =head1 NAME PGstandard.pl - Load standard PG macro packages. |
From: Mike G. v. a. <we...@ma...> - 2007-08-28 18:28:11
|
Log Message: ----------- Added line at top of page to allow POD documentation to work. Tags: ---- rel-2-4-dev Modified Files: -------------- pg/macros: MathObjects.pl Revision Data ------------- Index: MathObjects.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/MathObjects.pl,v retrieving revision 1.2.2.2 retrieving revision 1.2.2.3 diff -Lmacros/MathObjects.pl -Lmacros/MathObjects.pl -u -r1.2.2.2 -r1.2.2.3 --- macros/MathObjects.pl +++ macros/MathObjects.pl @@ -1,3 +1,5 @@ + + =head1 MathObjects.pl =pod |
From: dpvc v. a. <we...@ma...> - 2007-08-28 01:36:26
|
Log Message: ----------- Had the wrong inequality (but since answers won't usually contain newlines, this won't have affected anyone). Modified Files: -------------- pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.108 retrieving revision 1.109 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.108 -r1.109 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -602,7 +602,7 @@ # sub preformat { my $string = protectHTML(shift); - $string =~ s!\n!<br />!g if eval('$main::displayMode') eq 'TeX'; + $string =~ s!\n!<br />!g unless eval('$main::displayMode') eq 'TeX'; $string; } |
From: Gavin L. v. a. <we...@ma...> - 2007-08-27 21:11:44
|
Log Message: ----------- Make default setting for view_proctored_tests be 'student', so that students see the link for proctored tests on the set list page. Modified Files: -------------- webwork2/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.200 retrieving revision 1.201 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.200 -r1.201 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -623,7 +623,7 @@ proctor_quiz_login => "login_proctor", proctor_quiz_grade => "grade_proctor", - view_proctored_tests => "ta", + view_proctored_tests => "student", view_hidden_work => "ta", view_multiple_sets => "ta", |