Repository: syuilo/aiscript (ts)

Issue:
@Math:ceil(x: num): num
```javascript
 @Math:ceil(x: num): num 
``` 
で小数点以下切り上げができると嬉しいです。

Pull request description:
` @Math:ceil(x: num): num ` を追加
<!-- ℹ お読みください
PRありがとうございます！ PRを作成する前に、以下をご確認ください:
- 可能であればタイトルに、以下で示すようなPRの種類が分かるキーワードをプリフィクスしてください。
  - fix / refactor / feat / enhance / perf / chore
  - また、PRの粒度が適切であることを確認してください。ひとつのPRに複数の種類の変更や関心を含めることは避けてください。
- このPRによって解決されるIssueがある場合は、そのIssueへの参照を本文内に含めてください。
- CHANGELOG.mdに変更点を追記してください。リファクタリングなど、利用者に影響を与えない変更についてはこの限りではありません。
- この変更により新たに作成、もしくは更新すべきドキュメントがないか確認してください。
- 機能追加やバグ修正をした場合は、可能であればテストケースを追加してください。
- テスト、Lintが通っていることを予め確認してください。
  - `npm run test`、`npm run lint`でぞれぞれ実施可能です
- `npm run api`を実行してAPIレポートを更新し、差分がある場合はコミットしてください。
ご協力ありがとうございます🤗
-->
<!-- ℹ README
Thank you for your PR! Before creating a PR, please check the following:
- If possible, prefix the title with a keyword that identifies the type of this PR, as shown below.
  - fix / refactor / feat / enhance / perf / chore
  - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR.
- If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text.
- Please add the summary of the changes to CHANGELOG.md. However, this is not necessary for changes that do not affect the users, such as refactoring.
- Check if there are any documents that need to be created or updated due to this change.
- If you have added a feature or fixed a bug, please add a test case if possible.
- Please make sure that tests and Lint are passed in advance.
  - You can run it with `npm run test` and `npm run lint`.
- Run `npm run api` to update the API report and commit it if there are any diffs.
Thanks for your cooperation 🤗
-->

# What
resolve #243 
- ` @Math:ceil(x: num): num ` で小数点以下を切り上げできるように
- CHANGELOG.mdの更新
<!-- このPRで何をしたのか？ どう変わるのか？ -->
<!-- What did you do with this PR? How will it change things? -->

# Why
 ` @Math:ceil(x: num): num ` に相当するライブラリがない
<!-- なぜそうするのか？ どういう意図なのか？ 何が困っているのか？ -->
<!-- Why do you do it? What are your intentions? What is the problem? -->

# Additional info (optional)
<!-- テスト観点など -->
<!-- Test perspective, etc -->

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 882a65b..422cef1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 [Read translated version (en)](./translations/en/CHANGELOG.md)
 
 - 配列プロパティ`map`,`filter`,`reduce`,`find`に渡すコールバック関数が受け取るインデックスを0始まりに
+- `@Math:ceil(x: num): num` を追加
 - 冪乗の `Core:pow` とその糖衣構文 `^`
 
 # 0.12.4
diff --git a/docs/std.md b/docs/std.md
index c333106..627bcd0 100644
--- a/docs/std.md
+++ b/docs/std.md
@@ -77,8 +77,13 @@ JSONをパースします。
 平方根を計算します。  
 
 ### @Math:round(_x_: num): num
+四捨五入して、もっとも近い整数を返します。
+
+### @Math:ceil(_x_: num): num
+引数以上の最小の整数を返します。
 
 ### @Math:floor(_x_: num): num
+引数以下の最大の整数を返します。
 
 ### @Math:min(_a_: num, _b_: num): num
 小さい方の値を取得します。  
diff --git a/src/interpreter/lib/std.ts b/src/interpreter/lib/std.ts
index beaf579..07df18b 100644
--- a/src/interpreter/lib/std.ts
+++ b/src/interpreter/lib/std.ts
@@ -218,6 +218,11 @@ export const std: Record<string, Value> = {
 		return NUM(Math.round(v.value));
 	}),
 
+	'Math:ceil': FN_NATIVE(([v]) => {
+		assertNumber(v);
+		return NUM(Math.ceil(v.value));
+	}),
+
 	'Math:floor': FN_NATIVE(([v]) => {
 		assertNumber(v);
 		return NUM(Math.floor(v.value));
diff --git a/translations/en/docs/std.md b/translations/en/docs/std.md
index 33b8b33..c90ee57 100644
--- a/translations/en/docs/std.md
+++ b/translations/en/docs/std.md
@@ -76,6 +76,8 @@ Calculate the square root.
 
 ### @Math:round(x: num): num
 
+### @Math:ceil(x: num): num
+
 ### @Math:floor(x: num): num
 
 ### @Math:min(a: num, b: num): num


Test patch:
diff --git a/test/index.ts b/test/index.ts
index 06c7135..550ef65 100644
--- a/test/index.ts
+++ b/test/index.ts
@@ -1964,14 +1964,14 @@ describe('Attribute', () => {
 		`);
 		assert.equal(nodes.length, 1);
 		node = nodes[0];
-		if (node.type != 'def') assert.fail();
+		if (node.type !== 'def') assert.fail();
 		assert.equal(node.name, 'onRecieved');
 		assert.equal(node.attr.length, 1);
 		// attribute 1
 		attr = node.attr[0];
-		if (attr.type != 'attr') assert.fail();
+		if (attr.type !== 'attr') assert.fail();
 		assert.equal(attr.name, 'Event');
-		if (attr.value.type != 'str') assert.fail();
+		if (attr.value.type !== 'str') assert.fail();
 		assert.equal(attr.value.value, 'Recieved');
 	});
 
@@ -1989,18 +1989,18 @@ describe('Attribute', () => {
 		`);
 		assert.equal(nodes.length, 1);
 		node = nodes[0];
-		if (node.type != 'def') assert.fail();
+		if (node.type !== 'def') assert.fail();
 		assert.equal(node.name, 'createNote');
 		assert.equal(node.attr.length, 3);
 		// attribute 1
 		attr = node.attr[0];
-		if (attr.type != 'attr') assert.fail();
+		if (attr.type !== 'attr') assert.fail();
 		assert.equal(attr.name, 'Endpoint');
-		if (attr.value.type != 'obj') assert.fail();
+		if (attr.value.type !== 'obj') assert.fail();
 		assert.equal(attr.value.value.size, 1);
 		for (const [k, v] of attr.value.value) {
-			if (k == 'path') {
-				if (v.type != 'str') assert.fail();
+			if (k === 'path') {
+				if (v.type !== 'str') assert.fail();
 				assert.equal(v.value, '/notes/create');
 			}
 			else {
@@ -2009,15 +2009,15 @@ describe('Attribute', () => {
 		}
 		// attribute 2
 		attr = node.attr[1];
-		if (attr.type != 'attr') assert.fail();
+		if (attr.type !== 'attr') assert.fail();
 		assert.equal(attr.name, 'Desc');
-		if (attr.value.type != 'str') assert.fail();
+		if (attr.value.type !== 'str') assert.fail();
 		assert.equal(attr.value.value, 'Create a note.');
 		// attribute 3
 		attr = node.attr[2];
-		if (attr.type != 'attr') assert.fail();
+		if (attr.type !== 'attr') assert.fail();
 		assert.equal(attr.name, 'Cat');
-		if (attr.value.type != 'bool') assert.fail();
+		if (attr.value.type !== 'bool') assert.fail();
 		assert.equal(attr.value.value, true);
 	});
 
@@ -2034,14 +2034,14 @@ describe('Attribute', () => {
 		`);
 		assert.equal(nodes.length, 1);
 		node = nodes[0];
-		if (node.type != 'def') assert.fail();
+		if (node.type !== 'def') assert.fail();
 		assert.equal(node.name, 'data');
 		assert.equal(node.attr.length, 1);
 		// attribute 1
 		attr = node.attr[0];
-		assert.ok(attr.type == 'attr');
+		assert.ok(attr.type === 'attr');
 		assert.equal(attr.name, 'serializable');
-		if (attr.value.type != 'bool') assert.fail();
+		if (attr.value.type !== 'bool') assert.fail();
 		assert.equal(attr.value.value, true);
 	});
 });
@@ -2349,6 +2349,68 @@ describe('std', () => {
 
 	describe('Arr', () => {
 	});
+	
+	describe('Math', () => {
+		test.concurrent('trig', async () => {
+			eq(await exe("<: Math:sin(Math:PI / 2)"), NUM(1));
+			eq(await exe("<: Math:sin(0 - (Math:PI / 2))"), NUM(-1));
+			eq(await exe("<: Math:sin(Math:PI / 4) * Math:cos(Math:PI / 4)"), NUM(0.5));
+		});
+
+		test.concurrent('abs', async () => {
+			eq(await exe("<: Math:abs(1 - 6)"), NUM(5));
+		});
+
+		test.concurrent('sqrt', async () => {
+			eq(await exe("<: Math:sqrt(3*3 + 4*4)"), NUM(5));
+		});
+
+		test.concurrent('round', async () => {
+			eq(await exe("<: Math:round(3.14)"), NUM(3));
+			eq(await exe("<: Math:round(-1.414213)"), NUM(-1));
+		});
+
+		test.concurrent('ceil', async () => {
+			eq(await exe("<: Math:ceil(2.71828)"), NUM(3));
+			eq(await exe("<: Math:ceil(0 - Math:PI)"), NUM(-3));
+			eq(await exe("<: Math:ceil(1 / Math:Infinity)"), NUM(0));
+		});
+
+		test.concurrent('floor', async () => {
+			eq(await exe("<: Math:floor(23.14069)"), NUM(23));
+			eq(await exe("<: Math:floor(Math:Infinity / 0)"), NUM(Infinity));
+		});
+
+		test.concurrent('min', async () => {
+			eq(await exe("<: Math:min(2, 3)"), NUM(2));
+		});
+
+		test.concurrent('max', async () => {
+			eq(await exe("<: Math:max(-2, -3)"), NUM(-2));
+		});
+		
+		test.concurrent('rnd', async () => {
+			eq(await exe("<: if (0 <= Math:rnd() && Math:rnd() <= 1) {true}"), BOOL(true));
+		});
+
+		test.concurrent('rnd with arg', async () => {
+			eq(await exe("<: Math:rnd(1, 1.5)"), NUM(1));
+		});
+
+		test.concurrent('gen_rng', async () => {
+			const res = await exe(`
+			@test(seed) {
+				let random = Math:gen_rng(seed)
+				return random(0 100)
+			}
+			let test1 = test("にゃんぷっぷー")
+			let test2 = test("にゃんぷっぷー")
+			let test3 = test("わんぷっぷー")
+			<: [test1 test2 test3]
+			`)
+			eq(res, ARR([NUM(49), NUM(49) ,NUM(25)]));
+		});
+	});
 
 	describe('Obj', () => {
 		test.concurrent('keys', async () => {