├── .gitignore ├── README.md ├── demo.gif ├── index.html ├── package.json └── src ├── download-png.js ├── script.js └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | package-lock.json 3 | node_modules/ 4 | .cache/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # num2math 2 | Generate a complicated math expression that results in a number. If you wanna do this for some reason. 3 | 4 | Try it: https://enjeck.com/num2math/ 5 | 6 | ![](demo.gif) 7 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enjeck/num2math/216ac31b16cc2f97b79015b404a7ff6dc10b803e/demo.gif -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 16 | num2math - Complicated math equation generator 17 | 18 | 19 | 20 | 24 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 |
59 |
60 | x 61 |

Mobile Notice

62 |

63 | You appear to be on a device with a narrow screen width (i.e you are 64 | probably on a mobile device). Due to the nature of mathematics on this 65 | site, it best views in landscape mode. On narrow screens, some math 66 | expressions may run off the side of the screen (you should be able to 67 | scroll to see them) 68 |

69 |
70 |
71 |

num2math

72 |

73 | A math expression generator.
What is a complicated 74 | math equation that equals x? Let's find out! 75 |

76 |
77 | 78 |
79 | 82 |
83 | 91 |
92 |
93 |
94 | 95 | 96 |
97 |
98 | 99 |
100 |
101 | 102 | 103 |
104 | 105 |
106 |
107 | 108 | 109 |
110 |
111 | 112 | 113 |
114 |
115 | 116 | 117 |
118 |
119 | 120 | 121 |
122 |
123 | 124 | 125 |
126 |
127 | 128 | 129 |
130 |
131 | 132 |
133 | 134 |
135 |

Tips

136 | 146 |
147 |
148 | 149 | 150 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "homepage": "http://enjeck.github.io/num2math", 3 | "name": "num2math", 4 | "version": "1.0.0", 5 | "description": "A complication math expression generator. What is a really complicated math equation that equals xxx? Input a solution and get a math expression. Equations can be copied.", 6 | "main": "index.html", 7 | "scripts": { 8 | "start": "parcel index.html --open", 9 | "build": "parcel build index.html --public-url ./", 10 | "predeploy": "npm run build", 11 | "deploy": "gh-pages -d dist" 12 | }, 13 | "dependencies": { 14 | "jquery": "3.6.0", 15 | "parcel-bundler": "^1.6.1" 16 | }, 17 | "devDependencies": { 18 | "@babel/core": "7.2.0", 19 | "gh-pages": "^3.2.3" 20 | }, 21 | "resolutions": { 22 | "@babel/preset-env": "7.13.8" 23 | }, 24 | "keywords": [ 25 | "math", 26 | "latex", 27 | "math equations" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/download-png.js: -------------------------------------------------------------------------------- 1 | import jQuery from "jquery"; 2 | const $ = jQuery.noConflict(); 3 | 4 | $(document).ready(function () { 5 | let downloadBtn = document.getElementById("download-img"); 6 | 7 | downloadBtn.addEventListener("click", downloadPNG); 8 | // Initiate download of blob 9 | function download( 10 | filename, // string 11 | blob // Blob 12 | ) { 13 | if (window.navigator.msSaveOrOpenBlob) { 14 | window.navigator.msSaveBlob(blob, filename); 15 | } else { 16 | // Create and link and click on it to automatically download image 17 | const elem = window.document.createElement("a"); 18 | elem.href = window.URL.createObjectURL(blob); 19 | elem.download = filename; 20 | elem.click(); 21 | } 22 | } 23 | 24 | 25 | function downloadPNG() { 26 | // Get the svg from the page 27 | var svg = document.querySelector("svg"); 28 | 29 | // Get the current number 30 | var number = document.getElementById("input").value; 31 | 32 | // Increase the SVG's width and height to produce a bigger image 33 | let w = parseInt(svg.getAttribute("width"))*3; 34 | let h = parseInt(svg.getAttribute("height"))*3; 35 | 36 | // Clone the svg before changing width and height so that it does not affect the svg on the page 37 | svg = svg.cloneNode(true); 38 | svg.setAttribute("width", `${w}ex`) 39 | svg.setAttribute("height", `${h}ex`) 40 | 41 | // Convert SVG to string data 42 | var data = new XMLSerializer().serializeToString(svg); 43 | 44 | var canvas = document.createElement("canvas"); 45 | 46 | canvg(canvas, data, { 47 | renderCallback: function () { 48 | 49 | // Convert SVG data to PNG image 50 | canvas.toBlob(function (blob) { 51 | download(`complicated-equation-that-equals-${number}.png`, blob); 52 | }); 53 | }, 54 | }); 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /src/script.js: -------------------------------------------------------------------------------- 1 | import jQuery from "jquery"; 2 | const $ = jQuery.noConflict(); 3 | 4 | $(document).ready(function () { 5 | var form = document.getElementById("form"); 6 | function handleForm(event) { 7 | event.preventDefault(); 8 | convert(); 9 | } 10 | form.addEventListener("submit", handleForm); 11 | 12 | $("#display").on("change", convert); 13 | $(".close ").click(function () { 14 | $("#mobile-notice").hide(); 15 | }); 16 | 17 | function convert() { 18 | // Get the number input 19 | var number = document.getElementById("input").value; 20 | 21 | // Validating input 22 | if (isNaN(number) || number === "" || number > 1000 || number < 0) { 23 | return; 24 | } 25 | 26 | // Checkboxes 27 | 28 | let gammaFuncCheckBox = document.getElementById("gamma-function").checked; 29 | let eulersIdentityCheckBox = 30 | document.getElementById("eulers-identity").checked; 31 | let limitExponentialCheckBox = 32 | document.getElementById("limits-exponential").checked; 33 | let limitPolynomialCheckBox = 34 | document.getElementById("limits-polynomial").checked; 35 | let trigCheckBox = 36 | document.getElementById("trig").checked; 37 | let geometricSeriesCheckBox = 38 | document.getElementById("geometric-series").checked; 39 | 40 | /* helper functions */ 41 | function isOdd(n) { 42 | return n % 2 !== 0; 43 | } 44 | 45 | function isSquare(n) { 46 | return Number.isInteger(Math.sqrt(n)); 47 | } 48 | 49 | function isPrime(n) { 50 | if (n <= 1) return false; // handling negatives 51 | if (n % 2 == 0 && n > 2) return false; // handling even numbers 52 | const s = Math.sqrt(n); // store the square to loop faster 53 | for (let i = 3; i <= s; i += 2) { 54 | // start from 3, stop at the square, increment in twos 55 | if (n % i === 0) return false; // modulo shows a divisor was found 56 | } 57 | return true; 58 | } 59 | 60 | function getFactors(n) { 61 | return [...Array(n + 1).keys()].filter((i) => n % i === 0); 62 | } 63 | 64 | // Checking if a number can be formed using factorial 65 | function isFactorial(n) { 66 | switch (n) { 67 | case 2: 68 | return 2; 69 | break; 70 | case 6: 71 | return 3; 72 | break; 73 | case 24: 74 | return 4; 75 | break; 76 | case 120: 77 | return 5; 78 | break; 79 | case 720: 80 | return 6; 81 | break; 82 | default: 83 | return false; 84 | } 85 | } 86 | 87 | /* Need to add another slash to latex strings to prevent slash escape */ 88 | 89 | // Representing factorial values using the Gamma function or Pi Product notation 90 | function factorial(n) { 91 | if (Math.random() < 0.5 && gammaFuncCheckBox) { 92 | // Using the Gamma function. That is, Gamma(n) = (n-1)! 93 | return `{\\Gamma (${n + 1})}`; 94 | } else { 95 | // Using the pi product notation of factorial 96 | return `{\\prod_{k=1}^{${n}} k}`; 97 | } 98 | } 99 | 100 | // List of functions that generate LaTeX math expressions 101 | // Functions are chosen randomly 102 | let possible_options = []; 103 | 104 | if (eulersIdentityCheckBox) { 105 | possible_options.push(eulers_identity); 106 | } 107 | if (limitExponentialCheckBox) { 108 | possible_options.push(limit_natural_log); 109 | possible_options.push(limit_exponential); 110 | } 111 | if (limitPolynomialCheckBox) { 112 | possible_options.push(lim_diff_two_squares); 113 | possible_options.push(limit_polynomial); 114 | } 115 | if (trigCheckBox) { 116 | possible_options.push(trig_identity); 117 | } 118 | 119 | if (geometricSeriesCheckBox) { 120 | possible_options.push(infinite_geometric_series); 121 | } 122 | 123 | // If all options are unchecked, simply return the original number 124 | function same_number(n) { 125 | return n; 126 | } 127 | 128 | /* Using the difference of two squares in limits */ 129 | function lim_diff_two_squares(n) { 130 | let fac = isFactorial(n); 131 | if (fac && Math.random() < 0.5) { 132 | return factorial(fac); 133 | } 134 | let r = Math.floor(Math.random() * 10) + 1; 135 | if (Math.random() < 0.5) { 136 | var tex = `{\\lim_{x \\to ${n - r}} {{x^2 - ${ 137 | r ** 2 138 | }} \\over {x - ${r}}}}`.trim(); 139 | } else { 140 | var tex = `{\\lim_{x \\to ${n + r}} {{x^2 - ${ 141 | r ** 2 142 | }} \\over {x + ${r}}}}`.trim(); 143 | } 144 | return tex; 145 | } 146 | 147 | // Limits of natural log functions: https://en.wikipedia.org/wiki/List_of_limits#Natural_logarithms 148 | function limit_natural_log(n) { 149 | if (n === 0) { 150 | return `{\\lim_{x \\to \\infty}{ \\ln(x) \\over {x} }}`; 151 | } else if (n === 1) { 152 | return `{\\lim_{x \\to 1} { {\\ln(x)} \\over {x - 1} }}`; 153 | } else { 154 | return `{\\lim_{x \\to 0}{ {-\\ln(1 + ${n}(e^{-x} - 1))} \\over {x} }}`; 155 | } 156 | } 157 | 158 | // Limits of exponential functions: https://en.wikipedia.org/wiki/List_of_limits#Sums,_products_and_composites 159 | function limit_exponential(n) { 160 | if (n === 0) { 161 | return `{\\lim_{x \\to \\infty}{xe^{-x}}}`; 162 | } else if (n === 1) { 163 | return `{\\lim_{x \\to 0}{ {e^x - 1} \\over {x} }}`; 164 | } else { 165 | return `{\\lim_{x \\to 0}{ {e^{${n}x} - 1} \\over {x} }}`; 166 | } 167 | } 168 | 169 | // Limits of polynomial functions 170 | function limit_polynomial(n) { 171 | let fac = isFactorial(n); 172 | if (fac && Math.random() < 0.5) { 173 | return factorial(fac); 174 | } 175 | 176 | // https://en.wikipedia.org/wiki/List_of_limits#Functions_of_the_form_xa 177 | if (n === 0) { 178 | let r = Math.floor(Math.random() * 20); 179 | return `{\\lim_{x \\to \\infty}{${r}x^{-1}}}`; 180 | } 181 | 182 | // https://en.wikipedia.org/wiki/List_of_limits#Functions_of_the_form_xg(x) 183 | else if (n === 1) { 184 | let r = Math.floor(Math.random() * 20); 185 | return `{\\lim_{x \\to \\infty}{x^{1/x}}}`; 186 | } else { 187 | // Get random multiplier greater than 1 188 | let m = Math.floor(Math.random() * 5) + 1; 189 | // Choose a random highest power. This determines the final solution of the limit 190 | let highest_power = Math.floor(Math.random() * 3) + 2; 191 | let numerator_length = 192 | Math.floor(Math.random() * highest_power - 1) + 1; 193 | let denominator_length = 194 | Math.floor(Math.random() * highest_power - 1) + 1; 195 | let signs = ["-", "+"]; 196 | let numerator = `${m * n}x^{${highest_power}} `; 197 | let denominator = `${m}x^{${highest_power}} `; 198 | // Generate a polynomial numerator with random length and coefficients 199 | for (let i = numerator_length; i > 0; i--) { 200 | let coef = Math.floor(Math.random() * 10) + 2; 201 | let power = `^{${i}}`; 202 | if (i < 2) { 203 | // Do not show powers of value 1 204 | power = ""; 205 | } 206 | numerator += `${ 207 | signs[Math.floor(Math.random() * 2)] 208 | } ${coef}x${power} `; 209 | } 210 | 211 | // Generate a polynomial denominator with random length and coefficients 212 | for (let i = denominator_length; i > 0; i--) { 213 | let coef = Math.floor(Math.random() * 10) + 2; 214 | let power = `^{${i}}`; 215 | if (i < 2) { 216 | // Do not show powers of value 1 217 | power = ""; 218 | } 219 | denominator += `${ 220 | signs[Math.floor(Math.random() * 2)] 221 | } ${coef}x${power} `; 222 | } 223 | 224 | // Surround everything with curly braces so that they're treated as one 225 | numerator = `{ ${numerator} }`; 226 | denominator = `{ ${denominator} }`; 227 | return `{\\lim_{x \\to \\infty}{${numerator} \\over {${denominator}}}}`; 228 | } 229 | } 230 | 231 | // Using euler's identity. That is, e^(pi*i) = -1 232 | function eulers_identity(n) { 233 | // If number can be expressed as a factorial, prefer this instead of Euler's identity 234 | let fac = isFactorial(n); 235 | if (fac && Math.random() < 0.5) { 236 | return factorial(fac); 237 | } 238 | // e.g −6e^(pi*i)=6 239 | if (n != 0) { 240 | return `{-${n}e^{\\pi i}}`; 241 | } else { 242 | return `{(e^{\\pi i} + 1)}`; 243 | } 244 | } 245 | 246 | // Infinite geometric series that evaluates to a finite value 247 | function infinite_geometric_series(n) { 248 | // If number can be expressed as a factorial, prefer this instead of the inifinite geometric series expression 249 | let fac = isFactorial(n); 250 | if (fac && Math.random() < 0.5) { 251 | return factorial(fac); 252 | } 253 | 254 | // https://en.wikipedia.org/wiki/List_of_mathematical_series#Trigonometric_functions 255 | if (n === 0) { 256 | let r = Math.floor(Math.random() * 10) + 3; 257 | return `{\\sum\\limits_{k=0}^{${ 258 | r - 1 259 | }} {\\sin \\left({ {2 \\pi k} \\over {${r}} } \\right)}}`; 260 | } 261 | 262 | // Using the Riemann zeta function: https://en.wikipedia.org/wiki/Particular_values_of_the_Riemann_zeta_function#The_Riemann_zeta_function_at_0_and_1 263 | else if (n === 1) { 264 | return `{\\lim_{\\epsilon \\to 0}{ \\epsilon \\zeta(1 + \\epsilon) }}`; 265 | } else { 266 | // Using the infinite geometric series rule: When −1 -1) { 280 | options_for_trig.splice(index1, 1); 281 | } 282 | const index2 = options_for_trig.indexOf(trig_identity); 283 | if (index2 > -1) { 284 | options_for_trig.splice(index2, 1); 285 | } 286 | 287 | let randOption; 288 | 289 | let randIndex = Math.floor(Math.random() * options_for_trig.length); 290 | randOption = options_for_trig[randIndex]; 291 | 292 | if (options_for_trig.length < 1) { 293 | randOption = same_number 294 | } 295 | if (n > 0) { 296 | let randomValue = Math.random(); 297 | if (randomValue < 0.25) { 298 | return `\\left({${randOption( 299 | n 300 | )} \\over {(\\cos^2x + \\sin^2x)}}\\right)`; 301 | } else if (randomValue < 0.5) { 302 | return `\\left({${randOption( 303 | n 304 | )} \\times (\\cos^2x + \\sin^2x)}\\right)`; 305 | } else { 306 | return `\\left({${randOption( 307 | n + 1 308 | )} - (\\cos^2x + \\sin^2x)}\\right)`; 309 | } 310 | } else { 311 | return `\\left({${randOption(n + 1)} - (\\cos^2x + \\sin^2x)}\\right)`; 312 | } 313 | } 314 | 315 | // Break a number down into smaller numbers separated by operators 316 | function decompose(n) { 317 | // Randomly select options 318 | function moreRandomOptions(num) { 319 | return possible_options[ 320 | Math.floor(Math.random() * possible_options.length) 321 | ](num); 322 | } 323 | 324 | if (possible_options.length < 1) { 325 | randomOption1 = same_number; 326 | randomOption2 = same_number; 327 | randomOption3 = same_number; 328 | moreRandomOptions = same_number; 329 | } 330 | 331 | let randomOption1 = moreRandomOptions; 332 | let randomOption2 = moreRandomOptions; 333 | let randomOption3 = moreRandomOptions; 334 | 335 | if (possible_options.length >= 3) { 336 | // Select three possible options. 337 | // Delete option from the list after it's used to avoid using same thing more than once. 338 | let randIndex1 = Math.floor(Math.random() * possible_options.length); 339 | randomOption1 = possible_options[randIndex1]; 340 | let possible_options2 = [...possible_options]; 341 | if (randIndex1 > -1) { 342 | possible_options2.splice(randIndex1, 1); 343 | } 344 | let randIndex2 = Math.floor(Math.random() * possible_options2.length); 345 | randomOption2 = possible_options2[randIndex2]; 346 | let possible_options3 = [...possible_options2]; 347 | if (randIndex2 > -1) { 348 | possible_options3.splice(randIndex2, 1); 349 | } 350 | let randIndex3 = Math.floor(Math.random() * possible_options3.length); 351 | randomOption3 = possible_options3[randIndex3]; 352 | } 353 | 354 | // Another function to break numbers down into smaller numbers, expressed using exponent 355 | function decompose2(n) { 356 | //if (n > 50) { 357 | let randNum = Math.floor(Math.random() * 3) + 6; 358 | let logValue = Math.log(n) / Math.log(randNum); 359 | let floorLog = Math.floor(logValue); 360 | let ceilLog = Math.ceil(logValue); 361 | let exp1 = randNum ** floorLog; 362 | let exp2 = randNum ** ceilLog; 363 | let diff1 = Math.abs(n - exp1); 364 | let diff2 = Math.abs(n - exp2); 365 | if (diff1 < diff2) { 366 | let power = `^{${floorLog}}`; 367 | if (floorLog < 2) { 368 | power = ""; 369 | } 370 | let difference = diff1; 371 | return `{ \\left({${randomOption1( 372 | randNum 373 | )}}\\right)${power} + {${randomOption2(difference)}}}`; 374 | } else { 375 | let power = `^{${ceilLog}}`; 376 | if (ceilLog < 2) { 377 | power = ""; 378 | } 379 | let difference = diff2; 380 | return `{ \\left({${randomOption1( 381 | randNum 382 | )}}\\right) ${power} - {${randomOption2(difference)}}}`; 383 | } 384 | // } 385 | } 386 | 387 | // Generate the smallest possible breakdown in exponent 388 | // Ideal for large numbers > 50 389 | function minExp(n) { 390 | let oldDiff, newDiff, exp, exponent; 391 | let valObj = {}; 392 | 393 | let logValue = Math.log(n) / Math.log(3); 394 | let floorLog = Math.floor(logValue); 395 | let ceilLog = Math.ceil(logValue); 396 | let exp1 = 3 ** floorLog; 397 | let exp2 = 3 ** ceilLog; 398 | let diff1 = Math.abs(n - exp1); 399 | let diff2 = Math.abs(n - exp2); 400 | 401 | if (diff1 < diff2) { 402 | oldDiff = diff1; 403 | exp = [exp1, `\\left({${randomOption3(3)}}\\right)^{${floorLog}}`]; 404 | valObj[oldDiff] = exp; 405 | } else { 406 | oldDiff = diff2; 407 | exp = [exp2, `\\left({${randomOption3(3)}}\\right)^{${ceilLog}}`]; 408 | valObj[oldDiff] = exp; 409 | } 410 | 411 | for (let i = 4; i < 9; i++) { 412 | let logValue = Math.log(n) / Math.log(i); 413 | let floorLog = Math.floor(logValue); 414 | let ceilLog = Math.ceil(logValue); 415 | let exp1 = i ** floorLog; 416 | let exp2 = i ** ceilLog; 417 | let diff1 = Math.abs(n - exp1); 418 | let diff2 = Math.abs(n - exp2); 419 | if (diff1 < diff2) { 420 | exp = [exp1, `\\left({${randomOption3(i)}}\\right)^{${floorLog}}`]; 421 | newDiff = diff1; 422 | valObj[newDiff] = exp; 423 | } else { 424 | exp = [exp2, `\\left({${randomOption3(i)}}\\right)^{${ceilLog}}`]; 425 | newDiff = diff2; 426 | valObj[newDiff] = exp; 427 | } 428 | newDiff = Math.min(newDiff, oldDiff); 429 | oldDiff = newDiff; 430 | exponent = valObj[newDiff]; 431 | } 432 | 433 | if (exponent[0] < n) { 434 | return `${exponent[1]} + \\left({${decompose2(newDiff)}}\\right)`; 435 | } else { 436 | return `${exponent[1]} - \\left({${decompose2(newDiff)}}\\right)`; 437 | } 438 | } 439 | 440 | // Break down larger numbers using exponents 441 | if (n > 70) { 442 | return minExp(n); 443 | } 444 | 445 | let randomValue = Math.random(); 446 | 447 | // ab = (a - c)(b + c) + c (b - a + c), where c is any random positive number 448 | if (n < 100 && (n == 2 || !isPrime(n)) && randomValue < 0.25) { 449 | let factors = getFactors(parseInt(n)); 450 | let randomIndex = Math.floor(Math.random() * factors.length); 451 | let a = factors[randomIndex]; 452 | let b = n / a; 453 | let c = Math.floor(Math.random() * 30) + 1; 454 | 455 | // ab 456 | if (Math.random() < 0.2) { 457 | return `{{\\left({${randomOption1( 458 | a 459 | )}}\\right)}{\\left({${randomOption2(b)}}\\right)}}`; 460 | } 461 | 462 | // (a - c)(b + c) + c (b - a + c) 463 | else { 464 | return `{ \\left({${randomOption1(a)} - ${randomOption2( 465 | c 466 | )}}\\right) \\left({${randomOption3(b)} + ${moreRandomOptions( 467 | c 468 | )}}\\right) + {${moreRandomOptions(c)}}{\\left({${moreRandomOptions( 469 | b 470 | )} - ${moreRandomOptions(a)} + ${moreRandomOptions(c)}} \\right)} }`; 471 | } 472 | } 473 | 474 | // n = (d + 1)^2 - d^2 = 2d + 1 , where d = Math.floor(n/2) 475 | if (n > 2 && isOdd(n) && randomValue < 0.40) { 476 | let d = Math.floor(n / 2); 477 | if (Math.random() < 0.5) { 478 | return `{${randomOption1(2)} \\left({${randomOption2( 479 | d 480 | )}}\\right) + ${randomOption3(1)}}`; 481 | } else { 482 | return `{\\left({${randomOption1(d)} + ${randomOption2( 483 | 1 484 | )}}\\right)^2 - \\left({${randomOption3(d)}}\\right)^2}`; 485 | } 486 | } 487 | 488 | // Represent (small) numbers using their square and square root 489 | else if (randomValue < 0.55 && n < 10) { 490 | let square = n ** 2; 491 | return `{\\sqrt{${randomOption1(square)}}}`; 492 | } 493 | 494 | // The sum of the first n odd numbers is equal to n^2 e.g 1 + 3 + 5 = 3^2 495 | else if (n > 1 && isSquare(n) && randomValue < 0.6) { 496 | let squareroot = Math.sqrt(n); 497 | let sum = `${randomOption1(1)}`; 498 | let oddVal = 1; 499 | 500 | if (Math.random() < 0.2) { 501 | for (let i = 0; i < squareroot - 1; i++) { 502 | let randIndex = Math.floor(Math.random() * possible_options.length); 503 | let randomOption = possible_options[randIndex]; 504 | oddVal += 2; 505 | sum += `+ ${randomOption(oddVal)}`; 506 | } 507 | sum = `{ ${sum} }`; 508 | return sum; 509 | } 510 | 511 | // (a + b)^2 = a^2 + 2ab + b^2 512 | else { 513 | let a = Math.floor(Math.random() * squareroot - 1) + 1; 514 | let b = squareroot - a; 515 | 516 | // Representing (a + b)^2 517 | if (Math.random() < 0.5) { 518 | return `{ {\\left(${randomOption1(a)} + ${randomOption2( 519 | b 520 | )}\\right)}^2}`; 521 | } 522 | 523 | // Representing a^2 + 2ab + b^2 524 | else { 525 | return `{ {\\left(${randomOption1( 526 | a 527 | )}\\right)}^2 + {${moreRandomOptions(2)}}{\\left(${randomOption2( 528 | a 529 | )}\\right)}{\\left(${randomOption3( 530 | b 531 | )}\\right)} + {\\left(${moreRandomOptions(b)}\\right)}^2}`; 532 | } 533 | } 534 | } 535 | 536 | // The sum of two consecutive integers is the difference of their squares e.g 3 + 2 = 3^2 - 2^2 537 | else if (isOdd(n) && randomValue < 0.7) { 538 | let a = Math.floor(n / 2); 539 | let b = n - a; 540 | if (n < 22) { 541 | return `{${randomOption1(b ** 2)} - ${randomOption2(a ** 2)}}`; 542 | } else { 543 | return `{ \\left({${randomOption1( 544 | b 545 | )}}\\right)^2 - \\left({${randomOption2(a)}}\\right)^2}`; 546 | } 547 | } 548 | 549 | // Using Fibonacci's method to generate a pythagorean triple: https://en.wikipedia.org/wiki/Formulas_for_generating_Pythagorean_triples#Fibonacci's_method 550 | else if (randomValue < 0.8 && isOdd(n) && n < 10) { 551 | let a = n; 552 | let a_square = a ** 2; 553 | let position = (a_square + 1) / 2; 554 | // Find sum of previous position - 1 terms 555 | let b_square = 0; 556 | let odd = -1; 557 | for (let i = 1; i < position; i++) { 558 | odd += 2; 559 | b_square += odd; 560 | } 561 | let b = Math.sqrt(b_square); 562 | let c_square = odd + 2 + b_square; 563 | let c = Math.sqrt(c_square); 564 | return `{\\sqrt{\\left({${randomOption1( 565 | c 566 | )}}\\right)^2 - \\left({${randomOption2(b)}}\\right)^2}}`; 567 | } 568 | 569 | // Express a number using multiplication and addition. E.g 4 = 1 * 3 + 1 570 | else if (randomValue < 0.90) { 571 | let randNum = Math.floor(Math.random() * n + 1) + 1; 572 | let r = n % randNum; 573 | let a = Math.floor(n / randNum); 574 | return `${randomOption1(a)} \\times {${randomOption2( 575 | randNum 576 | )}} + ${randomOption3(r)}`; 577 | 578 | // Multiply and divide by a random number. e.g 2 = (2*5)/5 579 | } else { 580 | let r = Math.floor(Math.random() * 5) + 1; 581 | return `${randomOption1(n * r)} \\over {${randomOption2(r)}}`; 582 | } 583 | } 584 | 585 | let input = decompose(number); 586 | 587 | // Disable the display and render buttons until MathJax is done 588 | var display = document.getElementById("display"); 589 | var button = document.getElementById("render"); 590 | button.disabled = display.disabled = true; 591 | let downloadBtn = document.getElementById("download-img"); 592 | 593 | // Clear the old output 594 | 595 | let output = document.getElementById("output"); 596 | output.innerHTML = ""; 597 | 598 | // Reset the tex labels (and automatic equation numbers, though there aren't any here). 599 | // Get the conversion options (metrics and display settings) 600 | // Convert the input to CommonHTML output and use a promise to wait for it to be ready 601 | // (in case an extension needs to be loaded dynamically). 602 | MathJax.texReset(); 603 | var options = MathJax.getMetricsFor(output); 604 | options.display = display.checked; 605 | MathJax.tex2svgPromise(input, options) 606 | .then(function (node) { 607 | // The promise returns the typeset node, which we add to the output 608 | // Then update the document to include the adjusted CSS for the 609 | // content of the new equation. 610 | output.appendChild(node); 611 | MathJax.startup.document.clear(); 612 | MathJax.startup.document.updateDocument(); 613 | // Display download button 614 | downloadBtn.style.display = "block"; 615 | }) 616 | .catch(function (err) { 617 | // If there was an error, put the message into the output instead 618 | output 619 | .appendChild(document.createElement("pre")) 620 | .appendChild(document.createTextNode(err.message)); 621 | }) 622 | .then(function () { 623 | // Error or not, re-enable the display and render buttons 624 | button.disabled = display.disabled = false; 625 | }); 626 | } 627 | }); 628 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | width: 100%; 3 | text-align: right; 4 | padding-top: 30px; 5 | } 6 | .menu a { 7 | padding: 20px; 8 | font-weight: bold; 9 | } 10 | #frame { 11 | max-width: 60em; 12 | margin: auto; 13 | margin-top: 5em; 14 | font-family: Arial, Helvetica, sans-serif; 15 | } 16 | .intro { 17 | text-align: center; 18 | max-width: 22em; 19 | margin: auto; 20 | margin-bottom: 50px; 21 | 22 | } 23 | .intro h1 { 24 | color:brown; 25 | } 26 | 27 | .intro h3 { 28 | color:#555; 29 | } 30 | 31 | #input { 32 | border: 1px solid grey; 33 | margin: 0 0 0.25em; 34 | width: 100%; 35 | font-size: 120%; 36 | box-sizing: border-box; 37 | } 38 | #render { 39 | padding: 10px 20px; 40 | font-size: 15px; 41 | outline: none; 42 | border: none; 43 | font-weight: 500; 44 | background-color: brown; 45 | color: #fff; 46 | cursor: pointer; 47 | } 48 | #output { 49 | font-size: 120%; 50 | margin-top: 0.35em; 51 | border: 1px solid grey; 52 | padding: 0.25em; 53 | min-height: 2em; 54 | overflow-y: scroll; 55 | } 56 | #output > pre { 57 | margin-left: 5px; 58 | } 59 | .lr { 60 | display: flex; 61 | justify-content: space-between; 62 | } 63 | #mobile-notice { 64 | padding: 20px; 65 | margin: 10px; 66 | margin-bottom: 50px; 67 | border: 2px solid red; 68 | display: none; 69 | position: relative; 70 | } 71 | .close { 72 | padding: 10px 20px; 73 | font-weight: bold; 74 | font-size: 30px; 75 | position: absolute; 76 | right: 0; 77 | top: 0; 78 | background: red; 79 | cursor: pointer; 80 | } 81 | 82 | #download-img { 83 | margin-top: 10px; 84 | padding: 10px 20px; 85 | font-size: 15px; 86 | outline: none; 87 | border: 2px solid brown; 88 | font-weight: 500; 89 | background-color: transparent; 90 | color: brown; 91 | cursor: pointer; 92 | display: none; 93 | } 94 | 95 | .options { 96 | display: flex; 97 | flex-wrap: wrap; 98 | margin-top: 20px; 99 | } 100 | 101 | .tips h4 { 102 | margin-bottom: 1px; 103 | } 104 | 105 | .tips ul { 106 | margin-top: 0; 107 | padding-left: 23px; 108 | } 109 | .tips li { 110 | font-size: 15px; 111 | font-style: italic; 112 | } 113 | 114 | @media(max-width:550px){ 115 | #mobile-notice { 116 | display: block; 117 | } 118 | 119 | } --------------------------------------------------------------------------------