From ac1ee668962f55139d820205754cc770df317e34 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Mon, 10 Jun 2024 15:54:07 +1000 Subject: [PATCH] Add isogram exercise --- config.json | 8 ++ .../isogram/.docs/instructions.append.md | 5 + .../practice/isogram/.docs/instructions.md | 14 +++ exercises/practice/isogram/.eslintrc | 18 +++ exercises/practice/isogram/.meta/config.json | 25 ++++ exercises/practice/isogram/.meta/proof.ci.wat | 55 +++++++++ exercises/practice/isogram/.meta/tests.toml | 45 ++++++++ exercises/practice/isogram/.npmrc | 1 + exercises/practice/isogram/LICENSE | 21 ++++ exercises/practice/isogram/babel.config.js | 4 + exercises/practice/isogram/isogram.spec.js | 107 ++++++++++++++++++ exercises/practice/isogram/isogram.wat | 15 +++ exercises/practice/isogram/package.json | 34 ++++++ 13 files changed, 352 insertions(+) create mode 100644 exercises/practice/isogram/.docs/instructions.append.md create mode 100644 exercises/practice/isogram/.docs/instructions.md create mode 100644 exercises/practice/isogram/.eslintrc create mode 100644 exercises/practice/isogram/.meta/config.json create mode 100644 exercises/practice/isogram/.meta/proof.ci.wat create mode 100644 exercises/practice/isogram/.meta/tests.toml create mode 100644 exercises/practice/isogram/.npmrc create mode 100644 exercises/practice/isogram/LICENSE create mode 100644 exercises/practice/isogram/babel.config.js create mode 100644 exercises/practice/isogram/isogram.spec.js create mode 100644 exercises/practice/isogram/isogram.wat create mode 100644 exercises/practice/isogram/package.json diff --git a/config.json b/config.json index 66ba6ff..c7664b6 100644 --- a/config.json +++ b/config.json @@ -247,6 +247,14 @@ "prerequisites": [], "difficulty": 2 }, + { + "slug": "isogram", + "name": "Isogram", + "uuid": "b292a9c5-7628-4bc4-b156-df2460eb44ae", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, { "slug": "rotational-cipher", "name": "Rotational Cipher", diff --git a/exercises/practice/isogram/.docs/instructions.append.md b/exercises/practice/isogram/.docs/instructions.append.md new file mode 100644 index 0000000..4881b74 --- /dev/null +++ b/exercises/practice/isogram/.docs/instructions.append.md @@ -0,0 +1,5 @@ +# Instruction append + +## Reserved Memory + +The buffer for the input string uses bytes 64-319 of linear memory. diff --git a/exercises/practice/isogram/.docs/instructions.md b/exercises/practice/isogram/.docs/instructions.md new file mode 100644 index 0000000..2e8df85 --- /dev/null +++ b/exercises/practice/isogram/.docs/instructions.md @@ -0,0 +1,14 @@ +# Instructions + +Determine if a word or phrase is an isogram. + +An isogram (also known as a "non-pattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times. + +Examples of isograms: + +- lumberjacks +- background +- downstream +- six-year-old + +The word _isograms_, however, is not an isogram, because the s repeats. diff --git a/exercises/practice/isogram/.eslintrc b/exercises/practice/isogram/.eslintrc new file mode 100644 index 0000000..1dbeac2 --- /dev/null +++ b/exercises/practice/isogram/.eslintrc @@ -0,0 +1,18 @@ +{ + "root": true, + "extends": "@exercism/eslint-config-javascript", + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.spec.js" + ], + "excludedFiles": [ + "custom.spec.js" + ], + "extends": "@exercism/eslint-config-javascript/maintainers" + } + ] +} diff --git a/exercises/practice/isogram/.meta/config.json b/exercises/practice/isogram/.meta/config.json new file mode 100644 index 0000000..3953f2b --- /dev/null +++ b/exercises/practice/isogram/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "isogram.wat" + ], + "test": [ + "isogram.spec.js" + ], + "example": [ + ".meta/proof.ci.wat" + ] + }, + "blurb": "Determine if a word or phrase is an isogram.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Isogram", + "custom": { + "version.tests.compatibility": "jest-27", + "flag.tests.task-per-describe": false, + "flag.tests.may-run-long": false, + "flag.tests.includes-optional": false + } +} diff --git a/exercises/practice/isogram/.meta/proof.ci.wat b/exercises/practice/isogram/.meta/proof.ci.wat new file mode 100644 index 0000000..aabcd39 --- /dev/null +++ b/exercises/practice/isogram/.meta/proof.ci.wat @@ -0,0 +1,55 @@ +(module + (memory (export "mem") 1) + + (func (export "isIsogram") (param $offset i32) (param $length i32) (result i32) + (local $index i32) + (local $stop i32) + (local $letter i32) + (local $bitset i32) + (local $updated i32) + + (local.set $index (local.get $offset)) + (local.set $stop + (i32.add + (local.get $offset) + (local.get $length) + ) + ) + (local.set $bitset (i32.const 0)) + + (loop $process + (if (i32.eq (local.get $index) + (local.get $stop)) (then + (return (i32.const 1)) + )) + + (local.set $letter (i32.sub (i32.or (i32.load8_u (local.get $index)) + (i32.const 32)) + (i32.const 97))) + + (local.set $index + (i32.add (local.get $index) (i32.const 1)) + ) + + (if (i32.ge_u (local.get $letter) + (i32.const 26)) (then + (br $process) + )) + + (local.set $updated (i32.or (i32.shl (i32.const 1) + (local.get $letter)) + (local.get $bitset))) + + (if (i32.eq (local.get $updated) + (local.get $bitset)) (then + (return (i32.const 0)) + )) + + (local.set $bitset (local.get $updated)) + + (br $process) + ) + + (unreachable) + ) +) diff --git a/exercises/practice/isogram/.meta/tests.toml b/exercises/practice/isogram/.meta/tests.toml new file mode 100644 index 0000000..bd60778 --- /dev/null +++ b/exercises/practice/isogram/.meta/tests.toml @@ -0,0 +1,45 @@ +# This is an auto-generated file. Regular comments will be removed when this +# file is regenerated. Regenerating will not touch any manually added keys, +# so comments can be added in a "comment" key. + +[a0e97d2d-669e-47c7-8134-518a1e2c4555] +description = "empty string" + +[9a001b50-f194-4143-bc29-2af5ec1ef652] +description = "isogram with only lower case characters" + +[8ddb0ca3-276e-4f8b-89da-d95d5bae78a4] +description = "word with one duplicated character" + +[6450b333-cbc2-4b24-a723-0b459b34fe18] +description = "word with one duplicated character from the end of the alphabet" + +[a15ff557-dd04-4764-99e7-02cc1a385863] +description = "longest reported english isogram" + +[f1a7f6c7-a42f-4915-91d7-35b2ea11c92e] +description = "word with duplicated character in mixed case" + +[14a4f3c1-3b47-4695-b645-53d328298942] +description = "word with duplicated character in mixed case, lowercase first" + +[423b850c-7090-4a8a-b057-97f1cadd7c42] +description = "hypothetical isogrammic word with hyphen" + +[93dbeaa0-3c5a-45c2-8b25-428b8eacd4f2] +description = "hypothetical word with duplicated character following hyphen" + +[36b30e5c-173f-49c6-a515-93a3e825553f] +description = "isogram with duplicated hyphen" + +[cdabafa0-c9f4-4c1f-b142-689c6ee17d93] +description = "made-up name that is an isogram" + +[5fc61048-d74e-48fd-bc34-abfc21552d4d] +description = "duplicated character in the middle" + +[310ac53d-8932-47bc-bbb4-b2b94f25a83e] +description = "same first and last characters" + +[0d0b8644-0a1e-4a31-a432-2b3ee270d847] +description = "word with duplicated character and with two hyphens" diff --git a/exercises/practice/isogram/.npmrc b/exercises/practice/isogram/.npmrc new file mode 100644 index 0000000..d26df80 --- /dev/null +++ b/exercises/practice/isogram/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/practice/isogram/LICENSE b/exercises/practice/isogram/LICENSE new file mode 100644 index 0000000..90e73be --- /dev/null +++ b/exercises/practice/isogram/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Exercism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exercises/practice/isogram/babel.config.js b/exercises/practice/isogram/babel.config.js new file mode 100644 index 0000000..9c17ba5 --- /dev/null +++ b/exercises/practice/isogram/babel.config.js @@ -0,0 +1,4 @@ +export default { + presets: ["@exercism/babel-preset-javascript"], + plugins: [], +}; diff --git a/exercises/practice/isogram/isogram.spec.js b/exercises/practice/isogram/isogram.spec.js new file mode 100644 index 0000000..fac4aef --- /dev/null +++ b/exercises/practice/isogram/isogram.spec.js @@ -0,0 +1,107 @@ +import { compileWat, WasmRunner } from "@exercism/wasm-lib"; + +let wasmModule; +let currentInstance; + +function isIsogram(input = "") { + const inputBufferOffset = 64; + const inputBufferCapacity = 256; + + const inputLengthEncoded = new TextEncoder().encode(input).length; + if (inputLengthEncoded > inputBufferCapacity) { + throw new Error( + `String is too large for buffer of size ${inputBufferCapacity} bytes` + ); + } + + currentInstance.set_mem_as_utf8(inputBufferOffset, inputLengthEncoded, input); + + // Pass offset and length to WebAssembly function + return currentInstance.exports.isIsogram( + inputBufferOffset, + inputLengthEncoded + ); +} + +beforeAll(async () => { + try { + const watPath = new URL("./isogram.wat", import.meta.url); + const { buffer } = await compileWat(watPath); + wasmModule = await WebAssembly.compile(buffer); + } catch (err) { + console.log(`Error compiling *.wat: \n${err}`); + process.exit(1); + } +}); + +describe("Isogram()", () => { + beforeEach(async () => { + currentInstance = null; + if (!wasmModule) { + return Promise.reject(); + } + try { + currentInstance = await new WasmRunner(wasmModule); + return Promise.resolve(); + } catch (err) { + console.log(`Error instantiating WebAssembly module: ${err}`); + return Promise.reject(); + } + }); + + test("empty_string", () => { + expect(isIsogram("")).toBe(1); + }); + + xtest("isogram_with_only_lower_case_characters", () => { + expect(isIsogram("isogram")).toBe(1); + }); + + xtest("word_with_one_duplicated_character", () => { + expect(isIsogram("eleven")).toBe(0); + }); + + xtest("word_with_one_duplicated_character_from_the_end_of_the_alphabet", () => { + expect(isIsogram("zzyzx")).toBe(0); + }); + + xtest("longest_reported_english_isogram", () => { + expect(isIsogram("subdermatoglyphic")).toBe(1); + }); + + xtest("word_with_duplicated_character_in_mixed_case", () => { + expect(isIsogram("Alphabet")).toBe(0); + }); + + xtest("word_with_duplicated_character_in_mixed_case_lowercase_first", () => { + expect(isIsogram("alphAbet")).toBe(0); + }); + + xtest("hypothetical_isogrammic_word_with_hyphen", () => { + expect(isIsogram("thumbscrew-japingly")).toBe(1); + }); + + xtest("hypothetical_word_with_duplicated_character_following_hyphen", () => { + expect(isIsogram("thumbscrew-jappingly")).toBe(0); + }); + + xtest("isogram_with_duplicated_hyphen", () => { + expect(isIsogram("six-year-old")).toBe(1); + }); + + xtest("made_up_name_that_is_an_isogram", () => { + expect(isIsogram("Emily Jung Schwartzkopf")).toBe(1); + }); + + xtest("duplicated_character_in_the_middle", () => { + expect(isIsogram("accentor")).toBe(0); + }); + + xtest("same_first_and_last_characters", () => { + expect(isIsogram("angola")).toBe(0); + }); + + xtest("word_with_duplicated_character_and_with_two_hyphens", () => { + expect(isIsogram("up-to-date")).toBe(0); + }); +}); diff --git a/exercises/practice/isogram/isogram.wat b/exercises/practice/isogram/isogram.wat new file mode 100644 index 0000000..f793f7d --- /dev/null +++ b/exercises/practice/isogram/isogram.wat @@ -0,0 +1,15 @@ +(module + (memory (export "mem") 1) + + ;; + ;; Determine if a word or phrase is an isogram. + ;; + ;; @param {i32} offset - offset of string in linear memory + ;; @param {i32} length - length of string in linear memory + ;; + ;; @returns {i32} 1 if pangram, 0 otherwise + ;; + (func (export "isIsogram") (param $offset i32) (param $length i32) (result i32) + (return (i32.const 1)) + ) +) diff --git a/exercises/practice/isogram/package.json b/exercises/practice/isogram/package.json new file mode 100644 index 0000000..5fe54b4 --- /dev/null +++ b/exercises/practice/isogram/package.json @@ -0,0 +1,34 @@ +{ + "name": "@exercism/wasm-isogram", + "description": "Exercism exercises in WebAssembly.", + "type": "module", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/wasm", + "directory": "exercises/practice/isogram" + }, + "jest": { + "maxWorkers": 1 + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@exercism/babel-preset-javascript": "^0.2.1", + "@exercism/eslint-config-javascript": "^0.6.0", + "@types/jest": "^29.4.0", + "@types/node": "^18.13.0", + "babel-jest": "^29.4.2", + "core-js": "^3.27.2", + "eslint": "^8.34.0", + "jest": "^29.4.2" + }, + "dependencies": { + "@exercism/wasm-lib": "^0.1.0" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js ./*", + "watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch ./*", + "lint": "eslint ." + } +}