Skip to content
This repository has been archived by the owner on May 11, 2021. It is now read-only.

Commit

Permalink
Add exercise: Fundamental theorem of arithmetic
Browse files Browse the repository at this point in the history
Reviewers: emily

Reviewed By: emily

Differential Revision: http://phabricator.khanacademy.org/D82
  • Loading branch information
beneater committed May 25, 2012
1 parent 7374bcd commit 49acfef
Show file tree
Hide file tree
Showing 2 changed files with 329 additions and 0 deletions.
268 changes: 268 additions & 0 deletions exercises/the_fundamental_theorem_of_arithmetic.html
@@ -0,0 +1,268 @@
<!DOCTYPE html>
<html data-require="math math-format word-problems graphie interactive">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>The fundamental theorem of arithmetic</title>
<script src="../khan-exercise.js"></script>
</head>
<body>
<div class="exercise">
<div class="problems">
<div>
<div class="vars">
<var id="PRIMES">[2, 3, 5, 7, 11, 13]</var>
<var id="NUMBER">randFromArray([
4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26,
27, 28, 30, 32, 33, 35, 36, 39, 40, 42, 44, 45, 48, 49, 50,
52, 54, 55, 56, 60, 63, 65, 66, 70, 72, 75, 77, 78, 80, 81,
84, 88, 90, 91, 96, 98, 99])
</var>
<var id="FACTORIZATION">getPrimeFactorization(NUMBER)</var>
<var id="EXPONENTS">_.reduce(FACTORIZATION, function(exponents, base) {
exponents[base] = exponents[base] + 1 || 1;
return exponents;
}, {})
</var>
<var id="HAS_EXPONENTS">_.reduce(_.values(EXPONENTS), function(hasExponents, num) {
return hasExponents || num !== 1;
}, false)
</var>
<var id="EXPONENT_HINT">_.map(EXPONENTS, function(num, prime) {
return cardinal(num) + " &lt;code&gt;" + prime + "&lt;/code&gt;" + (num === 1 ? "" : "s");
})
</var>
<var id="SOLUTION">_.reduce(PRIMES, function(factors, prime) {
if (EXPONENTS[prime] &gt; 1) {
factors.push(prime + "^" + EXPONENTS[prime]);
} else if (EXPONENTS[prime] === 1) {
factors.push(prime);
}
return factors;
}, []).join("\\cdot")
</var>
<var id="REMAINING">_.map(FACTORIZATION, function(factor, step){
var remain = NUMBER;
_.each(FACTORIZATION.slice(0, step), function(n) { remain /= n; })
return remain;
})
</var>
<var id="GRAPHIE">null</var>
</div>

<p class="question">
Find the prime factorization of <code><var>NUMBER</var></code>.
</p>

<div class="problem">
<p>
Use the arrows to change the exponent on each prime number below to see if you can find the
prime factorization.
</p>
<div class="graphie">
// Save a reference to KhanUtil.currentGraph.graph to workaround the issue
// of the custom validator depending on KhanUtil.currentGraph and
// KhanUtil.currentGraph changing because of the second graphie element
// in the hints.
GRAPHIE = graph;

init({
range: [[0, 10], [-11, 0]]
});
addMouseLayer();

graph.primes = [];

graph.computeTotal = function() {
return _.reduce(PRIMES, function(total, prime) {
return total * pow(prime, graph.primes[prime].exponent);
}, 1);
};

graph.totalLabel = label([7, -10], 1, "right", {fontSize: 25});
graph.updateTotal = function() {
this.totalLabel.remove();
this.totalLabel = label([7, -10], graph.computeTotal(), "right", {fontSize: 25});

var answerPreview = _.reduce(PRIMES, function(answer, prime) {
if (graph.primes[prime].exponent &gt; 1) {
answer.push(prime + "^" + graph.primes[prime].exponent);
} else if (graph.primes[prime].exponent === 1) {
answer.push(prime);
}
return answer;
}, []).join("\\cdot");
answerPreview += " = " + graph.computeTotal();

$("#answer-preview code").text(answerPreview);
MathJax.Hub.Queue(["Reprocess", MathJax.Hub, $("#answer-preview")[0]]);
};

_.each(PRIMES, function(prime, y) {
var yCoord = -y * 1.5 - 1;
graph.primes[prime] = {
exponent: 0,
baseLabel: label([2, yCoord], prime, "left", {fontSize: 25, color: "#aaa"}),
expLabel: label([2, yCoord + 0.3], 0, "center", {fontSize: 20, color: "#aaa"}),
expandLabel: label([3, yCoord], 1, "right", {fontSize: 20, color: "#aaa"}),
factorLabel: label([7, yCoord], 1, "right", {fontSize: 25, color: "#aaa"}),
upArrow: addArrowWidget({
coord: [2, yCoord + 0.7],
direction: "up"
}),
downArrow: addArrowWidget({
coord: [2, yCoord - 0.1],
direction: "down"
}),
update: function() {
this.expLabel.remove();
this.expandLabel.remove();
this.factorLabel.remove();

this.expLabel = label([2, yCoord + 0.3], this.exponent, "center", {fontSize: 20});
this.factorLabel = label([7, yCoord], pow(prime, this.exponent),
"right", {fontSize: 25});
if (this.exponent === 0) {
this.baseLabel.css({color: "#aaa"});
this.expLabel.css({color: "#aaa"});
this.expandLabel = label([3, yCoord], "1", "right", {fontSize: 20, color: "#aaa"});
this.factorLabel.css({color: "#aaa"});
} else {
this.baseLabel.css({color: "black"});
this.expandLabel = label([3, yCoord],
getPrimeFactorization(pow(prime, this.exponent)).join("\\cdot"),
"right", {fontSize: 20});
}

if (this.exponent &gt;= 5 || (prime &gt;= 11 &amp;&amp; this.exponent &gt;= 3)) {
this.upArrow.hide();
} else {
this.upArrow.show();
}

if (this.exponent &lt;= 0) {
this.downArrow.hide();
} else {
this.downArrow.show();
}
}
};
graph.primes[prime].upArrow.onClick = function() {
graph.primes[prime].exponent += 1;
graph.primes[prime].update();
graph.updateTotal();
};
graph.primes[prime].downArrow.onClick = function() {
graph.primes[prime].exponent -= 1;
graph.primes[prime].update();
graph.updateTotal();
};

label([2.5, yCoord], "=", "right", {color: "#ccc"});
label([6.5, yCoord], "=", "right", {color: "#ccc"});
graph.primes[prime].downArrow.hide();
});

line([0, -9.25], [8, -9.25]);
label([0, -9], "\\times", "above right", {fontSize: 25});
</div>
</div>

<div class="solution" data-type="custom">
<div class="instruction">
Click the orange arrows to change your answer.
<p id="answer-preview"><code>=1</code></p>
</div>
<div class="guess">GRAPHIE.computeTotal()</div>
<div class="validator-function">
if (guess === 1) {
return "";
}
return guess === NUMBER;
</div>
<div class="show-guess">
_.each(PRIMES, function(prime) {
GRAPHIE.primes[prime].exponent = 0;
});
_.each(getPrimeFactorization(guess), function(prime) {
GRAPHIE.primes[prime].exponent += 1;
});
_.each(PRIMES, function(prime) {
GRAPHIE.primes[prime].update();
});
GRAPHIE.updateTotal();
</div>
<div class="show-guess-solutionarea">
_.each(PRIMES, function(prime) {
GRAPHIE.primes[prime].exponent = 0;
});
_.each(getPrimeFactorization(guess), function(prime) {
GRAPHIE.primes[prime].exponent += 1;
});
GRAPHIE.updateTotal();
</div>
</div>
</div>
</div>

<div class="hints">
<div>
<div class="graphie" id="factor-tree" style="float: left;">
graph.cx = 0;
graph.y = 0;
graph.curr = NUMBER;
init({
range: [[-1, FACTORIZATION.length + 1], [-2 * FACTORIZATION.length - 0.5, 1]],
scale: [30, 30]
});
label([graph.cx + 1, graph.y], graph.curr);
</div>
<p style="height: 60px">We can use a factor tree to break <code><var>NUMBER</var></code> into its prime
factorization. Which of the prime numbers divides into <code><var>NUMBER</var></code>?</p>
</div>
<div data-each="FACTORIZATION.slice(0, -1) as I, FACTOR">
<div class="graphie" data-update="factor-tree">
path([[graph.cx + 1, graph.y - 0.5], [graph.cx, graph.y - 1.5]]);
path([[graph.cx + 1, graph.y - 0.5], [graph.cx + 2, graph.y - 1.5]]);
graph.y -= 2;
graph.cx += 1;
graph.curr = graph.curr / FACTOR;
label( [graph.cx - 1, graph.y], FACTOR, {color: BLUE} );
circle( [graph.cx - 1, graph.y], 0.5);
graph.lastLabel = label( [graph.cx + 1, graph.y], graph.curr );
</div>
<p style="height: 38px">
<code><var>REMAINING[I]</var></code> is divisible by <code class="hint_blue"><var>FACTOR</var></code>,
leaving us with <code><var>REMAINING[I] / FACTOR</var></code>.
</p>
</div>
<div>
<div class="graphie" data-update="factor-tree">
circle( [graph.cx + 1, graph.y], 0.5);
graph.lastLabel.remove();
label( [graph.cx + 1, graph.y], graph.curr, {color: BLUE} );
</div>
<p>
<code class="hint_blue"><var>FACTORIZATION[FACTORIZATION.length-1]</var></code>
is prime, so we're done factoring.
</p>
</div>
<div>
<p>
The prime factorization of <code><var>NUMBER</var></code> is:
</p>
<p><code class="hint_blue">
\qquad<var>FACTORIZATION.join("\\space\\color{black}{\\cdot}\\space")</var>
</code></p>
<div data-if="HAS_EXPONENTS">
<p>
Because there <var>FACTORIZATION[0] === FACTORIZATION[1] ? "are" : "is"</var>
<var>toSentence(EXPONENT_HINT)</var>, we can use exponents to write the prime factorization as:
</p>
<p><code>\qquad\blue{<var>SOLUTION</var>} = <var>NUMBER</var></code></p>
</div>
</div>
</div>

</div>
</body>
</html>
61 changes: 61 additions & 0 deletions utils/interactive.js
Expand Up @@ -1123,6 +1123,67 @@ $.extend(KhanUtil, {
},


addArrowWidget: function(options) {
var arrowWidget = $.extend({
graph: KhanUtil.currentGraph,
direction: "up",
coord: [0, 0],
onClick: function() {}
}, options);
var graph = arrowWidget.graph;

if (arrowWidget.direction === "up") {
arrowWidget.visibleShape = graph.path([
[arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
[arrowWidget.coord[0] - 4 / graph.scale[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
[arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
[arrowWidget.coord[0] + 4 / graph.scale[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
[arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]]
], { stroke: null, fill: KhanUtil.ORANGE });
} else if (arrowWidget.direction === "down") {
arrowWidget.visibleShape = graph.path([
[arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
[arrowWidget.coord[0] - 4 / graph.scale[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
[arrowWidget.coord[0], arrowWidget.coord[1] - 4 / graph.scale[1]],
[arrowWidget.coord[0] + 4 / graph.scale[0], arrowWidget.coord[1] + 4 / graph.scale[1]],
[arrowWidget.coord[0], arrowWidget.coord[1] + 4 / graph.scale[1]]
], { stroke: null, fill: KhanUtil.ORANGE });
}

arrowWidget.mouseTarget = graph.mouselayer.circle(
graph.scalePoint(arrowWidget.coord)[0], graph.scalePoint(arrowWidget.coord)[1], 15);
arrowWidget.mouseTarget.attr({fill: "#000", "opacity": 0.0});

$(arrowWidget.mouseTarget[0]).css("cursor", "pointer");
$(arrowWidget.mouseTarget[0]).bind("vmousedown vmouseover vmouseout", function(event) {
if (event.type === "vmouseover") {
arrowWidget.visibleShape.animate({ scale: 2, fill: KhanUtil.ORANGE }, 20);
} else if (event.type === "vmouseout") {
arrowWidget.visibleShape.animate({ scale: 1, fill: KhanUtil.ORANGE }, 20);
} else if (event.type === "vmousedown" && (event.which === 1 || event.which === 0)) {
if (!arrowWidget.hidden) {
arrowWidget.onClick();
}
return false;
}
});

arrowWidget.hide = function() {
arrowWidget.visibleShape.hide();
arrowWidget.hidden = true;
$(arrowWidget.mouseTarget[0]).css("cursor", "default");
};

arrowWidget.show = function() {
arrowWidget.visibleShape.show();
arrowWidget.hidden = false;
$(arrowWidget.mouseTarget[0]).css("cursor", "pointer");
};

return arrowWidget;
},


createSorter: function() {
var sorter = {};
var list;
Expand Down

0 comments on commit 49acfef

Please sign in to comment.