From 2a41cb793f9be25c612c317f685e1a9349b10108 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Fri, 20 Oct 2023 22:21:40 +1100 Subject: [PATCH] feat: Add Pop Count (#72) --- config.json | 7 +++ .../pop-count/.docs/instructions.append.md | 3 ++ .../practice/pop-count/.docs/instructions.md | 8 ++++ .../practice/pop-count/.docs/introduction.md | 47 ++++++++++++++++++ exercises/practice/pop-count/.eslintrc | 18 +++++++ .../practice/pop-count/.meta/config.json | 25 ++++++++++ .../practice/pop-count/.meta/proof.ci.wat | 24 ++++++++++ exercises/practice/pop-count/.meta/tests.toml | 15 ++++++ exercises/practice/pop-count/.npmrc | 1 + exercises/practice/pop-count/LICENSE | 21 ++++++++ exercises/practice/pop-count/babel.config.js | 4 ++ exercises/practice/pop-count/package.json | 34 +++++++++++++ .../practice/pop-count/pop-count.spec.js | 48 +++++++++++++++++++ exercises/practice/pop-count/pop-count.wat | 24 ++++++++++ 14 files changed, 279 insertions(+) create mode 100644 exercises/practice/pop-count/.docs/instructions.append.md create mode 100644 exercises/practice/pop-count/.docs/instructions.md create mode 100644 exercises/practice/pop-count/.docs/introduction.md create mode 100644 exercises/practice/pop-count/.eslintrc create mode 100644 exercises/practice/pop-count/.meta/config.json create mode 100644 exercises/practice/pop-count/.meta/proof.ci.wat create mode 100644 exercises/practice/pop-count/.meta/tests.toml create mode 100644 exercises/practice/pop-count/.npmrc create mode 100644 exercises/practice/pop-count/LICENSE create mode 100644 exercises/practice/pop-count/babel.config.js create mode 100644 exercises/practice/pop-count/package.json create mode 100644 exercises/practice/pop-count/pop-count.spec.js create mode 100644 exercises/practice/pop-count/pop-count.wat diff --git a/config.json b/config.json index 7f13b91..3cdb892 100644 --- a/config.json +++ b/config.json @@ -174,6 +174,13 @@ "practices": [], "prerequisites": [], "difficulty": 2 + }, { + "slug": "pop-count", + "name": "Pop Count", + "uuid": "233fde6a-79d9-4d90-8f94-2a0519352a5c", + "practices": [], + "prerequisites": [], + "difficulty": 4 }, { "slug": "pangram", "name": "Pangram", diff --git a/exercises/practice/pop-count/.docs/instructions.append.md b/exercises/practice/pop-count/.docs/instructions.append.md new file mode 100644 index 0000000..c0f3736 --- /dev/null +++ b/exercises/practice/pop-count/.docs/instructions.append.md @@ -0,0 +1,3 @@ +## Reserved Addresses + +No linear memory is required for this exercise. diff --git a/exercises/practice/pop-count/.docs/instructions.md b/exercises/practice/pop-count/.docs/instructions.md new file mode 100644 index 0000000..b0c2df5 --- /dev/null +++ b/exercises/practice/pop-count/.docs/instructions.md @@ -0,0 +1,8 @@ +# Instructions + +Your task is to count the number of 1 bits in the binary representation of a number. + +## Restrictions + +Keep your hands off that bit-count functionality provided by your standard library! +Solve this one yourself using other basic tools instead. diff --git a/exercises/practice/pop-count/.docs/introduction.md b/exercises/practice/pop-count/.docs/introduction.md new file mode 100644 index 0000000..49eaffd --- /dev/null +++ b/exercises/practice/pop-count/.docs/introduction.md @@ -0,0 +1,47 @@ +# Introduction + +Your friend Eliud inherited a farm from her grandma Tigist. +Her granny was an inventor and had a tendency to build things in an overly complicated manner. +The chicken coop has a digital display showing an encoded number representing the positions of all eggs that could be picked up. + +Eliud is asking you to write a program that shows the actual number of eggs in the coop. + +The position information encoding is calculated as follows: + +1. Scan the potential egg-laying spots and mark down a `1` for an existing egg or a `0` for an empty spot. +2. Convert the number from binary to decimal. +3. Show the result on the display. + +Example 1: + +```text +Chicken Coop: + _ _ _ _ _ _ _ +|E| |E|E| | |E| + +Resulting Binary: + 1 0 1 1 0 0 1 + +Decimal number on the display: +89 + +Actual eggs in the coop: +4 +``` + +Example 2: + +```text +Chicken Coop: + _ _ _ _ _ _ _ _ +| | | |E| | | | | + +Resulting Binary: + 0 0 0 1 0 0 0 0 + +Decimal number on the display: +16 + +Actual eggs in the coop: +1 +``` diff --git a/exercises/practice/pop-count/.eslintrc b/exercises/practice/pop-count/.eslintrc new file mode 100644 index 0000000..1dbeac2 --- /dev/null +++ b/exercises/practice/pop-count/.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/pop-count/.meta/config.json b/exercises/practice/pop-count/.meta/config.json new file mode 100644 index 0000000..cf0f857 --- /dev/null +++ b/exercises/practice/pop-count/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "pop-count.wat" + ], + "test": [ + "pop-count.spec.js" + ], + "example": [ + ".meta/proof.ci.wat" + ] + }, + "blurb": "Count the 1 bits in a number", + "source": "Christian Willner, Eric Willigers", + "source_url": "https://forum.exercism.org/t/new-exercise-suggestion-pop-count/7632/5", + "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/pop-count/.meta/proof.ci.wat b/exercises/practice/pop-count/.meta/proof.ci.wat new file mode 100644 index 0000000..fc077e6 --- /dev/null +++ b/exercises/practice/pop-count/.meta/proof.ci.wat @@ -0,0 +1,24 @@ +(module + (func (export "eggCount") (param $number i32) (result i32) + (local $remaining i32) + (local $count i32) + + (if (i32.eq (local.get $number) (i32.const 0))(then + (return (i32.const 0)) + )) + + (local.set $remaining (local.get $number)) + (local.set $count (i32.const 0)) + + ;; do while $remaining != 0 + (loop + ;; Clear least-significant 1 bit using + ;; $remaining -= $remaining & -$remaining + (local.set $remaining (i32.sub (local.get $remaining) (i32.and (local.get $remaining) (i32.sub (i32.const 0) (local.get $remaining))))) + (local.set $count (i32.add (local.get $count) (i32.const 1))) + (br_if 0 (i32.ne (local.get $remaining) (i32.const 0))) + ) + + (return (local.get $count)) + ) +) diff --git a/exercises/practice/pop-count/.meta/tests.toml b/exercises/practice/pop-count/.meta/tests.toml new file mode 100644 index 0000000..ad7b578 --- /dev/null +++ b/exercises/practice/pop-count/.meta/tests.toml @@ -0,0 +1,15 @@ +# 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. + +[559e789d-07d1-4422-9004-3b699f83bca3] +description = "0 eggs" + +[97223282-f71e-490c-92f0-b3ec9e275aba] +description = "1 egg" + +[1f8fd18f-26e9-4144-9a0e-57cdfc4f4ff5] +description = "4 eggs" + +[0c18be92-a498-4ef2-bcbb-28ac4b06cb81] +description = "13 eggs" diff --git a/exercises/practice/pop-count/.npmrc b/exercises/practice/pop-count/.npmrc new file mode 100644 index 0000000..d26df80 --- /dev/null +++ b/exercises/practice/pop-count/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/practice/pop-count/LICENSE b/exercises/practice/pop-count/LICENSE new file mode 100644 index 0000000..90e73be --- /dev/null +++ b/exercises/practice/pop-count/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/pop-count/babel.config.js b/exercises/practice/pop-count/babel.config.js new file mode 100644 index 0000000..9c17ba5 --- /dev/null +++ b/exercises/practice/pop-count/babel.config.js @@ -0,0 +1,4 @@ +export default { + presets: ["@exercism/babel-preset-javascript"], + plugins: [], +}; diff --git a/exercises/practice/pop-count/package.json b/exercises/practice/pop-count/package.json new file mode 100644 index 0000000..c6f5e00 --- /dev/null +++ b/exercises/practice/pop-count/package.json @@ -0,0 +1,34 @@ +{ + "name": "@exercism/wasm-pop-count", + "description": "Exercism exercises in WebAssembly.", + "type": "module", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/wasm", + "directory": "exercises/practice/pop-count" + }, + "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 ." + } +} diff --git a/exercises/practice/pop-count/pop-count.spec.js b/exercises/practice/pop-count/pop-count.spec.js new file mode 100644 index 0000000..4504156 --- /dev/null +++ b/exercises/practice/pop-count/pop-count.spec.js @@ -0,0 +1,48 @@ +import { compileWat, WasmRunner } from "@exercism/wasm-lib"; + +let wasmModule; +let currentInstance; + +beforeAll(async () => { + try { + const watPath = new URL("./pop-count.wat", import.meta.url); + const { buffer } = await compileWat(watPath); + wasmModule = await WebAssembly.compile(buffer); + } catch (err) { + console.log(`Error compiling *.wat: ${err}`); + process.exit(1); + } +}); + +describe("eggCount()", () => { + 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("0 eggs", () => { + expect(currentInstance.exports.eggCount(0)).toEqual(0); + }); + + xtest("1 egg", () => { + expect(currentInstance.exports.eggCount(16)).toEqual(1); + }); + + xtest("4 eggs", () => { + expect(currentInstance.exports.eggCount(89)).toEqual(4); + }); + + xtest("13 eggs", () => { + expect(currentInstance.exports.eggCount(2000000000)).toEqual(13); + }); +}); diff --git a/exercises/practice/pop-count/pop-count.wat b/exercises/practice/pop-count/pop-count.wat new file mode 100644 index 0000000..a908877 --- /dev/null +++ b/exercises/practice/pop-count/pop-count.wat @@ -0,0 +1,24 @@ +(module + (func (export "eggCount") (param $number i32) (result i32) + (local $remaining i32) + (local $count i32) + + (if (i32.eq (local.get $number) (i32.const 0))(then + (return (i32.const 0)) + )) + + (local.set $remaining (i32.const 0)) + (local.set $count (i32.const 0)) + + ;; do while $remaining != 0 + (loop + ;; Clear least-significant 1 bit using + ;; $remaining -= $remaining & -$remaining + (local.set $remaining (i32.sub (local.get $remaining) (i32.and (local.get $remaining) (i32.sub (i32.const 0) (local.get $remaining))))) + (local.set $count (i32.add (local.get $count) (i32.const 1))) + (br_if 0 (i32.ne (local.get $remaining) (i32.const 0))) + ) + + (return (local.get $count)) + ) +)