【React&数学】既約ピタゴラス数生成マシンをつくってみた

#まず自己紹介
- 元ものづくりエンジニア🔧
- 独学でReact、Railsを使った開発をしてる人

#はじめに
Reactに慣れてきたので趣味である数学を使って遊んでみました。
今回は任意の数字を入力すると既約ピタゴラス数を作ってくれるツールをつくってみました。
本記事はこちらの方が対象です。

1. Reactの初心者
2. Reactを復習したい方
2. 数学とプログラミングが好きな方

#既約ピタゴラス数生成マシン
まずどんなものかお見せしたいと思います。見た目は少々悪いですがご了承下さい🙏
二つの入力欄に任意の数字を打ち込んで**CREATE**を入力すると、既約ピタゴラス数の組(a,b,c)と直角三角形の外形を出力してくれます。

![スクリーンショット 2021-04-06 9.59.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/717893/6756c807-8491-e69c-317c-f1d4d9eee95c.png)

#既約ピタゴラス数とは
そもそもピタゴラス数ってなんだっけ？となっている方もいると思います。
ピタゴラス数とは**直角三角形の辺となる3つの整数の組**です。例えば$(3,4,5)$が該当します。三平方の定理で馴染み深い方が多いのではないでしょうか。

$$ a^2 + b^2 = c^2$$

ピタゴラス数を見つけることはそんなに難しいことはありません。一つの組みを発見しさえすれば3つの数に同じ数をかければいくらでも生成できます。しかしこれは最初に発見したピタゴラス数の直角三角形を拡大したものに過ぎず、面白みがありません。

そこで既約ピタゴラス数の登場です。**既約ピタゴラス数とは共通因数をもたないピタゴラス数です。**
これを見つけるのは少し難しそうですが、次の式を使って無限に作ることができます。

$$a=st　b=\frac{t^2-s^2}{2}　c=\frac{t^2+s^2}{2}$$

ここで$s,t$は$1\leq{s}\<{t}$を満たす互いに素な奇数です。証明は中学数学でできるので興味のある方は証明してみてください!

#実装
##1.環境構築
さっそくReactで実装したいと思います。まずはNode.jsがインストールされていることを確認してください。
ご自身のPCでターミナルを開いて以下を実行してください。

```shell
$ node -v
v14.16.0
```

バージョンが出力されればインストールされています。

次に任意のディレクトリに移動して下記を実行します。名前は各自任意で構いません。

```shell
$npx create-react-app pythagoras
```

無事完了するとReactのプロジェクトの環境構築が完了します。
お使いのエディタでプロジェクトを開いてみて、ターミナルで以下を実行してみてください。

```shell
$npm start
```

するとブラウザが起動し、下のようなページが表示されるはずです。
![スクリーンショット 2021-04-06 9.04.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/717893/9e7048c4-f44b-0fa9-8e56-e0c0041eec83.png)

また今回はMaterial-UIと呼ばれるUIコンポーネントライブラリとreact-springというアニメーションライブラリを使っていきます。詳しくは[Material-UIのHP](https://material-ui.com/)と[react-springのHP](https://www.react-spring.io/)から。
デザインやアニメーションはどうでもいいという方は省いても結構です。

では以下をターミナル上で実行してインストールしてください。

```shell
$npm install --save @material-ui/core
```
```shell
$npm install react-spring
```

これでMaterial-UIとract-springのコンポーネントが使用できます。

##2.コンポーネントの作成
まずは設計。`src`ディレクトリ配下の`App.js`に以下の3つのコンポーネントを配置するようにします。（これも名前は任意で結構です。）

- s,tの入力や既約ピタゴラス数生成ボタンを配置する`Form.jsx`コンポーネント
- a,b,cの解を表示する`Length.jsx`コンポーネント
- 直角三角形の外見を表示する`Triangle.jsx`コンポーネント

また、`state`で`s,t,a,b,c`を状態管理します。初期値は
$$s=1,\ t=3,\ a=3,\ b=4,\ c=5$$
とします。

ではまずプロジェクトの`src/App.js`を開いて以下のように変更してください。
`state`を設定して`Form.jsx`、`Length.jsx`、`Triangle.jsx`のコンポーネントをとりあえず読み込んでいます。

```react:src/App.js
import React from 'react'
import Form from './components/Form'
import Length from './components/Length'
import Triangle from './components/Triangle'
import './App.css'

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      s:1,
      t:3,
      a:3,
      b:4,
      c:5
    }
  }

  render(){
    return(
      <div>
        <h3>互いに素な奇数s,tを入力してください(1≦s＜t)</h3>
        <Form />
        <Length />
        <Triangle />
      </div>
    )
  }
}

export default App;
```

###`Form.jsx`関数コンポーネント
まず`Form.jsx`からつくっていきます。`src`ディレクトリ配下に`components`ディレクトリを作成し、その配下に`Form.jsx`を以下のように作成してください。後に記述する`Length.jsx`と`Triangle.jsx`も同様に作成しておいてください。

```react:src/components/Form.jsx
import React from 'react'
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';

const useStyles = makeStyles((theme) => ({
  root: {
    '& > *': {
      margin: theme.spacing(1),
      width: '25ch',
    },
  },
}));


const Form = (props) => {
    const classes = useStyles();

    return(
        <form className={classes.root} noValidate autoComplete="off">
            <TextField name="s" label="sを入力" variant="outlined" onChange={props.handleChange}/>
            <TextField name="t" label="tを入力" variant="outlined" onChange={props.handleChange}/>
            <Button variant="contained" color="primary" href="#contained-buttons" onClick = {props.calculate}>
                CREATE
            </Button>
        </form>
    )
}

export default Form
```

`Form.jsx`コンポーネントは関数コンポーネントにして、Material UIの`TextField`と`Button`コンポーネントを読み込んでいます。また、`useStyles`は`TextField`と`Button`コンポーネントのスタイルを決定しています。

`TextField`と`Button`コンポーネントをみてみると`onChange`属性に`props.handleChange`、`onClick`属性に`props.calculate`が渡されています。これは後に`App.js`でコンポーネントを使用するときに、数字が入力されると`handleChange()`関数が渡されるように属性を設定しています。`props.calculate`も同様です。これらの関数は後に`App.js`で定義します。

###`Length.jsx`関数コンポーネント
続いて`Length.jsx`コンポーネントを作成します。以下のように記述してください。

```react:src/components/Length.jsx
import React from 'react'
import {useSpring, animated} from 'react-spring'

const Length = (props) => {
    const lengthValue = useSpring({
        a:props.a,
        b:props.b,
        c:props.c,
        config:{precision:1}
    })
    return(
        <div>
            <p>a=<animated.span>{lengthValue.a}</animated.span></p>
            <p>b=<animated.span>{lengthValue.b}</animated.span></p>
            <p>c=<animated.span>{lengthValue.c}</animated.span></p>
        </div>

    )
}

export default Length
```

react-springの`animated`コンポーネントでアニメーションを与えています。これにより数字の推移が連続的に変化していることを確認できます。
※react-springを使用しない方は`lengthValue.a`を`props.a`に変更すればいいです。

###`Triangle.jsx`関数コンポーネント
引き続き`Triangle.jsx`コンポーネントを作成します。これも`Length.jsx`と同様、react-springを使っていきます。

```react:src/components/Triangle.jsx
import React from 'react'
import {useSpring, animated} from 'react-spring'

const Triangle = (props) => {
    const useStyle = useSpring({
        width: 0,
        height: 0,
        borderRight: `${props.a*10}px solid transparent`,
        borderBottom: `${props.b*10}px solid lightblue`,
        marginLeft: '100px'
    })

    return(
        <animated.div style={useStyle}></animated.div>
    )
}

export default Triangle
```

直角三角形を描くスタイルを作って`animated`コンポーネントの`style`属性に渡しています。
また`borderRight:`に`props.a`を入れて`this.state.a`を渡せるようにしています。`borderBottom:`も同様です。

###`App.js`クラスコンポーネント
子コンポーネントの準備ができたので、親コンポーネント`App.js`を作っていきます。
まず`handleChange`関数と`calculate`関数を作成します。先に作成した`Form.jsx`コンポーネントに`props.handleChange`属性と`props.calculate`属性を設けましたが、ここにその関数を渡してあげます。

```react:src/App.js
import React from 'react'
import Form from './components/Form'
import Length from './components/Length'
import Triangle from './components/Triangle'
import './App.css'


class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      s:1,
      t:3,
      a:3,
      b:4,
      c:5
    }
    this.handleChange = this.handleChange.bind(this)
    this.calculate = this.calculate.bind(this)
  }

  handleChange = (event) => {
    this.setState({
      [event.target.name]:parseInt(event.target.value,10)
    })
  }

  calculate = () => {
    this.setState({
      a:this.state.s*this.state.t,
      b:(this.state.t**2-this.state.s**2)/2,
      c:(this.state.t**2+this.state.s**2)/2
    })
  }

  render(){
    return(
      <div>
        <h3>互いに素な奇数s,tを入力してください(1≦s＜t)</h3>
        <Form handleChange={this.handleChange} calculate={this.calculate} />
        <Length a={this.state.a} b={this.state.b} c={this.state.c}/>
        <Triangle a={this.state.a} b={this.state.b}/>
      </div>

    )
  }

}

export default App;
```
`handleChange`関数は`event`オブジェクトを受け取って`s`と`t`に`setState`します。これにより入力された値に随時変更されます。
`calculate`関数は`state`の`s`と`t`から計算し`a,b,c`を算出します。
これら関数は`Form.jsx`コンポーネントの`handleChange`と`calculate`属性に渡されます。

ちなみに`constructor(props){...}`内に

```react
    this.handleChange = this.handleChange.bind(this)
    this.calculate = this.calculate.bind(this)
```
が追加されていますが、これがないとエラーが出ます。コンポーネント内にイベントバンドラを渡す場合にはこのようにバインドするようにしましょう。

ここでターミナルで以下を実行し、ローカル環境で動作を確認してみてください。

```shell
$npm start
```

入力してみると`a,b,c`が出力され、直角三角形が変形したことを確認できると思います。

##3.バリデーションを追加
ここまでで一応`a,b,c`は出てきます。しかしお気づきの方もいらっしゃると思いますが、このままでは偶数や互いに素ではない数も入力できてしまいます。
そこでバリデーションを追加しましょう。`App.js`の`calculate`関数内で`switch(){...}`構文で条件分岐します。
するとこうなります。

```react:src/App.js
import React from 'react'
import Form from './components/Form'
import Length from './components/Length'
import Triangle from './components/Triangle'
import './App.css'


class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      s:1,
      t:3,
      a:3,
      b:4,
      c:5
    }

    this.handleChange = this.handleChange.bind(this)
    this.calculate = this.calculate.bind(this)
  }

  handleChange = (event) => {
    this.setState({
      [event.target.name]:parseInt(event.target.value,10)
    })
    event.preventDefault()
  }

  calculate = () => {
    const reg = new RegExp(/^[0-9]*$/)
    const gcd = (p,q) => {
      if (q === 0){
        return p
      }
      return gcd(q, p%q)
    }

    switch(true){
      case(this.state.s<1||this.state.t<1):
        alert('sとtは1以上の整数です!')
        break;
      case(!(reg.test(this.state.s))||!(reg.test(this.state.t))):
        alert('sとtは半角数字で入力してください!')
        break;
      case(this.state.s>=this.state.t):
        alert('sはtより小さくしてください!')
        break;
      case((this.state.s%2) === 0 && (this.state.t%2) === 0):
        alert('sとtが偶数になってます!')
        break;
      case((this.state.s%2) === 0):
        alert('sが偶数になってます!')
        break;
      case((this.state.t%2) === 0):
        alert('tが偶数になってます!')
        break;
      case(gcd(this.state.t,this.state.s)!==1):
        alert('sとtが互いに素ではありません!')
        break;
      default:
        this.setState({
          a:this.state.s*this.state.t,
          b:(this.state.t**2-this.state.s**2)/2,
          c:(this.state.t**2+this.state.s**2)/2
        })
        break;
    }
  }

  render(){
    return(
      <div>
        <h3>互いに素な奇数s,tを入力してください(1≦s＜t)</h3>
        <Form handleChange={this.handleChange} calculate={this.calculate} />
        <Length a={this.state.a} b={this.state.b} c={this.state.c}/>
        <Triangle a={this.state.a} b={this.state.b}/>
      </div>
    )
  }

}

export default App;
```

これにより「$s,t$は$1\leq{s}\<{t}$を満たす互いに素な奇数」を満たしていない値を入力するとアラートが出るようになります。
ここで`reg`は半角数字か判定を行うオブジェクトです。
また`gcd()`関数が追加されていますが、これは`s,t`の最大公約数を計算する再帰関数で、最大公約数が1でないものは認めないようにしています。

ここで改めてローカル環境で確認してください。不適な値を入力するとアラートが出てくると思います。

```shell
$npm start
```

#まとめ
Reactのコンポーネントやstate、さらにはMaterial-UIやreact-springを使って、既約ピタゴラス数を生成する簡単なアプリを実装しました。React初心者の方、復習したい方のお力になれば幸いです🙇‍♂️
今後もReactや数学に関する記事をアップしていきたいと思います!

##参考
1. [Material-UIホームページ](https://material-ui.com/)
2. [react-springホームページ](https://www.react-spring.io/)