Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ jobs:
- name: Check sources
run: make check
- name: Test interactive tools
run: node tests/base-converter.test.js
run: |
node tests/base-converter.test.js
node tests/bit-visualizer.test.js
- name: Build HTML, EPUB, PDF and Kindle
run: make all
- name: Upload course artifacts
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
<a href="no/">Norsk</a>
<a href="en/">English</a>
<a href="interactive/base-converter/">Basekonverterer</a>
<a href="interactive/bit-visualizer/">Bit-visualisering</a>
</nav>
</main>
</body>
Expand Down
29 changes: 29 additions & 0 deletions interactive/bit-visualizer/bits.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use strict";
const valueEl=document.querySelector("#value"), widthEl=document.querySelector("#width");
const bitsEl=document.querySelector("#bits"), errorEl=document.querySelector("#error");
function maxForWidth(w){return (1n<<BigInt(w))-1n}
function parseUnsigned(text,w){
if(!/^\d+$/.test(text.trim())) throw new Error("Skriv inn et usignert heltall.");
const n=BigInt(text.trim()), max=maxForWidth(w);
if(n>max) throw new Error(`Verdien må være mellom 0 og ${max} for ${w} bit.`);
return n;
}
function formatBinary(n,w){return n.toString(2).padStart(w,"0").replace(/(.{8})(?=.)/g,"$1 ")}
function render(){
const w=Number(widthEl.value); let n;
try{n=parseUnsigned(valueEl.value,w);errorEl.textContent=""}catch(e){errorEl.textContent=e.message;bitsEl.replaceChildren();return}
document.querySelector("#decimal").textContent=n.toString(10);
document.querySelector("#hex").textContent="0x"+n.toString(16).toUpperCase().padStart(w/4,"0");
document.querySelector("#binary").textContent=formatBinary(n,w);
bitsEl.replaceChildren();
for(let pos=w-1;pos>=0;pos--){
const mask=1n<<BigInt(pos), on=(n&mask)!==0n;
const b=document.createElement("button");
b.type="button"; b.className="bit"+(pos%8===7&&pos!==w-1?" byte-start":"");
b.setAttribute("aria-pressed",String(on));
b.innerHTML=`<span class="pos">bit ${pos}</span><strong>${on?1:0}</strong><span>2<sup>${pos}</sup></span><span class="contrib">${on?mask:0n}</span>`;
b.addEventListener("click",()=>{valueEl.value=(n^mask).toString();render()});
bitsEl.appendChild(b);
}
}
valueEl.addEventListener("input",render);widthEl.addEventListener("change",render);render();
23 changes: 23 additions & 0 deletions interactive/bit-visualizer/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="no">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>EduNumbers – Bit-visualisering</title><link rel="stylesheet" href="style.css">
</head>
<body><main>
<p><a href="../../no/">← EduNumbers</a></p>
<h1>Bit-visualisering</h1>
<p>Se hvordan et usignert heltall bygges opp av individuelle biter. Klikk på et bit for å slå det av eller på.</p>
<section class="controls">
<label>Desimalverdi <input id="value" value="42" inputmode="numeric"></label>
<label>Bredde <select id="width"><option>8</option><option>16</option><option>32</option></select> bit</label>
<p id="error" role="alert" aria-live="polite"></p>
</section>
<section aria-label="Representasjoner" class="summary">
<p><strong>Desimal:</strong> <code id="decimal"></code></p>
<p><strong>Hex:</strong> <code id="hex"></code></p>
<p><strong>Binær:</strong> <code id="binary"></code></p>
</section>
<div id="bits" class="bits" aria-label="Bits"></div>
<p class="hint">Hvert kort viser bitposisjon, 2-potens og bidraget til totalverdien. Tykkere mellomrom markerer bytegrenser.</p>
</main><script src="bits.js"></script></body></html>
6 changes: 6 additions & 0 deletions interactive/bit-visualizer/style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions tests/bit-visualizer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const fs=require("fs"),vm=require("vm");
const src=fs.readFileSync("interactive/bit-visualizer/bits.js","utf8");
const start=src.indexOf("function maxForWidth"),end=src.indexOf("function render");
const box={};vm.createContext(box);vm.runInContext(src.slice(start,end),box);
const eq=(a,b,m)=>{if(a!==b)throw new Error(m+": "+a+" != "+b)};
eq(box.maxForWidth(8),255n,"8-bit max");eq(box.maxForWidth(16),65535n,"16-bit max");
eq(box.maxForWidth(32),4294967295n,"32-bit max");
eq(box.parseUnsigned("255",8),255n,"parse 255");
eq(box.formatBinary(0x1234n,16),"00010010 00110100","binary grouping");
for(const [v,w] of [["256",8],["-1",8],["12x",16]]){let ok=false;try{box.parseUnsigned(v,w)}catch{ok=true}if(!ok)throw new Error("Expected rejection "+v)}
console.log("Bit visualizer tests passed");
Loading