let w,h,ratio,el,g,my={};function leastsquaresMain(){let version='0.6';let canvasid="canvas0"
my.dragQ=false;my.dragScreenQ=false;w=540;h=360;my.opts={title:'Title',xtitl:'X Values',ytitl:'Y Values',raws:'(1.21,1.69), (3.00,5.89), (5.16,4.11), (8.31,5.49), (10.21,8.65)'}
my.graphWd=w;my.graphHt=h;let s='';s+='
';var inps=[{lbl:'X Label',id:'xtitl',ht:20,type:0},{lbl:'Y Label',id:'ytitl',ht:20,type:0},];s+='
';}
s+='
';s+='';s+='';s+='
';s+='';s+='
';s+='
';s+='';s+='';s+='
';s+='
';s+='';s+='Zoom:';s+='';s+='';s+=' '
s+='';s+='';s+='
';s+='';s+='
';s+='
';s+='';s+='';s+='
';s+='
';s+='© 2020 MathsIsFun.com v'+version+'
';s+='';document.write(s);el=document.getElementById(canvasid);ratio=2;el.width=w*ratio;el.height=h*ratio;el.style.width=w+"px";el.style.height=h+"px";g=el.getContext("2d");g.setTransform(ratio,0,0,ratio,0,0);my.currZoom=1;my.sttRect=[-1,-1,12,10];my.coords=new Coords(0,0,w,h,my.sttRect[0],my.sttRect[1],my.sttRect[2],my.sttRect[3],true);this.graph=new Graph(g,my.coords);my.dragNo=0;my.coordsQ=false;my.guidesQ=false;my.shapes=[];my.pts=[];el.addEventListener("mousedown",mouseDownListener,false);el.addEventListener('touchstart',ontouchstart,false);el.addEventListener("mousemove",dopointer,false);document.getElementById('xtitl').value=optGet('xtitl')
document.getElementById('ytitl').value=optGet('ytitl')
document.getElementById('raws').value=optGet('raws')
decodeCSV(optGet('raws'))
go();shapesToEdit();}
function go(){optSet('xtitl',document.getElementById('xtitl').value)
optSet('ytitl',document.getElementById('ytitl').value)
optSet('raws',document.getElementById('raws').value)
console.log('go',my.opts)
g.clearRect(0,0,el.width,el.height);leastsquares();this.graph.drawGraph();drawPts();}
function leastsquares(){let sumx=0;let sumy=0
let sumx2=0;let sumxy=0;for(let i=0;i0)s+=', ';s+='(';s+=my.shapes[i].x
s+=',';s+=my.shapes[i].y
s+=')';}
return s;}
function decodeCSV(s){s=s.replace(/\s/g,"~");s=s.replace(/\)\,\(/g,"~");s=s.replace(/\)\(/g,"~");s=s.replace(/\)/g,"");s=s.replace(/\(/g,"");s=s.replace(/\,/g,"_");s=s.replace(/\*/g,"_");console.log("decodeCSV",s);decode(s);}
function decode(s){let a=s.split("~");my.shapes=[];for(let i=0;i=2){my.shapes.push(new Pt(Number(xy[0]),Number(xy[1])));}}
console.log("my.shapes",my.shapes);go();}
function fit(){my.coords.fitToPts(my.shapes,1.1);go();}
function toggleCoords(){my.coordsQ=!my.coordsQ;btn("coordsBtn",my.coordsQ);go();}
function toggleGuides(){my.guidesQ=!my.guidesQ;btn("guidesBtn",my.guidesQ);go();}
function btn(btn,onq){if(onq){document.getElementById(btn).classList.add("hi");document.getElementById(btn).classList.remove("lo");}else{document.getElementById(btn).classList.add("lo");document.getElementById(btn).classList.remove("hi");}}
function ontouchstart(evt){let touch=evt.targetTouches[0];evt.clientX=touch.clientX;evt.clientY=touch.clientY;evt.touchQ=true;mouseDownListener(evt);}
function ontouchmove(evt){let touch=evt.targetTouches[0];evt.clientX=touch.clientX;evt.clientY=touch.clientY;evt.touchQ=true;mouseMoveListener(evt);evt.preventDefault();}
function ontouchend(evt){el.addEventListener('touchstart',ontouchstart,false);window.removeEventListener("touchend",ontouchend,false);if(my.dragQ){my.dragQ=false;window.removeEventListener("touchmove",ontouchmove,false);}}
function dopointer(e){let bRect=el.getBoundingClientRect();let mouseX=(e.clientX-bRect.left)*(el.width/ratio/bRect.width);let mouseY=(e.clientY-bRect.top)*(el.height/ratio/bRect.height);let overQ=false;for(let i=0;ihighestIndex){my.dragHold={x:mouseX-my.pts[i].x,y:mouseY-my.pts[i].y}
highestIndex=i;my.dragNo=i;}}}
if(!my.dragQ){my.dragQ=true;my.dragScreenQ=true;}
if(evt.touchQ){window.addEventListener('touchmove',ontouchmove,false);}else{window.addEventListener("mousemove",mouseMoveListener,false);}
console.log("mouseDownListener",my.dragQ,my.dragScreenQ,highestIndex);go();if(evt.touchQ){el.removeEventListener("touchstart",ontouchstart,false);window.addEventListener("touchend",ontouchend,false);}else{el.removeEventListener("mousedown",mouseDownListener,false);window.addEventListener("mouseup",mouseUpListener,false);}
if(evt.preventDefault){evt.preventDefault();}
else if(evt.returnValue){evt.returnValue=false;}
return false;}
function mouseUpListener(evt){my.dragQ=false;el.addEventListener("mousedown",mouseDownListener,false);window.removeEventListener("mouseup",mouseUpListener,false);window.removeEventListener("mousemove",mouseMoveListener,false);}
function mouseMoveListener(evt){let posX;let posY;let shapeRad=my.shapes[my.dragNo].rad;let minX=shapeRad;let maxX=el.width-shapeRad;let minY=shapeRad;let maxY=el.height-shapeRad;let bRect=el.getBoundingClientRect();let mouseX=(evt.clientX-bRect.left)*(el.width/ratio/bRect.width);let mouseY=(evt.clientY-bRect.top)*(el.height/ratio/bRect.height);if(my.dragScreenQ){let moveX=my.prevX-mouseX;let moveY=mouseY-my.prevY;if(Math.abs(moveX)>1||Math.abs(moveY)>1){my.coords.drag(moveX,moveY);my.prevX=mouseX;my.prevY=mouseY;}}else{posX=mouseX-my.dragHold.x;posX=(posXmaxX)?maxX:posX);posY=mouseY-my.dragHold.y;posY=(posYmaxY)?maxY:posY);my.shapes[my.dragNo].x=fmt(my.coords.toXVal(posX),4)
my.shapes[my.dragNo].y=fmt(my.coords.toYVal(posY),4)}
go();shapesToEdit();}
function hitTest(shape,mx,my){let dx;let dy;dx=mx-shape.x;dy=my-shape.y;return(dx*dx+dy*dymaxNo){currNo=minNo+(currNo-minNo)%range;}
return currNo;}
function constrain(min,val,max){return(Math.min(Math.max(min,val),max));}
class Coords{constructor(left,top,width,height,xStt,yStt,xEnd,yEnd,uniScaleQ){this.left=left;this.top=top;this.width=width;this.height=height;this.xStt=xStt;this.yStt=yStt;this.xEnd=xEnd;this.yEnd=yEnd;this.uniScaleQ=uniScaleQ;this.xLogQ=false;this.yLogQ=false;this.xScale;let xLogScale;this.yScale;this.calcScale();}
calcScale(){if(this.xLogQ){if(this.xStt<=0)
this.xStt=1;if(this.xEnd<=0)
this.xEnd=1;}
if(this.yLogQ){if(this.yStt<=0)
this.yStt=1;if(this.yEnd<=0)
this.yEnd=1;}
let temp;if(this.xStt>this.xEnd){temp=this.xStt;this.xStt=this.xEnd;this.xEnd=temp;}
if(this.yStt>this.yEnd){temp=this.yStt;this.yStt=this.yEnd;this.yEnd=temp;}
let xSpan=this.xEnd-this.xStt;if(xSpan<=0)
xSpan=0.1;this.xScale=xSpan/this.width;this.xLogScale=(Math.log(this.xEnd)-Math.log(this.xStt))/this.width;let ySpan=this.yEnd-this.yStt;if(ySpan<=0)
ySpan=0.1;this.yScale=ySpan/this.height;this.yLogScale=(Math.log(this.yEnd)-Math.log(this.yStt))/this.height;if(this.uniScaleQ&&!this.xLogQ&&!this.yLogQ){let newScale=Math.max(this.xScale,this.yScale);this.xScale=newScale;xSpan=this.xScale*this.width;let xMid=(this.xStt+this.xEnd)/2;this.xStt=xMid-xSpan/2;this.xEnd=xMid+xSpan/2;this.yScale=newScale;ySpan=this.yScale*this.height;let yMid=(this.yStt+this.yEnd)/2;this.yStt=yMid-ySpan/2;this.yEnd=yMid+ySpan/2;}}
getXScale(){return this.xScale;}
getYScale(){return this.yScale;}
scale(factor,xMid,yMid){if(typeof xMid=='undefined')
xMid=(this.xStt+this.xEnd)/2;this.xStt=xMid-(xMid-this.xStt)*factor;this.xEnd=xMid+(this.xEnd-xMid)*factor;if(typeof yMid=='undefined')
yMid=(this.yStt+this.yEnd)/2;this.yStt=yMid-(yMid-this.yStt)*factor;this.yEnd=yMid+(this.yEnd-yMid)*factor;this.calcScale();}
drag(xPix,yPix){this.xStt+=xPix*this.xScale;this.xEnd+=xPix*this.xScale;this.yStt+=yPix*this.yScale;this.yEnd+=yPix*this.yScale;this.calcScale();}
newCenter(x,y){let xMid=this.xStt+x*this.xScale;let xhalfspan=(this.xEnd-this.xStt)/2;this.xStt=xMid-xhalfspan;this.xEnd=xMid+xhalfspan;let yMid=this.yEnd-y*this.yScale;let yhalfspan=(this.yEnd-this.yStt)/2;this.yStt=yMid-yhalfspan;this.yEnd=yMid+yhalfspan;this.calcScale();}
fitToPts(pts,borderFactor){for(let i=0;i",this.xEnd,this.yEnd);let xMid=(this.xStt+this.xEnd)/2;let xhalfspan=borderFactor*(this.xEnd-this.xStt)/2;this.xStt=xMid-xhalfspan;this.xEnd=xMid+xhalfspan;let yMid=(this.yStt+this.yEnd)/2;let yhalfspan=borderFactor*(this.yEnd-this.yStt)/2;this.yStt=yMid-yhalfspan;this.yEnd=yMid+yhalfspan;console.log("after factor",this.xStt,this.yStt,"-->",this.xEnd,this.yEnd);this.calcScale();}
toXPix(val,useCornerQ){if(this.xLogQ){return this.left+(Math.log(val)-Math.log(this.xStt))/this.xLogScale;}else{return this.left+((val-this.xStt)/this.xScale);}}
toYPix(val){if(this.yLogQ){return this.top+(Math.log(this.yEnd)-Math.log(val))/this.yLogScale;}else{return this.top+((this.yEnd-val)/this.yScale);}}
toPtVal(pt,useCornerQ){return new Pt(this.toXVal(pt.x,useCornerQ),this.toYVal(pt.y,useCornerQ));}
toXVal(pix,useCornerQ){if(useCornerQ){return this.xStt+(pix-this.left)*this.xScale;}else{return this.xStt+pix*this.xScale;}}
toYVal(pix,useCornerQ){if(useCornerQ){return this.yEnd-(pix-this.top)*this.yScale;}else{return this.yEnd-pix*this.yScale;}}
getTicks(stt,span,ratio){let ticks=[];let inter=this.tickInterval(span/ratio,false);let tickStt=Math.ceil(stt/inter)*inter;let i=0;let tick=0;do{tick=tickStt+i*inter;tick=Number(tick.toPrecision(8));ticks.push([tick,1]);i++;}while(tick=5){if(majorQ){return(5*pow10);}else{return(1*pow10);}}
if(mantissa>=3){if(majorQ){return(2*pow10);}else{return(0.2*pow10);}}
if(mantissa>=1.4){if(majorQ){return(0.5*pow10);}else{return(0.2*pow10);}}
if(mantissa>=0.8){if(majorQ){return(0.5*pow10);}else{return(0.1*pow10);}}
if(majorQ){return(0.2*pow10);}else{return(0.1*pow10);}}}
class Graph{constructor(g){this.g=g;this.xLinesQ=true;this.yLinesQ=true;this.xValsQ=true;this.yValsQ=true;this.vtNumsAlign='right';this.skewQ=false;}
drawGraph(){this.hzAxisY=my.coords.toYPix(0);if(this.hzAxisY<0)this.hzAxisY=10;if(this.hzAxisY>my.coords.height)this.hzAxisY=my.coords.height-17
this.hzNumsY=this.hzAxisY+14;if(this.hzAxisY>my.coords.height-17)this.hzNumsY=my.coords.height-3;this.vtAxisX=my.coords.toXPix(0);if(this.vtAxisX<0)this.vtAxisX=10;if(this.vtAxisX>my.coords.width)this.vtAxisX=my.coords.width;this.vtNumsX=this.vtAxisX-5;this.vtNumsAlign='right';this.vtLblX=this.vtAxisX-25
if(this.vtAxisX<=10){this.vtNumsAlign='left';this.vtNumsX=15;this.vtLblX=this.vtAxisX+45}
if(my.coords.xLogQ){this.drawLinesLogX();}else{if(this.xLinesQ){this.drawHzLines();}}
if(my.coords.yLogQ){this.drawLinesLogY();}else{if(this.yLinesQ){this.drawVtLines();}}
g.fillStyle="#0000ff";g.font="28px Arial";g.textAlign="center";g.fillText(my.formula,w/2,24);}
drawVtLines(){let g=this.g;g.lineWidth=1;let ticks=my.coords.getTicks(my.coords.xStt,my.coords.xEnd-my.coords.xStt,my.graphWd/100);for(let i=0;i-1);if(isChrome){win.focus();setTimeout(function(){win.print();},500);}else{win.focus();win.print();win.close();}}
function pageHTML(dataUrl){let s='Recent';s+='';s+='';s+='';s+='
';s+='';return s;}
function linearFormula(x1,y1,x2,y2){console.log("calculate",x1,y1,x2,y2);let deltax=x2-x1;let deltay=y2-y1;let pointStr="("+x1+","+y1+"), ("+x2+","+y2+")";let m=0
let b=0
let formula="";let parallel="";let perpendicular="";if(deltax==0){if(deltay==0){formula="Please enter two different points";}else{formula="x = "+x1+" [note: x, not y]";parallel="x = any number";perpendicular="y = any number";}}else{if(deltay==0){m=0;b=y1;formula="y = "+y1;parallel="y = any number";perpendicular="x = any number";}else{m=(y2-y1)/(x2-x1);b=y1-(m*x1);formula="y = "+linearPhrase([m,b]);parallel=linearPhrase([m,0])+" + any number";let newm=-1/m;perpendicular=linearPhrase([newm,0])+" + any number";}}
var s='';s+='Points: '+pointStr+'';s+='
Formula (y=mx+b): '+formula+'';s+='
Slope m: '+m+'';s+='
Y-intercept b: '+b+'';s+='
Parallel lines: '+parallel+'';s+='
Perpendicular lines: '+perpendicular+'';return formula}
function linearPhrase(a){var s="";for(var k=0;k0){s+=" − ";}else{s+=" −";}
v=-v;}else{if(s.length>0){s+=" + ";}}
switch(k){case 0:if(v!=1){s+=fmt(v,4);}
s+="x";break;case 1:s+=fmt(v,4)
break;default:if(v!=1){s+=fmt(v,4)}
s+="("+fmt(k,4)+")";break;}}}
if(s.length==0){s='0';}
return s;}