commit baab68b5a12bcd8ee673d97587141ec825b63185
parent 74c125455aba0f112360afc1fbf9c3a4fce5b805
Author: therealFIGBERT <figbertwelner@gmail.com>
Date: Wed, 4 Dec 2019 09:24:32 -0800
Add simultaneous equation solver graphics, add immutability-helper package
Adding dynamic generation of equations buttons using matrix iteration in the largebutton/index.js and simultaneousEquation.js files. The solver does not yet execute the defined calculate function.
The immutability-helper package is used to update the matrix in the SimultaneousEquation class' state property.
Diffstat:
6 files changed, 208 insertions(+), 13 deletions(-)
diff --git a/package.json b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "immutability-helper": "^3.0.1",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
diff --git a/src/components/highLevel/simultaneousEquation.js b/src/components/highLevel/simultaneousEquation.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import update from 'immutability-helper';
import { CalcOut } from "../lowLevel/calcOut";
import { Pad } from "../lowLevel/pad";
@@ -7,16 +8,144 @@ export class SimultaneousEquation extends React.Component {
constructor(props) {
super(props);
this.state = {
- output: '',
- type: 'Two Variables',
- amount: '2'
+ output: [],
+ dimension: 2,
+ coefficients: [['', ''], ['', '']],
+ sums: ['', '']
};
+ this.modifyMatrix = this.modifyMatrix.bind(this);
+ this.solveMatrix = this.solveMatrix.bind(this);
+ this.onChange = this.onChange.bind(this);
+ }
+
+ onChange(object) {
+ let name = object.target.name,
+ i, k;
+ const value = object.target.value;
+ if (name.indexOf('c') > -1) {
+ name = name.substr(1);
+ i = Number(name.split('_')[0]);
+ k = Number(name.split('_')[1]);
+ this.setState({
+ coefficients: update(this.state.coefficients, {[i]: {[k]: {$set: value}}})
+ });
+ } else if (name.indexOf('s') > -1) {
+ i = Number(name.substr(1));
+ this.setState({
+ sums: update(this.state.sums, {[i]: {$set: value}})
+ });
+ } else {
+ this.setState({
+ output: 'ERROR'
+ });
+ }
+ }
+
+
+ modifyMatrix(object) {
+ const value = object.target.value;
+ let numOfVars = [],
+ rows = [],
+ sums = [],
+ newDimension;
+ if (value === 'raise') {
+ newDimension = this.state.dimension + 1;
+ } else if (value === 'lower') {
+ newDimension = this.state.dimension - 1;
+ if (newDimension < 2) {
+ newDimension = 2;
+ }
+ }
+ for (let i = 0; i < newDimension; i++) {
+ numOfVars.push('');
+ sums.push('');
+ }
+ for (let i = 0; i < newDimension; i++) {
+ rows.push(numOfVars);
+ }
+ this.setState({
+ dimension: newDimension,
+ coefficients: rows,
+ sums: sums
+ });
+ }
+
+ solveMatrix(equations, sums) {
+ let abs = Math.abs;
+ function array_fill(i, n, v) {
+ let a = [];
+ while (i < n) {
+ a.push(v);
+ i++;
+ }
+ return a;
+ }
+
+ // Adds the sums to the equations array
+ for (let i = 0; i < equations.length; i++) {
+ equations[i].push(sums[i]);
+ }
+ const numberOfEquations = equations.length;
+
+ for (let i = 0; i < numberOfEquations; i++) {
+ // Iterates over the first column of every row below the current one (i) and determines which row is the largest
+ let maxEl = abs(equations[i][i]),
+ maxRow = i;
+ for (let k = i + 1; k < numberOfEquations; k++) {
+ if (abs(equations[k][i]) > maxEl) {
+ maxEl = abs(equations[k][i]);
+ maxRow = k;
+ }
+ }
+ // Swaps the larger row and the current row, if a swap should be made
+ if (maxRow !== i) {
+ for (let k = i; k < numberOfEquations + 1; k++) {
+ const tmp = equations[maxRow][k];
+ equations[maxRow][k] = equations[i][k];
+ equations[i][k] = tmp;
+ }
+ }
+ // Makes all lower numbers in the column zero
+ for (let k = i + 1; k < numberOfEquations; k++) {
+ const c = -equations[k][i] / equations[i][i];
+ for (let j = i; j < numberOfEquations + 1; j++) {
+ if (i === j) {
+ equations[k][j] = 0;
+ } else {
+ equations[k][j] += c * equations[i][j];
+ }
+ }
+ }
+ }
+
+ //Solve the simplified matrix
+ sums = array_fill(0, numberOfEquations, 0);
+ for (let i = numberOfEquations - 1; i > -1; i--) {
+ sums[i] = equations[i][numberOfEquations] / equations[i][i];
+ for (let k = i - 1; k > -1; k--) {
+ equations[k][numberOfEquations] -= equations[k][i] * sums[i];
+ }
+ }
+
+ //Set the output to the array of values
+ this.setState({
+ output: sums
+ });
}
render() {
return (
<div>
<CalcOut mode={this.props.mode} output={this.state.output} />
+ <Pad
+ type={'large'}
+ mode={this.props.mode}
+ buttonValues={['range', [this.state.coefficients, this.state.sums], 'select']}
+ displayValues={[this.state.dimension, undefined, '…']}
+ onClick={this.modifyMatrix}
+ onChange={this.onChange}
+ onModeClick={this.props.onModeChange}
+ />
</div>
);
}
diff --git a/src/components/lowLevel/largeButton/index.js b/src/components/lowLevel/largeButton/index.js
@@ -5,7 +5,8 @@ import styles from './styles.module.css';
export class LargeButton extends React.Component {
render() {
const canBeDisabled = this.props.mode === 'angSize',
- acceptsInput = this.props.mode !== 'select';
+ acceptsInput = this.props.mode !== 'select',
+ equationFormat = this.props.mode === 'simultaneousEQ';
if (this.props.value === 'select') {
return (
<button
@@ -78,6 +79,56 @@ export class LargeButton extends React.Component {
</button>
);
}
+ } else if (equationFormat) {
+ const alphabet = (function(charA, charZ) {
+ let a = [], i = charA.charCodeAt(0), j = charZ.charCodeAt(0);
+ for (; i <= j; ++i) {
+ a.push(String.fromCharCode(i));
+ }
+ return a;
+ }('a', 'z'));
+ let coefficients = this.props.value[0],
+ sums = this.props.value[1],
+ buttons = [];
+ for (let i = 0; i < coefficients.length; i++) {
+ let eq = [];
+ for (let k = 0; k < coefficients[i].length; k++) {
+ const name = 'c' + i + '_' + k;
+ eq.push(
+ <div>
+ <input
+ type='number'
+ className={styles.smallInput}
+ name={name}
+ value={coefficients[i][k]}
+ onChange={this.props.onChange}
+ />
+ {alphabet[k]}
+ </div>
+ );
+ if (k + 1 !== coefficients[i].length) { eq.push('+') }
+ }
+ eq.push(
+ '=',
+ <input
+ type='number'
+ className={styles.smallInput}
+ name={'s' + i}
+ value={sums[i]}
+ onChange={this.props.onChange}
+ />
+ );
+ buttons.push(
+ <div className={styles.rowButton}>
+ {eq}
+ </div>
+ )
+ }
+ return (
+ <div>
+ {buttons}
+ </div>
+ );
} else if (acceptsInput) {
return (
<div className={styles.columnButton}>
@@ -111,7 +162,13 @@ LargeButton.propTypes = {
onChange: PropTypes.func,
onModeClick: PropTypes.func,
disabledTruth: PropTypes.bool,
- value: PropTypes.string,
- displayValue: PropTypes.string,
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.array
+ ]),
+ displayValue: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ]),
textValue: PropTypes.string
};
\ No newline at end of file
diff --git a/src/components/lowLevel/largeButton/styles.module.css b/src/components/lowLevel/largeButton/styles.module.css
@@ -9,6 +9,10 @@ input {
font-family: Tomorrow, -apple-system, sans-serif;
}
+.smallInput {
+ width: 2vmax;
+}
+
.columnButton {
background: #ac1a2d;
color: rgba(255, 255, 255, .7);
diff --git a/src/components/lowLevel/pad/index.js b/src/components/lowLevel/pad/index.js
@@ -31,9 +31,9 @@ export class Pad extends React.Component {
onClick={onClick}
onChange={onChange}
onModeClick={onModeClick}
- disabledTruth={disabledTruths == null ? false : disabledTruths[index]}
value={value}
- displayValue={displayValues[index]}
+ disabledTruth={disabledTruths == null ? false : disabledTruths[index]}
+ displayValue={displayValues == null ? '' : displayValues[index]}
textValue={textValues == null ? '' : textValues[index]}
/>
);
@@ -48,15 +48,12 @@ export class Pad extends React.Component {
}
Pad.propTypes = {
type: PropTypes.string.isRequired,
- buttonValues: PropTypes.oneOfType([
- PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.string)),
- PropTypes.arrayOf(PropTypes.string)
- ]).isRequired,
+ buttonValues: PropTypes.array.isRequired,
onClick: PropTypes.func,
mode: PropTypes.string,
onChange: PropTypes.func,
onModeClick: PropTypes.func,
disabledTruths: PropTypes.arrayOf(PropTypes.bool),
- displayValues: PropTypes.arrayOf(PropTypes.string),
+ displayValues: PropTypes.array,
textValues: PropTypes.arrayOf(PropTypes.string)
};
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
@@ -4797,6 +4797,13 @@ immer@1.10.0:
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
+immutability-helper@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.0.1.tgz#4f609c5afbf8d78cb297970e8af2fba8b0eda1d6"
+ integrity sha512-U92ROQQt7XkIwrdqCByUI118TQM1hXdKnRQpvKeA0HRyGSnJipu9IWHe4UD8zCN00O8UnQjQzPCgZ1CC3yBzHA==
+ dependencies:
+ invariant "^2.2.4"
+
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"