diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml new file mode 100644 index 0000000..5835711 --- /dev/null +++ b/.github/workflows/cd.yaml @@ -0,0 +1,22 @@ +name: Build and Deploy + +on: [push] + +jobs: + build-and-deploy: + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + + - name: Install and Build 🔧 + run: | + mkdir output + cp readme.md output/ + echo theme: jekyll-theme-cayman >> output/_config.yml + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@v4.2.5 + with: + branch: gh-pages + folder: output diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1e8b215..1b560dd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,16 +1,44 @@ name: CI -on: push +on: [push, pull_request] jobs: - ci: - name: Lint and check format. - runs-on: ubuntu-latest + build: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + fail-fast: true steps: - - uses: actions/checkout@v3 - - uses: denoland/setup-deno@v1 + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Deno + uses: denoland/setup-deno@v1 with: deno-version: v1.x - - run: deno lint - - run: deno fmt --check - - run: deno test + + - name: Check Code Format + if: matrix.os == 'ubuntu-latest' + run: deno fmt --check + + - name: Lint + if: matrix.os == 'ubuntu-latest' + run: deno lint + + - name: Bundle + run: deno bundle mod.ts mod.bundle.js + + - name: Tests + run: deno test --coverage=./cov + + - name: Generate Coverage + if: matrix.os == 'ubuntu-latest' + run: deno coverage --unstable --lcov ./cov > cov.lcov + + - name: Upload Coverage + if: matrix.os == 'ubuntu-latest' + uses: codecov/codecov-action@v2 + with: + files: cov.lcov diff --git a/.github/workflows/deps.yaml b/.github/workflows/deps.yaml new file mode 100644 index 0000000..d0fcb57 --- /dev/null +++ b/.github/workflows/deps.yaml @@ -0,0 +1,29 @@ +name: update-deno-dependencies + +on: + workflow_dispatch: + schedule: + - cron: "0 0 1 * *" + +jobs: + udd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: denoland/setup-deno@v1 + - name: Update dependencies + run: > + deno run -A https://deno.land/x/udd/main.ts + $(find . -name "*.ts") + - name: Create Pull Request + uses: peter-evans/create-pull-request@v3 + with: + commit-message: "⬆️ Update dependencies" + title: Update Deno Dependencies + body: > + Automated updates by [deno-udd](https://github.com/hayd/deno-udd) and the + [create-pull-request](https://github.com/peter-evans/create-pull-request) + action. + branch: update-deno-dependencies + author: GitHub + delete-branch: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be06a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node/ diff --git a/deps.ts b/deps.ts new file mode 100644 index 0000000..c8f544a --- /dev/null +++ b/deps.ts @@ -0,0 +1,7 @@ +export { + assert, + assertEquals, + assertFalse, + assertNotEquals, + assertThrows, +} from "https://deno.land/std@0.175.0/testing/asserts.ts"; diff --git a/mod.ts b/mod.ts index 0f516ef..51ba807 100644 --- a/mod.ts +++ b/mod.ts @@ -1,3 +1,2 @@ -export * from "./src/browser/mod.ts"; -export * from "./src/language/mod.ts"; -export * from "./src/node/mod.ts"; +export * from "./src/program_structure/mod.ts"; +export * from "./src/functions/mod.ts"; diff --git a/readme.md b/readme.md index 1a04b87..3dd65f8 100644 --- a/readme.md +++ b/readme.md @@ -1,15 +1,93 @@ # Eloquent JavaScript -[Read the book for free!](https://eloquentjavascript.net) 📖 +[![Code Coverage](https://codecov.io/gh/ultirequiem/eloquent-javascript/branch/main/graph/badge.svg)](https://codecov.io/gh/ultirequiem/eloquent-javascript) +[![Deno Doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/eloquent/mod.ts) -I solved the exercises using TypeScript and Test Driven Development. +**[Read the book for free!](https://eloquentjavascript.net) 📖** -> [A cool TDD Tutorial](https://github.com/dwyl/learn-tdd) +I solved the exercises using [TypeScript](https://typescriptlang.org) and +[Test Driven Development](https://github.com/dwyl/learn-tdd). + +### Language + +#### [Program Structure](https://eloquentjavascript.net/02_program_structure.html) + +- [Looping a Triangle](https://eloquentjavascript.net/02_program_structure.html#i_umoXp9u0e7) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/triangle.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/triangle.test.ts) + +- [FizzBuzz](https://eloquentjavascript.net/02_program_structure.html#i_rebKE3gdjV) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/fizzbuzz.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/fizzbuzz.test.ts) + +- [Chessboard](https://eloquentjavascript.net/02_program_structure.html#i_swb9JBtSQQ) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/chessboard.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/chessboard.test.ts) + +#### [Functions](https://eloquentjavascript.net/03_functions.html) + +- [Minimum](https://eloquentjavascript.net/03_functions.html#i_XTmO7z7MPq) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/minimum.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/minimum.test.ts) + +- [Recursion](https://eloquentjavascript.net/03_functions.html#i_jxl1p970Fy) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/is_even.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/is_even.test.ts) + +- [Bean Counting](https://eloquentjavascript.net/03_functions.html#i_3rsiDgC2do) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/bean_counting.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/bean_counting.test.ts) + +#### [Data Structures](https://eloquentjavascript.net/04_data.html) + +- [Range](https://eloquentjavascript.net/04_data.html#i_8ZspxiCEC) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/range.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/range.test.ts) + +- [Reversing an Array](https://eloquentjavascript.net/04_data.html#i_6xTmjj4Rf5) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/reversing_array.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/reversing_array.test.ts) + +- [List](https://eloquentjavascript.net/04_data.html#i_nSTX34CM1M) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/list.test.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/list.test.ts) + +- [Deep Equal](https://eloquentjavascript.net/04_data.html#i_IJBU+aXOIC) - + [Solution](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/deep_equal.ts) - + [Test](https://github.com/UltiRequiem/eloquent-javascript/blob/main/src/language/deep_equal.test.ts) + +## Usage + +The vast majority of solutions are completely reusable functions, so I decided +to package them. + +The API is the same on all this platforms ✔️ + +### [Deno 🦕](https://deno.land/x/eloq) + +```javascript +import * as eloq from "https://deno.land/x/eloq/mod.ts"; +``` + +### [Node.js 🐢🚀](https://npmjs.com/package/eloq) + +```javascript +import * as eloq from "eloq"; +``` + +### [Browser 🌐](https://developer.mozilla.org/en-US/docs/Glossary/Browser) + +You can use any [CDN](https://en.wikipedia.org/wiki/Content_delivery_network) 🔥 + +Eg 👉 +[ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) ↔️ +[SkyPack](https://cdn.skypack.dev/eloq) 🆚 +[Script Tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) +↔️ [JSDelivr](https://cdn.jsdelivr.net/npm/eloq) ## Documentation -Is hosted on -[Deno Doc](https://doc.deno.land/https://deno.land/x/eloquent/mod.ts) 📄 +Is hosted on [Deno Doc](https://doc.deno.land/https://deno.land/x/eloq/mod.ts) 📄 ## Support @@ -26,15 +104,15 @@ Don't forget to start the repo ⭐ ## Versioning We use [Semantic Versioning](http://semver.org). For the versions available, see -the [tags](https://github.com/UltiRequiem/eloquent/tags) 🏷️ +the [tags](https://github.com/UltiRequiem/eloquent-javascript/tags) 🏷️ ## Authors [Eliaz Bobadilla](https://ultirequiem.com) - Creator and Maintainer 💪 See also the full list of -[contributors](https://github.com/UltiRequiem/eloquent/contributors) who -participated in this project ✨ +[contributors](https://github.com/UltiRequiem/eloquent-javascript/contributors) +who participated in this project ✨ ## Licence diff --git a/scripts/node_support.ts b/scripts/node_support.ts index 4e1aafc..4a93ddc 100644 --- a/scripts/node_support.ts +++ b/scripts/node_support.ts @@ -1,8 +1,8 @@ -import { buildPackage } from "https://deno.land/x/ultirequiem@0.0.12/node_support.ts"; +import { buildPackage } from "https://deno.land/x/ultirequiem@0.0.17/node_support.ts"; buildPackage({ repoName: "eloquent-javascript", - package: "eloq", + name: "eloq", description: "Eloquent JavaScript exercises.", homepage: "https://ulti.js.org/eloquent-javascript", keywords: ["book", "course"], diff --git a/src/browser/mod.ts b/src/data_structures/mod.ts similarity index 100% rename from src/browser/mod.ts rename to src/data_structures/mod.ts diff --git a/src/functions/bean_counting.bench.ts b/src/functions/bean_counting.bench.ts new file mode 100644 index 0000000..7700750 --- /dev/null +++ b/src/functions/bean_counting.bench.ts @@ -0,0 +1,24 @@ +import { countChar, countCharCool } from "./bean_counting.ts"; + +const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing +elit. Donec iaculis enim et sem pharetra sagittis. +Nunc semper, neque id volutpat aliquet, sapien diam vehicula augue, +semper efficitur mauris ipsum ut justo. Aenean urna tortor, +tempus vitae sodales non, posuere id mauris. Quisque consectetur, +turpis sed sagittis fermentum, orci magna pharetra sem, vitae vulputate +risus urna vitae eros. Aenean sagittis ipsum dolor, sed iaculis lacus +faucibus sit amet. Proin at suscipit dolor. Sed volutpat ipsum id metus +dictum gravida. Curabitur ut diam ac sapien finibus mattis. + +Vivamus lobortis suscipit lorem, quis rutrum quam molestie quis. +In hac habitasse platea dictumst. Nunc id mi id ipsum consequat +dictum vitae a mi. Quisque sapien est, consequat nec neque aliquet, +porttitor mollis leo.`; + +Deno.bench("[countCharCool]", { group: "Counting" }, () => { + countCharCool(lorem, "q"); +}); + +Deno.bench("[countCharRegex]", { group: "Counting" }, () => { + countChar(lorem, "q"); +}); diff --git a/src/functions/bean_counting.test.ts b/src/functions/bean_counting.test.ts new file mode 100644 index 0000000..a4f8dc0 --- /dev/null +++ b/src/functions/bean_counting.test.ts @@ -0,0 +1,32 @@ +import { + countBs, + countChar, + countCharCool, + counterOcurrencesCreator, +} from "./bean_counting.ts"; +import { assertEquals } from "../../deps.ts"; + +const lorem = `Lorem ipsum dolor sit amet, consectetur adipiscing elit.`; + +Deno.test("[countBs]", () => { + assertEquals(countBs("BBBaB"), 4); + assertEquals(countBs(lorem), 0); +}); + +Deno.test("[countCharCool]", () => { + assertEquals(countCharCool(lorem, "o"), 4); +}); + +Deno.test("[countChar]", () => { + assertEquals(countChar(lorem, "s"), 4); +}); + +Deno.test("[counterOcurrencesCreator]", () => { + const countEs = counterOcurrencesCreator("e"); + + assertEquals(countEs(lorem), 5); +}); + +Deno.test("[countChar vs countCharCool]", () => { + assertEquals(countCharCool(lorem, "c"), countChar(lorem, "c")); +}); diff --git a/src/functions/bean_counting.ts b/src/functions/bean_counting.ts new file mode 100644 index 0000000..c9cea51 --- /dev/null +++ b/src/functions/bean_counting.ts @@ -0,0 +1,17 @@ +export function countCharCool(text: string, charToSearch: string) { + const { length } = [...text].filter((letter) => letter === charToSearch); + return length; +} + +// Almost 9 times faster! +export function countChar(text: string, charToSearch: string) { + const expression = new RegExp(charToSearch, "gi"); + const { length } = text.match(expression) ?? []; + return length; +} + +export function counterOcurrencesCreator(charToSearch: string) { + return (text: string) => countChar(text, charToSearch); +} + +export const countBs = counterOcurrencesCreator("B"); diff --git a/src/functions/is_even.test.ts b/src/functions/is_even.test.ts new file mode 100644 index 0000000..4787630 --- /dev/null +++ b/src/functions/is_even.test.ts @@ -0,0 +1,17 @@ +import { isEven } from "./is_even.ts"; +import { assert, assertFalse, assertThrows } from "../../deps.ts"; + +Deno.test("[isEven]", () => { + assert(isEven(2)); + assertFalse(isEven(11)); + assert(isEven(24)); + assert(isEven(24)); + assert(isEven(50)); + assertFalse(isEven(75)); + assertFalse(isEven(75)); + + assertThrows(() => isEven(-3)); + + // @ts-expect-error Should throw + assertThrows(() => isEven("a")); +}); diff --git a/src/functions/is_even.ts b/src/functions/is_even.ts new file mode 100644 index 0000000..894d32a --- /dev/null +++ b/src/functions/is_even.ts @@ -0,0 +1,17 @@ +export function isEven(numberToTest: number) { + if (!Number.isInteger(numberToTest) || numberToTest <= -1) { + throw new TypeError("Only positive numbers are allowed."); + } + + function isEvenCore(coreNumber: number): boolean { + if (coreNumber === 0) { + return true; + } else if (coreNumber === 1) { + return false; + } + + return isEvenCore(coreNumber - 2); + } + + return isEvenCore(numberToTest); +} diff --git a/src/functions/minimum.test.ts b/src/functions/minimum.test.ts new file mode 100644 index 0000000..8b44af7 --- /dev/null +++ b/src/functions/minimum.test.ts @@ -0,0 +1,16 @@ +import { min, minRecursive } from "./minimum.ts"; + +import { assertEquals } from "../../deps.ts"; + +Deno.test("[min]", () => { + assertEquals(min(5, 3, 4, 2, 22), 2); + + assertEquals(min(5, 3, 4, 2, 22, -3), -3); + assertEquals(min(-23, 5, 3, 4, 2, 22, -3), -23); +}); + +Deno.test("[minRecursive]", () => { + assertEquals(minRecursive(5, 3, 4, 2, 22), 2); + + assertEquals(minRecursive(5, 3, 4, 2, 22, -3), -3); +}); diff --git a/src/language/minimum.ts b/src/functions/minimum.ts similarity index 100% rename from src/language/minimum.ts rename to src/functions/minimum.ts diff --git a/src/functions/mod.ts b/src/functions/mod.ts new file mode 100644 index 0000000..1fd8c3c --- /dev/null +++ b/src/functions/mod.ts @@ -0,0 +1,3 @@ +export * from "./minimum.ts"; +export * from "./is_even.ts"; +export * from "./bean_counting.ts"; diff --git a/src/language/array_to_list.ts b/src/language/array_to_list.ts deleted file mode 100644 index aa742f9..0000000 --- a/src/language/array_to_list.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface List { - value: T; - rest: List | null; -} - -export function arrayToList([value, ...values]: T[]): List | null { - return value ? { value, rest: arrayToList(values) } : null; -} - -export function arrayToListReduce(values: T[]) { - // it would enter on one line if this were plain JS 🥲 - return values.reduceRight | null>( - (rest, value) => ({ value, rest }), - null, - ); -} - -export function arrayToListFor(values: T[]) { - let rest = {}; - - for (let i = values.length - 1; i >= 0; i--) { - rest = { value: values[i], rest }; - } - - return rest as List; -} diff --git a/src/language/average.bench.ts b/src/language/average.bench.ts new file mode 100644 index 0000000..4790f70 --- /dev/null +++ b/src/language/average.bench.ts @@ -0,0 +1,11 @@ +import { average, averageIterative } from "./average.ts"; + +const BENCH_DATA = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + +Deno.bench("Reduce", { group: "average" }, () => { + average(BENCH_DATA); +}); + +Deno.bench("Iterative", { group: "average" }, () => { + averageIterative(BENCH_DATA); +}); diff --git a/src/language/average.ts b/src/language/average.ts new file mode 100644 index 0000000..bd5c353 --- /dev/null +++ b/src/language/average.ts @@ -0,0 +1,13 @@ +export function average(array: readonly number[]) { + return array.reduce((a, b) => a + b) / array.length; +} + +export function averageIterative(array: readonly number[]) { + let count = 0; + + for (let index = 0; index < count; index++) { + count += array[index]; + } + + return count / array.length; +} diff --git a/src/language/bean_counting.test.ts b/src/language/bean_counting.test.ts deleted file mode 100644 index 6fed66f..0000000 --- a/src/language/bean_counting.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { - countBs, - countChar, - counterOcurrencesCreator, -} from "./bean_counting.ts"; -import { assert } from "./testing.ts"; - -Deno.test("[countBs]", () => { - assert(countBs("BBBaB") === 4); - assert(countBs("111aaa") === 0); -}); - -Deno.test("[countChar]", () => { - assert(countChar("BBBa", "B") === 3); -}); - -Deno.test("[counterOcurrencesCreator]", () => { - const countEs = counterOcurrencesCreator("E"); - - assert(countEs("EEEEaaa33") == 4); -}); diff --git a/src/language/bean_counting.ts b/src/language/bean_counting.ts deleted file mode 100644 index 896444f..0000000 --- a/src/language/bean_counting.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function countChar(text: string, charToSearch: string) { - const occurences = [...text].filter((letter) => letter === charToSearch); - return occurences.length; -} - -export function counterOcurrencesCreator(charToSearch: string) { - return (text: string) => countChar(text, charToSearch); -} - -export const countBs = counterOcurrencesCreator("B"); diff --git a/src/language/deep_equal.test.ts b/src/language/deep_equal.test.ts new file mode 100644 index 0000000..668acce --- /dev/null +++ b/src/language/deep_equal.test.ts @@ -0,0 +1,26 @@ +import { deepEqual } from "./deep_equal.ts"; +import { assert, assertFalse } from "../../deps.ts"; + +Deno.test("deepEqual", () => { + const obj = { here: { is: "an" }, object: 2 }; + + assert(deepEqual(obj, obj)); + + assert(deepEqual([1, 2, 3], [1, 2, 3])); + + assertFalse(deepEqual([1, 2, 3], [1, 3])); + + assertFalse(deepEqual([2, 1], [1, 2])); + + assert(deepEqual("a", "a")); + + assertFalse(deepEqual(23, 34)); + + assertFalse(deepEqual(obj, { here: 1, object: 2 })); + + assert(deepEqual(obj, { here: { is: "an" }, object: 2 })); + + assertFalse(deepEqual({ a: "b" }, { c: "d" })); + + assertFalse(deepEqual({ hey: "rosa", chales: "diah" }, { me: "eliaz" })); +}); diff --git a/src/language/deep_equal.ts b/src/language/deep_equal.ts new file mode 100644 index 0000000..860597b --- /dev/null +++ b/src/language/deep_equal.ts @@ -0,0 +1,37 @@ +// deno-lint-ignore-file no-explicit-any + +export function deepEqual(first: any, second: any) { + const [firstType, secondType] = [typeof first, typeof second]; + + const [firstObject, secondObject] = [ + firstType === "object", + secondType === "object", + ]; + + const [firstIsObject, secondIsObject] = [ + firstObject && first !== null, + secondObject && second !== null, + ]; + + if (!firstIsObject || !secondIsObject) { + return first === second; + } + + const [firstKeys, secondKeys] = [first, second].map(Object.keys); + + if (firstKeys.length !== secondKeys.length) { + return false; + } + + for (const key of firstKeys) { + if (!secondKeys.includes(key)) { + return false; + } + + if (!deepEqual(first[key], second[key])) { + return false; + } + } + + return true; +} diff --git a/src/language/filter.test.ts b/src/language/filter.test.ts new file mode 100644 index 0000000..9a3dd1d --- /dev/null +++ b/src/language/filter.test.ts @@ -0,0 +1,31 @@ +import { filter } from "./filter.ts"; +import { assertEquals } from "../../deps.ts"; + +const data = [1, 2, 3, 4, 5]; + +const cb = (item: number) => item % 2 === 0; + +Deno.test("[filter] Main", () => { + const result = filter(data, cb); + + assertEquals(result, [2, 4]); +}); + +Deno.test("[filter] Object", () => { + const example = [{ hey: true, value: "x" }, { hey: false }]; + + const cb = (item: typeof example[number]) => item.hey; + + const result = filter(example, cb); + const expected = example.filter(cb); + + assertEquals(result, [{ hey: true, value: "x" }]); + assertEquals(result, expected); +}); + +Deno.test("[filter] Versus Array.filter", () => { + const expected = data.filter(cb); + const result = filter(data, cb); + + assertEquals(result, expected); +}); diff --git a/src/language/filter.ts b/src/language/filter.ts new file mode 100644 index 0000000..41c5bcf --- /dev/null +++ b/src/language/filter.ts @@ -0,0 +1,19 @@ +export type FilterCallback = ( + item: T, + index: number, + array: readonly T[], +) => unknown; + +export function filter(array: readonly T[], test: FilterCallback) { + const result = []; + + const entries = array.entries(); + + for (const [index, value] of entries) { + if (test(value, index, array)) { + result.push(value); + } + } + + return result; +} diff --git a/src/language/for_each.ts b/src/language/for_each.ts new file mode 100644 index 0000000..422e5f3 --- /dev/null +++ b/src/language/for_each.ts @@ -0,0 +1,13 @@ +export type ForEachCallback = ( + element: T, + index: number, + array: readonly T[], +) => void; + +export function forEach(array: T[], callback: ForEachCallback) { + const entries = array.entries(); + + for (const [index, value] of entries) { + callback(value, index, array); + } +} diff --git a/src/language/greater_than.ts b/src/language/greater_than.ts new file mode 100644 index 0000000..46823e7 --- /dev/null +++ b/src/language/greater_than.ts @@ -0,0 +1,22 @@ +export function greaterThan(base: number) { + return (toTest: number) => toTest > base; +} + +export function greaterThanOrEqual(base: number) { + return (toTest: number) => toTest >= base; +} + +export function greaterThanParameter(toTest: number, base: number) { + if (!greaterThanParameter.checkers.has(base)) { + greaterThanParameter.checkers.set(base, greaterThan(base)); + } + + const checker = greaterThanParameter.checkers.get(base)!; + + return checker(toTest); +} + +greaterThanParameter.checkers = new Map< + number, + ReturnType +>(); diff --git a/src/language/is_even.test.ts b/src/language/is_even.test.ts deleted file mode 100644 index db0bd54..0000000 --- a/src/language/is_even.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { isEven } from "./is_even.ts"; -import { assert } from "./testing.ts"; - -Deno.test("[isEven]", () => { - assert(isEven(2)); - assert(!isEven(11)); - assert(isEven(24)); - assert(isEven(24)); - assert(isEven(50)); - assert(!isEven(75)); -}); diff --git a/src/language/is_even.ts b/src/language/is_even.ts deleted file mode 100644 index 17114b1..0000000 --- a/src/language/is_even.ts +++ /dev/null @@ -1,15 +0,0 @@ -export function isEven(numberToTest: number): boolean { - if (!Number.isInteger(numberToTest) || numberToTest <= -1) { - throw new Error("Only positive numbers are allowed."); - } - - if (numberToTest === 0) { - return true; - } - - if (numberToTest == 1) { - return false; - } - - return isEven(numberToTest - 2); -} diff --git a/src/language/list.test.ts b/src/language/list.test.ts new file mode 100644 index 0000000..652fae4 --- /dev/null +++ b/src/language/list.test.ts @@ -0,0 +1,48 @@ +import { assertEquals, assertThrows } from "../../deps.ts"; +import { + arrayToList, + arrayToListFor, + arrayToListReduce, + nth, + prepend, +} from "./list.ts"; + +const rest = { + value: 1, + rest: { + value: 2, + rest: { + value: 3, + rest: null, + }, + }, +}; + +Deno.test("[arrayToList]", () => { + assertEquals(arrayToList([1, 2, 3]), rest); +}); + +Deno.test("[arrayToListFor]", () => { + assertEquals(arrayToListFor([1, 2, 3]), rest); +}); + +Deno.test("[arrayToListReduce]", () => { + assertEquals(arrayToListReduce([1, 2, 3]), rest); +}); + +Deno.test("[nth]", () => { + const nth3 = nth(rest, 2); + assertEquals(nth3, 3); + + assertThrows( + () => { + nth(rest, 5); + }, + RangeError, + "Index out of range", + ); +}); + +Deno.test("[prepend]", () => { + assertEquals(prepend(4, rest), { value: 4, rest }); +}); diff --git a/src/language/list.ts b/src/language/list.ts new file mode 100644 index 0000000..1062584 --- /dev/null +++ b/src/language/list.ts @@ -0,0 +1,44 @@ +export interface List { + value: T; + rest: List | null; +} + +export function arrayToList([value, ...values]: T[]): List | null { + return value ? { value, rest: arrayToList(values) } : null; +} + +export function arrayToListReduce(values: T[]) { + // it would enter on one line if this were plain JS + return values.reduceRight | null>( + (rest, value) => ({ value, rest }), + null, + ); +} + +export function arrayToListFor(values: T[]) { + let rest: List | null = null; + + const reversedItems = values.reverse(); + + for (const value of reversedItems) { + rest = { value, rest }; + } + + return rest; +} + +export function prepend(value: T, rest: List): List { + return { value, rest }; +} + +export function nth(list: List, index: number): T { + if (index === 0) { + return list.value; + } + + if (list.rest === null) { + throw new RangeError("Index out of range."); + } + + return nth(list.rest, index - 1); +} diff --git a/src/language/map.test.ts b/src/language/map.test.ts new file mode 100644 index 0000000..359c103 --- /dev/null +++ b/src/language/map.test.ts @@ -0,0 +1,20 @@ +import { map } from "./map.ts"; +import { assert, assertEquals } from "../../deps.ts"; + +const TEST_DATA = [1, 2, 3, 4, 5]; + +Deno.test("[map] Versus Array.map", () => { + const result = map(TEST_DATA, (x) => x * 2); + const expected = TEST_DATA.map((x) => x * 2); + + assertEquals(result, expected); +}); + +Deno.test("[map] Chaning Types", () => { + const result = map(TEST_DATA, (x) => `${x}`); + const expected = TEST_DATA.map((x) => `${x}`); + + assert(result.every((x) => typeof x === "string")); + + assertEquals(result, expected); +}); diff --git a/src/language/map.ts b/src/language/map.ts new file mode 100644 index 0000000..58bc1b9 --- /dev/null +++ b/src/language/map.ts @@ -0,0 +1,17 @@ +export type MapCallback = ( + value: T, + index: number, + array: readonly T[], +) => unknown; + +export function map>(array: T[], transform: F) { + const result = []; + + const entries = array.entries(); + + for (const [index, value] of entries) { + result.push(transform(value, index, array)); + } + + return result as ReturnType[]; +} diff --git a/src/language/minimum.test.ts b/src/language/minimum.test.ts deleted file mode 100644 index 9da7b0f..0000000 --- a/src/language/minimum.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { min, minRecursive } from "./minimum.ts"; - -import { assert } from "./testing.ts"; - -Deno.test("[min]", () => { - assert(min(5, 3, 4, 2, 22) === 2); - - assert(min(5, 3, 4, 2, 22, -3) === -3); - assert(min(-23, 5, 3, 4, 2, 22, -3) === -23); -}); - -Deno.test("[minRecursive]", () => { - assert(minRecursive(5, 3, 4, 2, 22) === 2); - - assert(minRecursive(5, 3, 4, 2, 22, -3) === -3); -}); diff --git a/src/language/mod.ts b/src/language/mod.ts index 38b9177..f445378 100644 --- a/src/language/mod.ts +++ b/src/language/mod.ts @@ -1,4 +1,4 @@ -export * from "./array_to_list.ts"; +export * from "./list.ts"; export * from "./bean_counting.ts"; export * from "./minimum.ts"; export * from "./is_even.ts"; diff --git a/src/language/noisy.ts b/src/language/noisy.ts new file mode 100644 index 0000000..708f9b1 --- /dev/null +++ b/src/language/noisy.ts @@ -0,0 +1,15 @@ +// deno-lint-ignore-file no-explicit-any + +export function noisy any>(func: F) { + return (...args: Parameters) => { + const functionName = func.name ?? "the function"; + + console.log(`Calling "${functionName}" with args: ${args}.`); + + const result = func.apply(undefined, args); + + console.log(`Result: ${result}.`); + + return result as ReturnType; + }; +} diff --git a/src/language/range.test.ts b/src/language/range.test.ts index f24a31c..2de0f07 100644 --- a/src/language/range.test.ts +++ b/src/language/range.test.ts @@ -1,29 +1,21 @@ import { range } from "./range.ts"; -import { assert } from "./testing.ts"; +import { assertEquals, assertThrows } from "../../deps.ts"; Deno.test("[range] Main", () => { - const result = range(1, 3); - const expectedResult = [1, 2, 3]; - - for (let i = 0; i < result.length; i++) { - assert(result[i] === expectedResult[i]); - } + assertEquals(range(1, 3), [1, 2, 3]); }); Deno.test("[range] Step", () => { - const result = range(1, 10, 2); - const expectedResult = [1, 3, 5, 7, 9]; - - for (let i = 0; i < result.length; i++) { - assert(result[i] === expectedResult[i]); - } + assertEquals(range(1, 10, 3), [1, 4, 7, 10]); }); Deno.test("[range] Negative numbers", () => { - const result = range(5, 2, -1); - const expectedResult = [5, 4, 3, 2]; + assertEquals(range(5, 2, -1), [5, 4, 3, 2]); +}); - for (let i = 0; i < result.length; i++) { - assert(result[i] === expectedResult[i]); - } +Deno.test("[range] Throws", () => { + assertThrows(() => { + // @ts-expect-error Should throw + range(1, "a", 0); + }); }); diff --git a/src/language/range.ts b/src/language/range.ts index 8b0f63f..59c1986 100644 --- a/src/language/range.ts +++ b/src/language/range.ts @@ -1,22 +1,25 @@ -function areNumbers(values: unknown[]): values is number[] { +function areNumbers(values: readonly unknown[]): values is number[] { return values.every(Number.isInteger); } -export function range(start: number, end: number, step = 1): number[] { - if (!areNumbers([start, end, step])) { - throw new TypeError("Exepected all parameters to be numbers."); +function assertAreNumbers( + values: readonly unknown[], + message = "Expected all the items to be numbers.", +): asserts values is number[] { + if (!areNumbers(values)) { + throw new TypeError(message); } +} + +export function range(start: number, end: number, step = 1): number[] { + assertAreNumbers([start, end, step]); const result: number[] = []; const positiveStep = step >= 1; - for ( - let item = start; - item <= end; - positiveStep ? (item += step) : (item -= step) - ) { - result.push(item); + for (let i = start; positiveStep ? i <= end : i >= end; i += step) { + result.push(i); } return result; diff --git a/src/language/reduce.ts b/src/language/reduce.ts new file mode 100644 index 0000000..e0d344f --- /dev/null +++ b/src/language/reduce.ts @@ -0,0 +1,30 @@ +// I don't like TypeScript Anymore, my mood: 😔 -> 😩 -> 😭 + +export type ReduceCallback = ( + accumulator: U, + value: T, + index: number, + array: readonly T[], +) => unknown; + +export function reduce< + T extends unknown[], + F extends ReduceCallback, + U = unknown, +>(array: readonly T[], callback: F, initialValue?: U | T) { + const copy = [...array]; + + if (array.length >= 1 && initialValue === undefined) { + initialValue = copy.shift(); + } + + let previous = initialValue; + + const entries = copy.entries(); + + for (const [index, element] of entries) { + previous = callback(previous as T & U, element, index, array) as U; + } + + return previous as ReturnType; +} diff --git a/src/language/regex.ts b/src/language/regex.ts new file mode 100644 index 0000000..416e0f4 --- /dev/null +++ b/src/language/regex.ts @@ -0,0 +1 @@ +export const isDigit = /^\d+/; diff --git a/src/language/repeat.ts b/src/language/repeat.ts new file mode 100644 index 0000000..323e3f8 --- /dev/null +++ b/src/language/repeat.ts @@ -0,0 +1,16 @@ +// deno-lint-ignore-file no-explicit-any + +export function repeat any>( + times: number, + action: T, +) { + const results = []; + + for (let i = 0; i < times; i++) { + const result = action(i); + + results.push(result as ReturnType); + } + + return results; +} diff --git a/src/language/reversing_array.test.ts b/src/language/reversing_array.test.ts index afba549..e3a323f 100644 --- a/src/language/reversing_array.test.ts +++ b/src/language/reversing_array.test.ts @@ -1,20 +1,18 @@ import { reverseArray, reverseArrayInPlace } from "./reversing_array.ts"; -import { assert } from "./testing.ts"; +import { assertEquals, assertNotEquals } from "../../deps.ts"; Deno.test("[reverseArray]", () => { const exampleArray = [1, 2, 3, 4, 5]; const inversedArray = reverseArray(exampleArray); // It returns a new copy - assert(exampleArray !== inversedArray); + assertNotEquals(exampleArray, inversedArray); const stdReversed = [...exampleArray].reverse(); - assert(inversedArray.length === stdReversed.length); + assertEquals(inversedArray.length, stdReversed.length); - for (let i = 0; i < inversedArray.length; i++) { - assert(inversedArray[i] === stdReversed[i]); - } + assertEquals(inversedArray, stdReversed); }); Deno.test("[reverseArrayInPlace]", () => { @@ -23,11 +21,9 @@ Deno.test("[reverseArrayInPlace]", () => { const result = reverseArrayInPlace(exampleArray); // It's same because it mutates the same array and then returns it - assert(exampleArray === result); + assertEquals(exampleArray, result); - assert(exampleArray.length === result.length); + assertEquals(exampleArray.length, result.length); - for (let i = 0; i < result.length; i++) { - assert(result[i] === expectedResult[i]); - } + assertEquals(result, expectedResult); }); diff --git a/src/language/reversing_array.ts b/src/language/reversing_array.ts index c24309a..8cda7f9 100644 --- a/src/language/reversing_array.ts +++ b/src/language/reversing_array.ts @@ -1,4 +1,4 @@ -export function reverseArray(array: T[]) { +export function reverseArray(array: readonly T[]) { const result = []; for (let i = array.length - 1; i >= 0; i--) { diff --git a/src/language/scripts.json b/src/language/scripts.json new file mode 100644 index 0000000..6b65c1d --- /dev/null +++ b/src/language/scripts.json @@ -0,0 +1,1874 @@ +[ + { + "name": "Adlam", + "ranges": [ + [125184, 125259], + [125264, 125274], + [125278, 125280] + ], + "direction": "rtl", + "year": 1987, + "living": true, + "link": "https://en.wikipedia.org/wiki/Fula_alphabets#Adlam_alphabet" + }, + { + "name": "Caucasian Albanian", + "ranges": [ + [66864, 66916], + [66927, 66928] + ], + "direction": "ltr", + "year": 420, + "living": false, + "link": "https://en.wikipedia.org/wiki/Caucasian_Albanian_alphabet" + }, + { + "name": "Ahom", + "ranges": [ + [71424, 71450], + [71453, 71468], + [71472, 71488] + ], + "direction": "ltr", + "year": 1250, + "living": false, + "link": "https://en.wikipedia.org/wiki/Ahom_alphabet" + }, + { + "name": "Arabic", + "ranges": [ + [1536, 1541], + [1542, 1548], + [1549, 1563], + [1564, 1565], + [1566, 1567], + [1568, 1600], + [1601, 1611], + [1622, 1648], + [1649, 1757], + [1758, 1792], + [1872, 1920], + [2208, 2229], + [2230, 2238], + [2260, 2274], + [2275, 2304], + [64336, 64450], + [64467, 64830], + [64848, 64912], + [64914, 64968], + [65008, 65022], + [65136, 65141], + [65142, 65277], + [69216, 69247], + [126464, 126468], + [126469, 126496], + [126497, 126499], + [126500, 126501], + [126503, 126504], + [126505, 126515], + [126516, 126520], + [126521, 126522], + [126523, 126524], + [126530, 126531], + [126535, 126536], + [126537, 126538], + [126539, 126540], + [126541, 126544], + [126545, 126547], + [126548, 126549], + [126551, 126552], + [126553, 126554], + [126555, 126556], + [126557, 126558], + [126559, 126560], + [126561, 126563], + [126564, 126565], + [126567, 126571], + [126572, 126579], + [126580, 126584], + [126585, 126589], + [126590, 126591], + [126592, 126602], + [126603, 126620], + [126625, 126628], + [126629, 126634], + [126635, 126652], + [126704, 126706] + ], + "direction": "rtl", + "year": 400, + "living": true, + "link": "https://en.wikipedia.org/wiki/Arabic_script" + }, + { + "name": "Imperial Aramaic", + "ranges": [ + [67648, 67670], + [67671, 67680] + ], + "direction": "rtl", + "year": 800, + "living": false, + "link": "https://en.wikipedia.org/wiki/Aramaic_alphabet" + }, + { + "name": "Armenian", + "ranges": [ + [1329, 1367], + [1369, 1376], + [1377, 1416], + [1418, 1419], + [1421, 1424], + [64275, 64280] + ], + "direction": "ltr", + "year": 405, + "living": true, + "link": "https://en.wikipedia.org/wiki/Armenian_alphabet" + }, + { + "name": "Avestan", + "ranges": [ + [68352, 68406], + [68409, 68416] + ], + "direction": "rtl", + "year": 400, + "living": false, + "link": "https://en.wikipedia.org/wiki/Avestan_alphabet" + }, + { + "name": "Balinese", + "ranges": [ + [6912, 6988], + [6992, 7037] + ], + "direction": "ltr", + "year": 1000, + "living": true, + "link": "https://en.wikipedia.org/wiki/Balinese_script" + }, + { + "name": "Bamum", + "ranges": [ + [42656, 42744], + [92160, 92729] + ], + "direction": "ltr", + "year": 1896, + "living": true, + "link": "https://en.wikipedia.org/wiki/Bamum_script" + }, + { + "name": "Bassa Vah", + "ranges": [ + [92880, 92910], + [92912, 92918] + ], + "direction": "ltr", + "year": 1950, + "living": false, + "link": "https://en.wikipedia.org/wiki/Bassa_alphabet" + }, + { + "name": "Batak", + "ranges": [ + [7104, 7156], + [7164, 7168] + ], + "direction": "ltr", + "year": 1300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Batak_alphabet" + }, + { + "name": "Bengali", + "ranges": [ + [2432, 2436], + [2437, 2445], + [2447, 2449], + [2451, 2473], + [2474, 2481], + [2482, 2483], + [2486, 2490], + [2492, 2501], + [2503, 2505], + [2507, 2511], + [2519, 2520], + [2524, 2526], + [2527, 2532], + [2534, 2558] + ], + "direction": "ltr", + "year": 1050, + "living": true, + "link": "https://en.wikipedia.org/wiki/Bengali_alphabet" + }, + { + "name": "Bhaiksuki", + "ranges": [ + [72704, 72713], + [72714, 72759], + [72760, 72774], + [72784, 72813] + ], + "direction": "ltr", + "year": 1050, + "living": false, + "link": "https://en.wikipedia.org/wiki/Bhaiksuki_alphabet" + }, + { + "name": "Bopomofo", + "ranges": [ + [746, 748], + [12549, 12591], + [12704, 12731] + ], + "direction": "ltr", + "year": 1918, + "living": true, + "link": "https://en.wikipedia.org/wiki/Bopomofo" + }, + { + "name": "Brahmi", + "ranges": [ + [69632, 69710], + [69714, 69744], + [69759, 69760] + ], + "direction": "ltr", + "year": -250, + "living": false, + "link": "https://en.wikipedia.org/wiki/Brahmi_script" + }, + { + "name": "Braille", + "ranges": [[10240, 10496]], + "direction": "ltr", + "year": 1824, + "living": true, + "link": "https://en.wikipedia.org/wiki/Braille" + }, + { + "name": "Buginese", + "ranges": [ + [6656, 6684], + [6686, 6688] + ], + "direction": "ltr", + "year": 1650, + "living": true, + "link": "https://en.wikipedia.org/wiki/Lontara_script" + }, + { + "name": "Buhid", + "ranges": [[5952, 5972]], + "direction": "ltr", + "year": 1300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Buhid_alphabet" + }, + { + "name": "Chakma", + "ranges": [ + [69888, 69941], + [69942, 69956] + ], + "direction": "ltr", + "year": 1050, + "living": true, + "link": "https://en.wikipedia.org/wiki/Chakma_alphabet" + }, + { + "name": "Canadian Aboriginal", + "ranges": [ + [5120, 5760], + [6320, 6390] + ], + "direction": "ltr", + "year": 1840, + "living": true, + "link": "https://en.wikipedia.org/wiki/Canadian_Aboriginal_syllabics" + }, + { + "name": "Carian", + "ranges": [[66208, 66257]], + "direction": "ltr", + "year": -650, + "living": false, + "link": "https://en.wikipedia.org/wiki/Carian_alphabets" + }, + { + "name": "Cham", + "ranges": [ + [43520, 43575], + [43584, 43598], + [43600, 43610], + [43612, 43616] + ], + "direction": "ltr", + "year": 750, + "living": true, + "link": "https://en.wikipedia.org/wiki/Cham_alphabet" + }, + { + "name": "Cherokee", + "ranges": [ + [5024, 5110], + [5112, 5118], + [43888, 43968] + ], + "direction": "ltr", + "year": 1820, + "living": true, + "link": "https://en.wikipedia.org/wiki/Cherokee_syllabary" + }, + { + "name": "Coptic", + "ranges": [ + [994, 1008], + [11392, 11508], + [11513, 11520] + ], + "direction": "ltr", + "year": -200, + "living": false, + "link": "https://en.wikipedia.org/wiki/Coptic_alphabet" + }, + { + "name": "Cypriot", + "ranges": [ + [67584, 67590], + [67592, 67593], + [67594, 67638], + [67639, 67641], + [67644, 67645], + [67647, 67648] + ], + "direction": "rtl", + "year": -1100, + "living": false, + "link": "https://en.wikipedia.org/wiki/Cypriot_syllabary" + }, + { + "name": "Cyrillic", + "ranges": [ + [1024, 1157], + [1159, 1328], + [7296, 7305], + [7467, 7468], + [7544, 7545], + [11744, 11776], + [42560, 42656], + [65070, 65072] + ], + "direction": "ltr", + "year": 950, + "living": true, + "link": "https://en.wikipedia.org/wiki/Cyrillic_script" + }, + { + "name": "Devanagari", + "ranges": [ + [2304, 2385], + [2387, 2404], + [2406, 2432], + [43232, 43262] + ], + "direction": "ltr", + "year": 100, + "living": true, + "link": "https://en.wikipedia.org/wiki/Devanagari" + }, + { + "name": "Deseret", + "ranges": [[66560, 66640]], + "direction": "ltr", + "year": 1854, + "living": true, + "link": "https://en.wikipedia.org/wiki/Deseret_alphabet" + }, + { + "name": "Duployan", + "ranges": [ + [113664, 113771], + [113776, 113789], + [113792, 113801], + [113808, 113818], + [113820, 113824] + ], + "direction": "ltr", + "year": 1860, + "living": true, + "link": "https://en.wikipedia.org/wiki/Duployan_shorthand" + }, + { + "name": "Egyptian Hieroglyphs", + "ranges": [[77824, 78895]], + "direction": "ltr", + "year": -3200, + "living": false, + "link": "https://en.wikipedia.org/wiki/Egyptian_hieroglyphs" + }, + { + "name": "Elbasan", + "ranges": [[66816, 66856]], + "direction": "ltr", + "year": 1750, + "living": false, + "link": "https://en.wikipedia.org/wiki/Elbasan_alphabet" + }, + { + "name": "Ethiopic", + "ranges": [ + [4608, 4681], + [4682, 4686], + [4688, 4695], + [4696, 4697], + [4698, 4702], + [4704, 4745], + [4746, 4750], + [4752, 4785], + [4786, 4790], + [4792, 4799], + [4800, 4801], + [4802, 4806], + [4808, 4823], + [4824, 4881], + [4882, 4886], + [4888, 4955], + [4957, 4989], + [4992, 5018], + [11648, 11671], + [11680, 11687], + [11688, 11695], + [11696, 11703], + [11704, 11711], + [11712, 11719], + [11720, 11727], + [11728, 11735], + [11736, 11743], + [43777, 43783], + [43785, 43791], + [43793, 43799], + [43808, 43815], + [43816, 43823] + ], + "direction": "ltr", + "year": -900, + "living": true, + "link": "https://en.wikipedia.org/wiki/Ge%27ez_script" + }, + { + "name": "Georgian", + "ranges": [ + [4256, 4294], + [4295, 4296], + [4301, 4302], + [4304, 4347], + [4348, 4352], + [11520, 11558], + [11559, 11560], + [11565, 11566] + ], + "direction": "ltr", + "year": 430, + "living": true, + "link": "https://en.wikipedia.org/wiki/Georgian_scripts" + }, + { + "name": "Glagolitic", + "ranges": [ + [11264, 11311], + [11312, 11359], + [122880, 122887], + [122888, 122905], + [122907, 122914], + [122915, 122917], + [122918, 122923] + ], + "direction": "ltr", + "year": 862, + "living": false, + "link": "https://en.wikipedia.org/wiki/Glagolitic_script" + }, + { + "name": "Masaram Gondi", + "ranges": [ + [72960, 72967], + [72968, 72970], + [72971, 73015], + [73018, 73019], + [73020, 73022], + [73023, 73032], + [73040, 73050] + ], + "direction": "ltr", + "year": 1918, + "living": true, + "link": "https://en.wikipedia.org/wiki/Gondi_writing#Masaram" + }, + { + "name": "Gothic", + "ranges": [[66352, 66379]], + "direction": "ltr", + "year": 350, + "living": false, + "link": "https://en.wikipedia.org/wiki/Gothic_alphabet" + }, + { + "name": "Grantha", + "ranges": [ + [70400, 70404], + [70405, 70413], + [70415, 70417], + [70419, 70441], + [70442, 70449], + [70450, 70452], + [70453, 70458], + [70460, 70469], + [70471, 70473], + [70475, 70478], + [70480, 70481], + [70487, 70488], + [70493, 70500], + [70502, 70509], + [70512, 70517] + ], + "direction": "ltr", + "year": 550, + "living": false, + "link": "https://en.wikipedia.org/wiki/Grantha_alphabet" + }, + { + "name": "Greek", + "ranges": [ + [880, 884], + [885, 888], + [890, 894], + [895, 896], + [900, 901], + [902, 903], + [904, 907], + [908, 909], + [910, 930], + [931, 994], + [1008, 1024], + [7462, 7467], + [7517, 7522], + [7526, 7531], + [7615, 7616], + [7936, 7958], + [7960, 7966], + [7968, 8006], + [8008, 8014], + [8016, 8024], + [8025, 8026], + [8027, 8028], + [8029, 8030], + [8031, 8062], + [8064, 8117], + [8118, 8133], + [8134, 8148], + [8150, 8156], + [8157, 8176], + [8178, 8181], + [8182, 8191], + [8486, 8487], + [43877, 43878], + [65856, 65935], + [65952, 65953], + [119296, 119366] + ], + "direction": "ltr", + "year": -800, + "living": true, + "link": "https://en.wikipedia.org/wiki/Greek_alphabet" + }, + { + "name": "Gujarati", + "ranges": [ + [2689, 2692], + [2693, 2702], + [2703, 2706], + [2707, 2729], + [2730, 2737], + [2738, 2740], + [2741, 2746], + [2748, 2758], + [2759, 2762], + [2763, 2766], + [2768, 2769], + [2784, 2788], + [2790, 2802], + [2809, 2816] + ], + "direction": "ltr", + "year": 1592, + "living": true, + "link": "https://en.wikipedia.org/wiki/Gujarati_alphabet" + }, + { + "name": "Gurmukhi", + "ranges": [ + [2561, 2564], + [2565, 2571], + [2575, 2577], + [2579, 2601], + [2602, 2609], + [2610, 2612], + [2613, 2615], + [2616, 2618], + [2620, 2621], + [2622, 2627], + [2631, 2633], + [2635, 2638], + [2641, 2642], + [2649, 2653], + [2654, 2655], + [2662, 2678] + ], + "direction": "ltr", + "year": 1550, + "living": true, + "link": "https://en.wikipedia.org/wiki/Gurmukh%C4%AB_alphabet" + }, + { + "name": "Hangul", + "ranges": [ + [4352, 4608], + [12334, 12336], + [12593, 12687], + [12800, 12831], + [12896, 12927], + [43360, 43389], + [44032, 55204], + [55216, 55239], + [55243, 55292], + [65440, 65471], + [65474, 65480], + [65482, 65488], + [65490, 65496], + [65498, 65501] + ], + "direction": "ltr", + "year": 1443, + "living": true, + "link": "https://en.wikipedia.org/wiki/Hangul" + }, + { + "name": "Han", + "ranges": [ + [11904, 11930], + [11931, 12020], + [12032, 12246], + [12293, 12294], + [12295, 12296], + [12321, 12330], + [12344, 12348], + [13312, 19894], + [19968, 40939], + [63744, 64110], + [64112, 64218], + [131072, 173783], + [173824, 177973], + [177984, 178206], + [178208, 183970], + [183984, 191457], + [194560, 195102] + ], + "direction": "ltr", + "year": -1100, + "living": true, + "link": "https://en.wikipedia.org/wiki/Chinese_characters" + }, + { + "name": "Hanunoo", + "ranges": [[5920, 5941]], + "direction": "ltr", + "year": 1300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Hanun%C3%B3%27o_alphabet" + }, + { + "name": "Hatran", + "ranges": [ + [67808, 67827], + [67828, 67830], + [67835, 67840] + ], + "direction": "rtl", + "year": -40, + "living": false, + "link": "https://en.wikipedia.org/wiki/Hatran_alphabet" + }, + { + "name": "Hebrew", + "ranges": [ + [1425, 1480], + [1488, 1515], + [1520, 1525], + [64285, 64311], + [64312, 64317], + [64318, 64319], + [64320, 64322], + [64323, 64325], + [64326, 64336] + ], + "direction": "rtl", + "year": -100, + "living": true, + "link": "https://en.wikipedia.org/wiki/Hebrew_alphabet" + }, + { + "name": "Hiragana", + "ranges": [ + [12353, 12439], + [12445, 12448], + [110593, 110879], + [127488, 127489] + ], + "direction": "ltr", + "year": 800, + "living": true, + "link": "https://en.wikipedia.org/wiki/Hiragana" + }, + { + "name": "Anatolian Hieroglyphs", + "ranges": [[82944, 83527]], + "direction": "ltr", + "year": -1400, + "living": false, + "link": "https://en.wikipedia.org/wiki/Anatolian_hieroglyphs" + }, + { + "name": "Pahawh Hmong", + "ranges": [ + [92928, 92998], + [93008, 93018], + [93019, 93026], + [93027, 93048], + [93053, 93072] + ], + "direction": "ltr", + "year": 1959, + "living": true, + "link": "https://en.wikipedia.org/wiki/Pahawh_Hmong" + }, + { + "name": "Old Hungarian", + "ranges": [ + [68736, 68787], + [68800, 68851], + [68858, 68864] + ], + "direction": "rtl", + "year": 1150, + "living": false, + "link": "https://en.wikipedia.org/wiki/Old_Hungarian_alphabet" + }, + { + "name": "Old Italic", + "ranges": [ + [66304, 66340], + [66349, 66352] + ], + "direction": "ltr", + "year": -750, + "living": false, + "link": "https://en.wikipedia.org/wiki/Old_Italic_script" + }, + { + "name": "Javanese", + "ranges": [ + [43392, 43470], + [43472, 43482], + [43486, 43488] + ], + "direction": "ltr", + "year": 1250, + "living": true, + "link": "https://en.wikipedia.org/wiki/Javanese_script" + }, + { + "name": "Kayah Li", + "ranges": [ + [43264, 43310], + [43311, 43312] + ], + "direction": "ltr", + "year": 1962, + "living": true, + "link": "https://en.wikipedia.org/wiki/Kayah_Li_alphabet" + }, + { + "name": "Katakana", + "ranges": [ + [12449, 12539], + [12541, 12544], + [12784, 12800], + [13008, 13055], + [13056, 13144], + [65382, 65392], + [65393, 65438], + [110592, 110593] + ], + "direction": "ltr", + "year": 800, + "living": true, + "link": "https://en.wikipedia.org/wiki/Katakana" + }, + { + "name": "Kharoshthi", + "ranges": [ + [68096, 68100], + [68101, 68103], + [68108, 68116], + [68117, 68120], + [68121, 68148], + [68152, 68155], + [68159, 68168], + [68176, 68185] + ], + "direction": "rtl", + "year": -400, + "living": false, + "link": "https://en.wikipedia.org/wiki/Kharosthi" + }, + { + "name": "Khmer", + "ranges": [ + [6016, 6110], + [6112, 6122], + [6128, 6138], + [6624, 6656] + ], + "direction": "ltr", + "year": 611, + "living": true, + "link": "https://en.wikipedia.org/wiki/Khmer_alphabet" + }, + { + "name": "Khojki", + "ranges": [ + [70144, 70162], + [70163, 70207] + ], + "direction": "ltr", + "year": 1520, + "living": false, + "link": "https://en.wikipedia.org/wiki/Khojki_script" + }, + { + "name": "Kannada", + "ranges": [ + [3200, 3204], + [3205, 3213], + [3214, 3217], + [3218, 3241], + [3242, 3252], + [3253, 3258], + [3260, 3269], + [3270, 3273], + [3274, 3278], + [3285, 3287], + [3294, 3295], + [3296, 3300], + [3302, 3312], + [3313, 3315] + ], + "direction": "ltr", + "year": 450, + "living": true, + "link": "https://en.wikipedia.org/wiki/Kannada_alphabet" + }, + { + "name": "Kaithi", + "ranges": [[69760, 69826]], + "direction": "ltr", + "year": 1550, + "living": false, + "link": "https://en.wikipedia.org/wiki/Kaithi" + }, + { + "name": "Tai Tham", + "ranges": [ + [6688, 6751], + [6752, 6781], + [6783, 6794], + [6800, 6810], + [6816, 6830] + ], + "direction": "ltr", + "year": 1300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tai_Tham_alphabet" + }, + { + "name": "Lao", + "ranges": [ + [3713, 3715], + [3716, 3717], + [3719, 3721], + [3722, 3723], + [3725, 3726], + [3732, 3736], + [3737, 3744], + [3745, 3748], + [3749, 3750], + [3751, 3752], + [3754, 3756], + [3757, 3770], + [3771, 3774], + [3776, 3781], + [3782, 3783], + [3784, 3790], + [3792, 3802], + [3804, 3808] + ], + "direction": "ltr", + "year": 1350, + "living": true, + "link": "https://en.wikipedia.org/wiki/Lao_alphabet" + }, + { + "name": "Latin", + "ranges": [ + [65, 91], + [97, 123], + [170, 171], + [186, 187], + [192, 215], + [216, 247], + [248, 697], + [736, 741], + [7424, 7462], + [7468, 7517], + [7522, 7526], + [7531, 7544], + [7545, 7615], + [7680, 7936], + [8305, 8306], + [8319, 8320], + [8336, 8349], + [8490, 8492], + [8498, 8499], + [8526, 8527], + [8544, 8585], + [11360, 11392], + [42786, 42888], + [42891, 42927], + [42928, 42936], + [42999, 43008], + [43824, 43867], + [43868, 43877], + [64256, 64263], + [65313, 65339], + [65345, 65371] + ], + "direction": "ltr", + "year": -700, + "living": true, + "link": "https://en.wikipedia.org/wiki/Latin_script" + }, + { + "name": "Lepcha", + "ranges": [ + [7168, 7224], + [7227, 7242], + [7245, 7248] + ], + "direction": "ltr", + "year": 1700, + "living": true, + "link": "https://en.wikipedia.org/wiki/Lepcha_alphabet" + }, + { + "name": "Limbu", + "ranges": [ + [6400, 6431], + [6432, 6444], + [6448, 6460], + [6464, 6465], + [6468, 6480] + ], + "direction": "ltr", + "year": 1740, + "living": true, + "link": "https://en.wikipedia.org/wiki/Limbu_alphabet" + }, + { + "name": "Linear A", + "ranges": [ + [67072, 67383], + [67392, 67414], + [67424, 67432] + ], + "direction": "ltr", + "year": -2500, + "living": false, + "link": "https://en.wikipedia.org/wiki/Linear_A" + }, + { + "name": "Linear B", + "ranges": [ + [65536, 65548], + [65549, 65575], + [65576, 65595], + [65596, 65598], + [65599, 65614], + [65616, 65630], + [65664, 65787] + ], + "direction": "ltr", + "year": -1450, + "living": false, + "link": "https://en.wikipedia.org/wiki/Linear_B" + }, + { + "name": "Lisu", + "ranges": [[42192, 42240]], + "direction": "ltr", + "year": 1915, + "living": true, + "link": "https://en.wikipedia.org/wiki/Fraser_alphabet" + }, + { + "name": "Lycian", + "ranges": [[66176, 66205]], + "direction": "ltr", + "year": -500, + "living": false, + "link": "https://en.wikipedia.org/wiki/Lycian_alphabet" + }, + { + "name": "Lydian", + "ranges": [ + [67872, 67898], + [67903, 67904] + ], + "direction": "rtl", + "year": -700, + "living": false, + "link": "https://en.wikipedia.org/wiki/Lydian_alphabet" + }, + { + "name": "Mahajani", + "ranges": [[69968, 70007]], + "direction": "ltr", + "year": 1150, + "living": false, + "link": "https://en.wikipedia.org/wiki/Mahajani" + }, + { + "name": "Mandaic", + "ranges": [ + [2112, 2140], + [2142, 2143] + ], + "direction": "rtl", + "year": 200, + "living": true, + "link": "https://en.wikipedia.org/wiki/Mandaic_alphabet" + }, + { + "name": "Manichaean", + "ranges": [ + [68288, 68327], + [68331, 68343] + ], + "direction": "rtl", + "year": 250, + "living": false, + "link": "https://en.wikipedia.org/wiki/Manichaean_alphabet" + }, + { + "name": "Marchen", + "ranges": [ + [72816, 72848], + [72850, 72872], + [72873, 72887] + ], + "direction": "ltr", + "year": 650, + "living": false, + "link": "https://en.wikipedia.org/wiki/Zhang-Zhung_language#Scripts" + }, + { + "name": "Mende Kikakui", + "ranges": [ + [124928, 125125], + [125127, 125143] + ], + "direction": "rtl", + "year": 1880, + "living": true, + "link": "https://en.wikipedia.org/wiki/Mende_Kikakui_script" + }, + { + "name": "Meroitic Cursive", + "ranges": [ + [68000, 68024], + [68028, 68048], + [68050, 68096] + ], + "direction": "rtl", + "year": -300, + "living": false, + "link": "https://en.wikipedia.org/wiki/Meroitic_alphabet" + }, + { + "name": "Meroitic Hieroglyphs", + "ranges": [[67968, 68000]], + "direction": "rtl", + "year": -300, + "living": false, + "link": "https://en.wikipedia.org/wiki/Meroitic_alphabet" + }, + { + "name": "Malayalam", + "ranges": [ + [3328, 3332], + [3333, 3341], + [3342, 3345], + [3346, 3397], + [3398, 3401], + [3402, 3408], + [3412, 3428], + [3430, 3456] + ], + "direction": "ltr", + "year": 830, + "living": true, + "link": "https://en.wikipedia.org/wiki/Malayalam_script" + }, + { + "name": "Modi", + "ranges": [ + [71168, 71237], + [71248, 71258] + ], + "direction": "ltr", + "year": 1200, + "living": false, + "link": "https://en.wikipedia.org/wiki/Modi_alphabet" + }, + { + "name": "Mongolian", + "ranges": [ + [6144, 6146], + [6148, 6149], + [6150, 6159], + [6160, 6170], + [6176, 6264], + [6272, 6315], + [71264, 71277] + ], + "direction": "ttb", + "year": 1204, + "living": false, + "link": "https://en.wikipedia.org/wiki/Mongolian_script" + }, + { + "name": "Mro", + "ranges": [ + [92736, 92767], + [92768, 92778], + [92782, 92784] + ], + "direction": "ltr", + "year": 1985, + "living": true, + "link": "https://en.wikipedia.org/wiki/Mru_language#Alphabet" + }, + { + "name": "Meetei Mayek", + "ranges": [ + [43744, 43767], + [43968, 44014], + [44016, 44026] + ], + "direction": "ltr", + "year": 200, + "living": true, + "link": "https://en.wikipedia.org/wiki/Meitei_script" + }, + { + "name": "Multani", + "ranges": [ + [70272, 70279], + [70280, 70281], + [70282, 70286], + [70287, 70302], + [70303, 70314] + ], + "direction": "ltr", + "year": 1750, + "living": false, + "link": "https://en.wikipedia.org/wiki/Multani_alphabet" + }, + { + "name": "Myanmar", + "ranges": [ + [4096, 4256], + [43488, 43519], + [43616, 43648] + ], + "direction": "ltr", + "year": 984, + "living": true, + "link": "https://en.wikipedia.org/wiki/Burmese_alphabet" + }, + { + "name": "Old North Arabian", + "ranges": [[68224, 68256]], + "direction": "rtl", + "year": 750, + "living": false, + "link": "https://en.wikipedia.org/wiki/Ancient_North_Arabian" + }, + { + "name": "Nabataean", + "ranges": [ + [67712, 67743], + [67751, 67760] + ], + "direction": "rtl", + "year": 150, + "living": false, + "link": "https://en.wikipedia.org/wiki/Nabataean_alphabet" + }, + { + "name": "Newa", + "ranges": [ + [70656, 70746], + [70747, 70748], + [70749, 70750] + ], + "direction": "ltr", + "year": 1000, + "living": true, + "link": "https://en.wikipedia.org/wiki/Prachalit_Nepal_alphabet" + }, + { + "name": "Nko", + "ranges": [[1984, 2043]], + "direction": "rtl", + "year": 1949, + "living": false, + "link": "https://en.wikipedia.org/wiki/N%27Ko_alphabet" + }, + { + "name": "Nushu", + "ranges": [ + [94177, 94178], + [110960, 111356] + ], + "direction": "ltr", + "year": 1500, + "living": true, + "link": "https://en.wikipedia.org/wiki/N%C3%BCshu_script" + }, + { + "name": "Ogham", + "ranges": [[5760, 5789]], + "direction": "ltr", + "year": 350, + "living": false, + "link": "https://en.wikipedia.org/wiki/Ogham" + }, + { + "name": "Ol Chiki", + "ranges": [[7248, 7296]], + "direction": "ltr", + "year": 1925, + "living": true, + "link": "https://en.wikipedia.org/wiki/Ol_Chiki_script" + }, + { + "name": "Old Turkic", + "ranges": [[68608, 68681]], + "direction": "rtl", + "year": 750, + "living": false, + "link": "https://en.wikipedia.org/wiki/Old_Turkic_alphabet" + }, + { + "name": "Oriya", + "ranges": [ + [2817, 2820], + [2821, 2829], + [2831, 2833], + [2835, 2857], + [2858, 2865], + [2866, 2868], + [2869, 2874], + [2876, 2885], + [2887, 2889], + [2891, 2894], + [2902, 2904], + [2908, 2910], + [2911, 2916], + [2918, 2936] + ], + "direction": "ltr", + "year": 1060, + "living": true, + "link": "https://en.wikipedia.org/wiki/Odia_alphabet" + }, + { + "name": "Osage", + "ranges": [ + [66736, 66772], + [66776, 66812] + ], + "direction": "ltr", + "year": 2006, + "living": true, + "link": "https://en.wikipedia.org/wiki/Osage_alphabet" + }, + { + "name": "Osmanya", + "ranges": [ + [66688, 66718], + [66720, 66730] + ], + "direction": "ltr", + "year": 1920, + "living": true, + "link": "https://en.wikipedia.org/wiki/Osmanya_alphabet" + }, + { + "name": "Palmyrene", + "ranges": [[67680, 67712]], + "direction": "rtl", + "year": -100, + "living": false, + "link": "https://en.wikipedia.org/wiki/Palmyrene_alphabet" + }, + { + "name": "Pau Cin Hau", + "ranges": [[72384, 72441]], + "direction": "ltr", + "year": 1900, + "living": true, + "link": "https://en.wikipedia.org/wiki/Pau_Cin_Hau" + }, + { + "name": "Old Permic", + "ranges": [[66384, 66427]], + "direction": "ltr", + "year": 1372, + "living": false, + "link": "https://en.wikipedia.org/wiki/Old_Permic_alphabet" + }, + { + "name": "Phags-pa", + "ranges": [ + [43072, 43123], + [43124, 43127] + ], + "direction": "ttb", + "year": 1269, + "living": false, + "link": "https://en.wikipedia.org/wiki/%27Phags-pa_script" + }, + { + "name": "Inscriptional Pahlavi", + "ranges": [ + [68448, 68467], + [68472, 68480] + ], + "direction": "rtl", + "year": -171, + "living": false, + "link": "https://en.wikipedia.org/wiki/Inscriptional_Pahlavi" + }, + { + "name": "Psalter Pahlavi", + "ranges": [ + [68480, 68498], + [68505, 68509], + [68521, 68528] + ], + "direction": "rtl", + "year": 550, + "living": false, + "link": "https://en.wikipedia.org/wiki/Psalter_Pahlavi" + }, + { + "name": "Phoenician", + "ranges": [ + [67840, 67868], + [67871, 67872] + ], + "direction": "rtl", + "year": -1200, + "living": false, + "link": "https://en.wikipedia.org/wiki/Phoenician_alphabet" + }, + { + "name": "Miao", + "ranges": [ + [93952, 94021], + [94032, 94079], + [94095, 94112] + ], + "direction": "ltr", + "year": 1936, + "living": true, + "link": "https://en.wikipedia.org/wiki/Pollard_script" + }, + { + "name": "Inscriptional Parthian", + "ranges": [ + [68416, 68438], + [68440, 68448] + ], + "direction": "rtl", + "year": -250, + "living": false, + "link": "https://en.wikipedia.org/wiki/Inscriptional_Parthian" + }, + { + "name": "Rejang", + "ranges": [ + [43312, 43348], + [43359, 43360] + ], + "direction": "ltr", + "year": 1750, + "living": true, + "link": "https://en.wikipedia.org/wiki/Rejang_script" + }, + { + "name": "Runic", + "ranges": [ + [5792, 5867], + [5870, 5881] + ], + "direction": "ltr", + "year": 150, + "living": false, + "link": "https://en.wikipedia.org/wiki/Runes" + }, + { + "name": "Samaritan", + "ranges": [ + [2048, 2094], + [2096, 2111] + ], + "direction": "rtl", + "year": -600, + "living": true, + "link": "https://en.wikipedia.org/wiki/Samaritan_alphabet" + }, + { + "name": "Old South Arabian", + "ranges": [[68192, 68224]], + "direction": "rtl", + "year": -850, + "living": false, + "link": "https://en.wikipedia.org/wiki/Ancient_South_Arabian_script" + }, + { + "name": "Saurashtra", + "ranges": [ + [43136, 43206], + [43214, 43226] + ], + "direction": "ltr", + "year": 1920, + "living": true, + "link": "https://en.wikipedia.org/wiki/Saurashtra_alphabet" + }, + { + "name": "SignWriting", + "ranges": [ + [120832, 121484], + [121499, 121504], + [121505, 121520] + ], + "direction": "ttb", + "year": 1974, + "living": true, + "link": "https://en.wikipedia.org/wiki/SignWriting" + }, + { + "name": "Shavian", + "ranges": [[66640, 66688]], + "direction": "ltr", + "year": 1960, + "living": true, + "link": "https://en.wikipedia.org/wiki/Shavian_alphabet" + }, + { + "name": "Sharada", + "ranges": [ + [70016, 70094], + [70096, 70112] + ], + "direction": "ltr", + "year": 800, + "living": true, + "link": "https://en.wikipedia.org/wiki/%C5%9A%C4%81rad%C4%81_script" + }, + { + "name": "Siddham", + "ranges": [ + [71040, 71094], + [71096, 71134] + ], + "direction": "ltr", + "year": 550, + "living": false, + "link": "https://en.wikipedia.org/wiki/Siddha%E1%B9%83_script" + }, + { + "name": "Khudawadi", + "ranges": [ + [70320, 70379], + [70384, 70394] + ], + "direction": "ltr", + "year": 1550, + "living": true, + "link": "https://en.wikipedia.org/wiki/Khudabadi_script" + }, + { + "name": "Sinhala", + "ranges": [ + [3458, 3460], + [3461, 3479], + [3482, 3506], + [3507, 3516], + [3517, 3518], + [3520, 3527], + [3530, 3531], + [3535, 3541], + [3542, 3543], + [3544, 3552], + [3558, 3568], + [3570, 3573], + [70113, 70133] + ], + "direction": "ltr", + "year": 700, + "living": true, + "link": "https://en.wikipedia.org/wiki/Sinhalese_alphabet" + }, + { + "name": "Sora Sompeng", + "ranges": [ + [69840, 69865], + [69872, 69882] + ], + "direction": "ltr", + "year": 1936, + "living": true, + "link": "https://en.wikipedia.org/wiki/Sorang_Sompeng_alphabet" + }, + { + "name": "Soyombo", + "ranges": [ + [72272, 72324], + [72326, 72349], + [72350, 72355] + ], + "direction": "ltr", + "year": 1650, + "living": false, + "link": "https://en.wikipedia.org/wiki/Soyombo_alphabet" + }, + { + "name": "Sundanese", + "ranges": [ + [7040, 7104], + [7360, 7368] + ], + "direction": "ltr", + "year": 1350, + "living": true, + "link": "https://en.wikipedia.org/wiki/Sundanese_script" + }, + { + "name": "Syloti Nagri", + "ranges": [[43008, 43052]], + "direction": "ltr", + "year": 1303, + "living": true, + "link": "https://en.wikipedia.org/wiki/Sylheti_Nagari" + }, + { + "name": "Syriac", + "ranges": [ + [1792, 1806], + [1807, 1867], + [1869, 1872], + [2144, 2155] + ], + "direction": "rtl", + "year": -200, + "living": true, + "link": "https://en.wikipedia.org/wiki/Syriac_alphabet" + }, + { + "name": "Tagbanwa", + "ranges": [ + [5984, 5997], + [5998, 6001], + [6002, 6004] + ], + "direction": "ltr", + "year": 1300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tagbanwa_script" + }, + { + "name": "Takri", + "ranges": [ + [71296, 71352], + [71360, 71370] + ], + "direction": "ltr", + "year": 1550, + "living": true, + "link": "https://en.wikipedia.org/wiki/Takri_alphabet" + }, + { + "name": "Tai Le", + "ranges": [ + [6480, 6510], + [6512, 6517] + ], + "direction": "ltr", + "year": 1200, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tai_Le_alphabet" + }, + { + "name": "New Tai Lue", + "ranges": [ + [6528, 6572], + [6576, 6602], + [6608, 6619], + [6622, 6624] + ], + "direction": "ltr", + "year": 1950, + "living": true, + "link": "https://en.wikipedia.org/wiki/New_Tai_Lue_alphabet" + }, + { + "name": "Tamil", + "ranges": [ + [2946, 2948], + [2949, 2955], + [2958, 2961], + [2962, 2966], + [2969, 2971], + [2972, 2973], + [2974, 2976], + [2979, 2981], + [2984, 2987], + [2990, 3002], + [3006, 3011], + [3014, 3017], + [3018, 3022], + [3024, 3025], + [3031, 3032], + [3046, 3067] + ], + "direction": "ltr", + "year": 700, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tamil_script" + }, + { + "name": "Tangut", + "ranges": [ + [94176, 94177], + [94208, 100333], + [100352, 101107] + ], + "direction": "ltr", + "year": 1036, + "living": false, + "link": "https://en.wikipedia.org/wiki/Tangut_script" + }, + { + "name": "Tai Viet", + "ranges": [ + [43648, 43715], + [43739, 43744] + ], + "direction": "ltr", + "year": 1200, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tai_Dam_language#Writing_system" + }, + { + "name": "Telugu", + "ranges": [ + [3072, 3076], + [3077, 3085], + [3086, 3089], + [3090, 3113], + [3114, 3130], + [3133, 3141], + [3142, 3145], + [3146, 3150], + [3157, 3159], + [3160, 3163], + [3168, 3172], + [3174, 3184], + [3192, 3200] + ], + "direction": "ltr", + "year": -900, + "living": true, + "link": "https://en.wikipedia.org/wiki/Telugu_script" + }, + { + "name": "Tifinagh", + "ranges": [ + [11568, 11624], + [11631, 11633], + [11647, 11648] + ], + "direction": "ltr", + "year": -300, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tifinagh" + }, + { + "name": "Tagalog", + "ranges": [ + [5888, 5901], + [5902, 5909] + ], + "direction": "ltr", + "year": 1250, + "living": true, + "link": "https://en.wikipedia.org/wiki/Baybayin" + }, + { + "name": "Thaana", + "ranges": [[1920, 1970]], + "direction": "rtl", + "year": 1599, + "living": true, + "link": "https://en.wikipedia.org/wiki/Thaana" + }, + { + "name": "Thai", + "ranges": [ + [3585, 3643], + [3648, 3676] + ], + "direction": "ltr", + "year": 1283, + "living": true, + "link": "https://en.wikipedia.org/wiki/Thai_alphabet" + }, + { + "name": "Tibetan", + "ranges": [ + [3840, 3912], + [3913, 3949], + [3953, 3992], + [3993, 4029], + [4030, 4045], + [4046, 4053], + [4057, 4059] + ], + "direction": "ltr", + "year": 650, + "living": false, + "link": "https://en.wikipedia.org/wiki/Tibetan_alphabet" + }, + { + "name": "Tirhuta", + "ranges": [ + [70784, 70856], + [70864, 70874] + ], + "direction": "ltr", + "year": 1450, + "living": true, + "link": "https://en.wikipedia.org/wiki/Tirhuta" + }, + { + "name": "Ugaritic", + "ranges": [ + [66432, 66462], + [66463, 66464] + ], + "direction": "ltr", + "year": -1400, + "living": false, + "link": "https://en.wikipedia.org/wiki/Ugaritic_alphabet" + }, + { + "name": "Vai", + "ranges": [[42240, 42540]], + "direction": "ltr", + "year": 1830, + "living": true, + "link": "https://en.wikipedia.org/wiki/Vai_syllabary" + }, + { + "name": "Warang Citi", + "ranges": [ + [71840, 71923], + [71935, 71936] + ], + "direction": "ltr", + "year": 1946, + "living": true, + "link": "https://en.wikipedia.org/wiki/Warang_Citi" + }, + { + "name": "Old Persian", + "ranges": [ + [66464, 66500], + [66504, 66518] + ], + "direction": "ltr", + "year": -525, + "living": false, + "link": "https://en.wikipedia.org/wiki/Old_Persian_cuneiform" + }, + { + "name": "Cuneiform", + "ranges": [ + [73728, 74650], + [74752, 74863], + [74864, 74869], + [74880, 75076] + ], + "direction": "ltr", + "year": -3050, + "living": false, + "link": "https://en.wikipedia.org/wiki/Cuneiform_script" + }, + { + "name": "Yi", + "ranges": [ + [40960, 42125], + [42128, 42183] + ], + "direction": "ltr", + "year": 1450, + "living": true, + "link": "https://en.wikipedia.org/wiki/Yi_script" + }, + { + "name": "Zanabazar Square", + "ranges": [[72192, 72264]], + "direction": "ltr", + "year": 1700, + "living": false, + "link": "https://en.wikipedia.org/wiki/Mongolian_writing_systems#Horizontal_square_script" + } +] diff --git a/src/language/scripts.ts b/src/language/scripts.ts new file mode 100644 index 0000000..8c9654f --- /dev/null +++ b/src/language/scripts.ts @@ -0,0 +1,13 @@ +import scripts from "./scripts.json" assert { type: "json" }; + +import { reduce } from "./reduce.ts"; + +export function characterCount(script: typeof scripts[number]) { + return reduce(script.ranges, (count, [from, to]) => count + (to - from), 0); +} + +const result = scripts.reduce((pre, current) => { + return characterCount(pre) < characterCount(current) ? current : pre; +}); + +console.log(result); diff --git a/src/language/sum.test.ts b/src/language/sum.test.ts index 5be57da..c693dec 100644 --- a/src/language/sum.test.ts +++ b/src/language/sum.test.ts @@ -1,8 +1,14 @@ -import { sum } from "./sum.ts"; -import { assert } from "./testing.ts"; +import { sum, sumFor } from "./sum.ts"; +import { assertEquals } from "../../deps.ts"; Deno.test("[sum]", () => { const result = sum(1, 10, 23, 46); - const expectedResult = 80; - assert(result === expectedResult); + + assertEquals(result, 80); +}); + +Deno.test("[sumFor]", () => { + const result = sumFor(1, 10, 23, 46); + + assertEquals(result, 80); }); diff --git a/src/language/sum.ts b/src/language/sum.ts index ae9558e..e1013eb 100644 --- a/src/language/sum.ts +++ b/src/language/sum.ts @@ -1,3 +1,13 @@ export function sum(...numbers: number[]) { return numbers.reduce((previous, current) => (previous += current)); } + +export function sumFor(...numbers: number[]) { + let sum = 0; + + for (const number of numbers) { + sum += number; + } + + return sum; +} diff --git a/src/language/testing.ts b/src/language/testing.ts deleted file mode 100644 index 4716c89..0000000 --- a/src/language/testing.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function assert( - condition: unknown, - message = "Codition is false!", -): asserts condition { - if (!condition) { - throw new Error(message); - } -} diff --git a/src/language/unless.ts b/src/language/unless.ts new file mode 100644 index 0000000..b4685b4 --- /dev/null +++ b/src/language/unless.ts @@ -0,0 +1,7 @@ +// deno-lint-ignore-file no-explicit-any + +export function unless any>(test: T, then: T) { + if (test()) { + return then() as ReturnType; + } +} diff --git a/src/node/mod.ts b/src/node/mod.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/program_structure/chessboard.test.ts b/src/program_structure/chessboard.test.ts new file mode 100644 index 0000000..81e78b8 --- /dev/null +++ b/src/program_structure/chessboard.test.ts @@ -0,0 +1,21 @@ +import { chessboard } from "./chessboard.ts"; +import { assertEquals } from "../../deps.ts"; + +const defaultBoard = ` # # # # +# # # # + # # # # +# # # # + # # # # +# # # # + # # # # +# # # # `; + +Deno.test("[chessboard] Length", () => { + assertEquals(defaultBoard, chessboard()); +}); + +Deno.test("[chessboard] Length", () => { + const { length: chessboardLength } = chessboard(); + + assertEquals(chessboardLength, 71); +}); diff --git a/src/program_structure/chessboard.ts b/src/program_structure/chessboard.ts new file mode 100644 index 0000000..68dc0bb --- /dev/null +++ b/src/program_structure/chessboard.ts @@ -0,0 +1,19 @@ +export function chessboard(length = 8) { + let result = ""; + + const totalLines = length - 1; + + for (let line = 0; line < length; line++) { + const oddLine = line % 2; + + for (let character = 0; character < length; character++) { + result += oddLine === character % 2 ? " " : "#"; + } + + if (line < totalLines) { + result += "\n"; + } + } + + return result; +} diff --git a/src/program_structure/fizzbuzz.test.ts b/src/program_structure/fizzbuzz.test.ts new file mode 100644 index 0000000..a2308ff --- /dev/null +++ b/src/program_structure/fizzbuzz.test.ts @@ -0,0 +1,31 @@ +import { BUZZ, divisibleBy, FIZZ, FIZZ_BUZZ, fizzBuzz } from "./fizzbuzz.ts"; +import { assert, assertEquals, assertFalse } from "../../deps.ts"; + +Deno.test("[fizzBuzz]", () => { + const expected = [ + 1, + 2, + FIZZ, + 4, + BUZZ, + FIZZ, + 7, + 8, + FIZZ, + BUZZ, + 11, + FIZZ, + 13, + 14, + FIZZ, + FIZZ_BUZZ, + ]; + + assertEquals(fizzBuzz(15), expected); +}); + +Deno.test("[isDivisbleBy]", () => { + assert(divisibleBy(15, 3)); + assert(divisibleBy(15, 5)); + assertFalse(divisibleBy(15, 7)); +}); diff --git a/src/program_structure/fizzbuzz.ts b/src/program_structure/fizzbuzz.ts new file mode 100644 index 0000000..09c71ab --- /dev/null +++ b/src/program_structure/fizzbuzz.ts @@ -0,0 +1,31 @@ +/** + * @private + */ +export const divisibleBy = (n: number, divisor: number) => n % divisor === 0; + +export const FIZZ = "Fizz"; +export const BUZZ = "Buzz"; +export const FIZZ_BUZZ = "FizzBuzz"; + +export function fizzBuzz(max = 100) { + const result: (typeof FIZZ | typeof BUZZ | typeof FIZZ_BUZZ | number)[] = []; + + for (let number = 1; number <= max; number++) { + const fizz = divisibleBy(number, 3); + const buzz = divisibleBy(number, 5); + + if (fizz) { + result.push(FIZZ); + } else if (buzz) { + result.push(BUZZ); + } else { + result.push(number); + } + + if (fizz && buzz) { + result.push(FIZZ_BUZZ); + } + } + + return result; +} diff --git a/src/program_structure/mod.ts b/src/program_structure/mod.ts new file mode 100644 index 0000000..1c7954a --- /dev/null +++ b/src/program_structure/mod.ts @@ -0,0 +1,3 @@ +export * from "./triangle.ts"; +export * from "./fizzbuzz.ts"; +export * from "./chessboard.ts"; diff --git a/src/program_structure/triangle.bench.ts b/src/program_structure/triangle.bench.ts new file mode 100644 index 0000000..d569447 --- /dev/null +++ b/src/program_structure/triangle.bench.ts @@ -0,0 +1,9 @@ +import { coolTriangle, triangle } from "./triangle.ts"; + +Deno.bench("[triangle]", { group: "Triangle" }, () => { + triangle(300); +}); + +Deno.bench("[coolTriangle]", { group: "Triangle" }, () => { + coolTriangle(300); +}); diff --git a/src/program_structure/triangle.test.ts b/src/program_structure/triangle.test.ts new file mode 100644 index 0000000..db1c1b7 --- /dev/null +++ b/src/program_structure/triangle.test.ts @@ -0,0 +1,18 @@ +import { coolTriangle, triangle } from "./triangle.ts"; +import { assertEquals } from "../../deps.ts"; + +const expected = `# +## +###`; + +Deno.test("[triangle]", () => { + assertEquals(triangle(), expected); +}); + +Deno.test("[coolTriangle]", () => { + assertEquals(coolTriangle(), expected); +}); + +Deno.test("[triangle vs coolTriangle]", () => { + assertEquals(triangle(), coolTriangle()); +}); diff --git a/src/program_structure/triangle.ts b/src/program_structure/triangle.ts new file mode 100644 index 0000000..2feae45 --- /dev/null +++ b/src/program_structure/triangle.ts @@ -0,0 +1,16 @@ +export function coolTriangle(length = 3) { + return Array.from({ length }, (_, time) => "#".repeat(time + 1)).join("\n"); +} + +// Almost 3 times faster! +export function triangle(rows = 3) { + let result = ""; + + for (let index = 1; index < rows; index++) { + result += "#".repeat(index) + "\n"; + } + + result += "#".repeat(rows); + + return result; +}