Compare commits
5 Commits
c69e6ab7c1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 45a11a379a | |||
| 8a7b952deb | |||
| 540fdb208c | |||
| f2e29f2735 | |||
| 97c0fb7f8e |
12
README.md
12
README.md
@@ -1,7 +1,15 @@
|
|||||||
GCF (and LCM) Practice
|
GCF/LCM/Multiplication Practice
|
||||||
===
|
===
|
||||||
This is a simple app to practice on greatest common factor (GCF) and least
|
This is a simple app to practice on greatest common factor (GCF) and least
|
||||||
common multiple (LCM) math problems.
|
common multiple (LCM) math problems, as well as simple multiplication. It
|
||||||
|
was created to help a primary school student practice math skills. Solutions
|
||||||
|
are provided on request.
|
||||||
|
|
||||||
|
While the defaults for GCF and LCM (pairs of positive integers up to 200) may
|
||||||
|
seem large for such a young learner, the app demonstrates how to apply Euclid's
|
||||||
|
algorithm, which requires only a knowledge of integer division with remainders,
|
||||||
|
to calculate GCF. Numeric values are color coded so that the student can follow
|
||||||
|
each step of the calculation.
|
||||||
|
|
||||||
Compiling
|
Compiling
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
"react-bootstrap": "^2.10.1",
|
"react-bootstrap": "^2.10.1",
|
||||||
"react-dom": "npm:@preact/compat",
|
"react-dom": "npm:@preact/compat",
|
||||||
"react-router": "^6.22.2",
|
"react-router": "^6.22.2",
|
||||||
"react-router-dom": "^6.22.2"
|
"react-router-dom": "^6.22.2",
|
||||||
|
"rollup": "npm:@rollup/wasm-node"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.8.1",
|
"@preact/preset-vite": "^2.8.1",
|
||||||
|
|||||||
@@ -27,10 +27,10 @@
|
|||||||
|
|
||||||
.no-arrows::-webkit-outer-spin-button,
|
.no-arrows::-webkit-outer-spin-button,
|
||||||
.no-arrows::-webkit-inner-spin-button {
|
.no-arrows::-webkit-inner-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none !important;
|
||||||
margin: 0;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-arrows {
|
.no-arrows {
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function App() {
|
|||||||
<Navbar maxNum={maxNum} onMaxNumChanged={onMaxNumSet} />
|
<Navbar maxNum={maxNum} onMaxNumChanged={onMaxNumSet} />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Navigate to="/gcf" />} />
|
<Route path="/" element={<Navigate to="/gcf" />} />
|
||||||
<Route path="/gcf" element={<GCF />} />
|
<Route path="/gcf" element={<GCF maxNum={maxNum} />} />
|
||||||
<Route path="/times" element={<TimesPractice maxNum={12} />} />
|
<Route path="/times" element={<TimesPractice maxNum={12} />} />
|
||||||
<Route path="/lcm" element={<LCM />} />
|
<Route path="/lcm" element={<LCM />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
22
src/gcf.tsx
22
src/gcf.tsx
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'preact/hooks'
|
import { useState, useEffect } from 'preact/hooks'
|
||||||
import './gcf.css'
|
import './gcf.css'
|
||||||
import { gcf } from './lib'
|
import { gcf } from './lib'
|
||||||
import type { JSX } from 'preact'
|
import type { JSX } from 'preact'
|
||||||
@@ -37,15 +37,17 @@ function Solution({rows}:SolutionParams) {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_MAXNUM = 200
|
||||||
|
|
||||||
export function GCF() {
|
|
||||||
|
export function GCF({ maxNum } : { maxNum? : number }) {
|
||||||
// choose number between 2 and max inclusive
|
// choose number between 2 and max inclusive
|
||||||
const getRandomInt = (max : number) : number => Math.floor(Math.random() * (max - 1) + 2)
|
const getRandomInt = (max : number) : number => Math.floor(Math.random() * (max - 1) + 2)
|
||||||
|
|
||||||
const chooseFactorsRandom = () : [number, number] => [ getRandomInt(200), getRandomInt(200) ]
|
const chooseFactorsRandom = () : [number, number] => [ getRandomInt(maxNum || DEFAULT_MAXNUM), getRandomInt(maxNum || DEFAULT_MAXNUM) ]
|
||||||
|
|
||||||
const _chooseMultiplesOf = (f : number) : [number, number] => {
|
const _chooseMultiplesOf = (f : number) : [number, number] => {
|
||||||
const max = Math.floor(200 / f)
|
const max = Math.floor((maxNum || DEFAULT_MAXNUM) / f)
|
||||||
return [ f * getRandomInt(max), f * getRandomInt(max) ]
|
return [ f * getRandomInt(max), f * getRandomInt(max) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ export function GCF() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chooseMultiples = () : [number, number] => {
|
const chooseMultiples = () : [number, number] => {
|
||||||
const f = getRandomInt(50)
|
const f = getRandomInt(Math.min(50, Math.floor((maxNum || DEFAULT_MAXNUM) / 2)))
|
||||||
return _chooseMultiplesOf(f)
|
return _chooseMultiplesOf(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +137,16 @@ export function GCF() {
|
|||||||
const [feedback, setFeedback] = useState(false)
|
const [feedback, setFeedback] = useState(false)
|
||||||
const [solution, setSolution] = useState<JSX.Element|null>(null)
|
const [solution, setSolution] = useState<JSX.Element|null>(null)
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
if (maxNum) {
|
||||||
|
if ((factors[0] > maxNum) || (factors[1] > maxNum)) {
|
||||||
|
// don't change unless new constraint violated
|
||||||
|
setFactors(chooseFactors())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [ maxNum ])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-4 py-5 my-5 text-center">
|
<div className="px-4 py-5 my-5 text-center">
|
||||||
<h1 className="my-5 d-none d-sm-block">Greatest Common Factor Practice</h1>
|
<h1 className="my-5 d-none d-sm-block">Greatest Common Factor Practice</h1>
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ function Navbar({maxNum, onMaxNumChanged} : NavbarSettings) {
|
|||||||
<div className="nav-item ms-auto me-6 g-3 row align-items-center">
|
<div className="nav-item ms-auto me-6 g-3 row align-items-center">
|
||||||
<label className="col-form-label col-sm-4 col-form-label-sm ge-2 flex-shrink-0" for="maxNum">Max:</label>
|
<label className="col-form-label col-sm-4 col-form-label-sm ge-2 flex-shrink-0" for="maxNum">Max:</label>
|
||||||
<div className="col-sm-4 flex-shrink-0">
|
<div className="col-sm-4 flex-shrink-0">
|
||||||
<input className="form-range" type="range" id="maxNum" step={10} value={maxNum} max={200} min={50} onInput={handleSliderInput} onChange={onSliderChange} />
|
<input className="form-range" type="range" id="maxNum" step={10} value={displayedMax} max={200} min={50} onMouseUp={handleSliderInput} onKeyUp={handleSliderInput} onChange={onSliderChange} />
|
||||||
</div>
|
</div>
|
||||||
<label className="col-form-label col-sm-2 ms-auto">{displayedMax}</label>
|
<label className="col-form-label col-sm-2 ms-auto">{displayedMax}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useState } from 'preact/hooks'
|
import { useEffect, useState } from 'preact/hooks'
|
||||||
import './times.css'
|
import './times.css'
|
||||||
|
import type { JSX } from 'preact'
|
||||||
|
|
||||||
interface TimesPracticeParams {
|
interface TimesPracticeParams {
|
||||||
maxNum : number
|
maxNum : number
|
||||||
|
|||||||
Reference in New Issue
Block a user