Fix ggt and write tests

This commit is contained in:
Jan-Niclas Loosen
2025-10-26 00:55:37 +02:00
parent dbac3442e9
commit a26207ae23
5 changed files with 156 additions and 98 deletions

View File

@@ -35,7 +35,6 @@ public class AbstractMachine {
// Executes single step and returns configuration // Executes single step and returns configuration
public void executeStep() { public void executeStep() {
try { try {
ensureCapacity(this.TOP);
Instruction inst = this.prog.get(this.PC); Instruction inst = this.prog.get(this.PC);
int arg1 = inst.getArg1() != null ? inst.getArg1() : 0; int arg1 = inst.getArg1() != null ? inst.getArg1() : 0;
@@ -45,96 +44,96 @@ public class AbstractMachine {
switch (inst.getOpcode()) { switch (inst.getOpcode()) {
case Instruction.CONST: case Instruction.CONST:
// Pg. 9 // Pg. 9
this.stack.set(this.TOP + 1, arg1); write(this.TOP + 1, arg1);
this.TOP += 1; this.TOP += 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.LOAD: case Instruction.LOAD:
// Pg. 25 // Pg. 25
int loadSpp = spp(arg2, this.PP, this.FP); int loadSpp = spp(arg2, this.PP, this.FP);
this.stack.set(this.TOP + 1, this.stack.get(loadSpp + arg1)); write(this.TOP + 1, read(loadSpp + arg1));
this.TOP += 1; this.TOP += 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.STORE: case Instruction.STORE:
// Pg. 25 // Pg. 25
int storeSpp = spp(arg2, this.PP, this.FP); int storeSpp = spp(arg2, this.PP, this.FP);
this.stack.set(storeSpp + arg1, this.stack.get(this.TOP)); write(storeSpp + arg1, read(this.TOP));
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.ADD: case Instruction.ADD:
// Pg. 9 // Pg. 9
int addA = stack.get(this.TOP - 1); int addA = read(this.TOP - 1);
int addB = stack.get(this.TOP); int addB = read(this.TOP);
this.stack.set(this.TOP - 1, addA + addB); write(this.TOP - 1, addA + addB);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.SUB: case Instruction.SUB:
// Pg. 9 // Pg. 9
int subA = stack.get(this.TOP - 1); int subA = read(this.TOP - 1);
int subB = stack.get(this.TOP); int subB = read(this.TOP);
this.stack.set(this.TOP - 1, subA - subB); write(this.TOP - 1, subA - subB);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.MUL: case Instruction.MUL:
// Pg. 9 // Pg. 9
int mulA = stack.get(this.TOP - 1); int mulA = read(this.TOP - 1);
int mulB = stack.get(this.TOP); int mulB = read(this.TOP);
this.stack.set(this.TOP - 1, mulA * mulB); write(this.TOP - 1, mulA * mulB);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.DIV: case Instruction.DIV:
// Pg. 9 // Pg. 9
int divA = stack.get(this.TOP - 1); int divA = read(this.TOP - 1);
int divB = stack.get(this.TOP); int divB = read(this.TOP);
if (divB == 0) { if (divB == 0) {
throw new ArithmeticException("Division by zero"); throw new ArithmeticException("Division by zero");
} }
this.stack.set(this.TOP - 1, divA / divB); write(this.TOP - 1, Math.floorDiv(divA, divB));
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.LT: case Instruction.LT:
// Pg. 12 // Pg. 12
int ltA = stack.get(this.TOP - 1); int ltA = read(this.TOP - 1);
int ltB = stack.get(this.TOP); int ltB = read(this.TOP);
this.stack.set(this.TOP - 1, ltA < ltB ? 1 : 0); write(this.TOP - 1, ltA < ltB ? 1 : 0);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.GT: case Instruction.GT:
// Pg. 12 // Pg. 12
int gtA = stack.get(this.TOP - 1); int gtA = read(this.TOP - 1);
int gtB = stack.get(this.TOP); int gtB = read(this.TOP);
this.stack.set(this.TOP - 1, gtA > gtB ? 1 : 0); write(this.TOP - 1, gtA > gtB ? 1 : 0);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.EQ: case Instruction.EQ:
// Pg. 12 // Pg. 12
int eqA = stack.get(this.TOP - 1); int eqA = read(this.TOP - 1);
int eqB = stack.get(this.TOP); int eqB = read(this.TOP);
this.stack.set(this.TOP - 1, eqA == eqB ? 1 : 0); write(this.TOP - 1, eqA == eqB ? 1 : 0);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.NEQ: case Instruction.NEQ:
// Pg. 12 // Pg. 12
int neqA = stack.get(this.TOP - 1); int neqA = read(this.TOP - 1);
int neqB = stack.get(this.TOP); int neqB = read(this.TOP);
this.stack.set(this.TOP - 1, neqA != neqB ? 1 : 0); write(this.TOP - 1, neqA != neqB ? 1 : 0);
this.TOP -= 1; this.TOP -= 1;
this.PC += 1; this.PC += 1;
break; break;
case Instruction.IFZERO: case Instruction.IFZERO:
// Pg. 13 // Pg. 13
if (this.stack.get(this.TOP) == 0) { if (read(this.TOP) == 0) {
this.PC = arg1; this.PC = arg1;
} else { } else {
this.PC += 1; this.PC += 1;
@@ -155,11 +154,11 @@ public class AbstractMachine {
break; break;
case Instruction.INVOKE: case Instruction.INVOKE:
// Pg. 23 // Pg. 23
this.stack.set(this.TOP + 1, this.PC + 1); write(this.TOP + 1, this.PC + 1);
this.stack.set(this.TOP + 2, this.PP); write(this.TOP + 2, this.PP);
this.stack.set(this.TOP + 3, this.FP); write(this.TOP + 3, this.FP);
this.stack.set(this.TOP + 4, spp(arg3, this.PP, this.FP)); write(this.TOP + 4, spp(arg3, this.PP, this.FP));
this.stack.set(this.TOP + 5, sfp(arg3, this.PP, this.FP)); write(this.TOP + 5, sfp(arg3, this.PP, this.FP));
this.PP = this.TOP - arg1 + 1; this.PP = this.TOP - arg1 + 1;
this.FP = this.TOP + 1; this.FP = this.TOP + 1;
this.TOP += 5; this.TOP += 5;
@@ -167,12 +166,12 @@ public class AbstractMachine {
break; break;
case Instruction.RETURN: case Instruction.RETURN:
// Pg. 24 // Pg. 24
int res = this.stack.get(this.TOP); int res = read(this.TOP);
this.TOP = this.PP; this.TOP = this.PP;
this.PC = this.stack.get(this.FP); this.PC = read(this.FP);
this.PP = this.stack.get(this.FP + 1); this.PP = read(this.FP + 1);
this.FP = this.stack.get(this.FP + 2); this.FP = read(this.FP + 2);
this.stack.set(this.TOP, res); write(this.TOP, res);
break; break;
case Instruction.POP: case Instruction.POP:
// Not in docs, see MaMa // Not in docs, see MaMa
@@ -211,23 +210,6 @@ public class AbstractMachine {
return sfp(d-1, this.stack.get(fp+3), this.stack.get(fp+4)); return sfp(d-1, this.stack.get(fp+3), this.stack.get(fp+4));
} }
// Called before each instruction
protected void ensureCapacity(int currSize) {
int size = stack.size();
// Always ensure a minimum working capacity of 16
if (size < 16) {
for (int i = size; i < 16; i++) stack.add(0);
size = 16;
}
// Double list capacity when fewer than 8 free slots remain
if (currSize >= size - 8) {
int newSize = size * 2;
for (int i = size; i < newSize; i++) stack.add(0);
}
}
// Returns the machine's current configuration as string // Returns the machine's current configuration as string
public String confStr() { public String confStr() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -266,4 +248,13 @@ public class AbstractMachine {
} }
return result; return result;
} }
protected void write(int index, int value) {
while (stack.size() <= index) stack.add(0);
stack.set(index, value);
}
protected int read(int index) {
return stack.get(index);
}
} }

View File

@@ -2,8 +2,6 @@ package de.unitrier.st.uap.w25.tram;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
public class AbstractMachineTests { public class AbstractMachineTests {
@@ -75,29 +73,7 @@ public class AbstractMachineTests {
} }
@Test @Test
void testGGTx48y18() { void testMODx5y2() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{48, 18});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx270y192() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{270, 192});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(270,192) = 6.");
}
@Test
void testModPositive() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram"); Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5}); AbstractMachine tram = new AbstractMachine(code, new Integer[]{17, 5});
tram.execute(); tram.execute();
@@ -108,7 +84,7 @@ public class AbstractMachineTests {
} }
@Test @Test
void testModExactDivision() { void testMODx20y5() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram"); Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{20, 5}); AbstractMachine tram = new AbstractMachine(code, new Integer[]{20, 5});
tram.execute(); tram.execute();
@@ -119,7 +95,7 @@ public class AbstractMachineTests {
} }
@Test @Test
void testModSmallerDividend() { void testMODx3y10() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram"); Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{3, 10}); AbstractMachine tram = new AbstractMachine(code, new Integer[]{3, 10});
tram.execute(); tram.execute();
@@ -129,6 +105,16 @@ public class AbstractMachineTests {
assertEquals(3, result, "Expected 3 mod 10 = 3."); assertEquals(3, result, "Expected 3 mod 10 = 3.");
} }
@Test
void testMODx37y16() {
Instruction[] code = Assembler.readTRAMCode("tramcode/mod.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{37,16});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(5, result, "Expected 37 mod 16 = 5.");
}
@Test @Test
void testGGTx17y5() { void testGGTx17y5() {
@@ -163,4 +149,80 @@ public class AbstractMachineTests {
assertEquals(25, result, "Expected ggt(0,25) = 25."); assertEquals(25, result, "Expected ggt(0,25) = 25.");
} }
@Test
void testGGTx270y192() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{270, 192});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(270,192) = 6.");
}
@Test
void testGGTx48y18() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{48, 18});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(6, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx3y9() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{3,9});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(3, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx9y3() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{9,3});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(3, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx24y4() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{24,4});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(4, result, "Expected ggt(48,18) = 6.");
}
@Test
void testGGTx16y37() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{16,37});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(1, result, "Expected ggt(16,37) = 1.");
}
@Test
void testGGTx37y16() {
Instruction[] code = Assembler.readTRAMCode("tramcode/ggt.tram");
AbstractMachine tram = new AbstractMachine(code, new Integer[]{37,16});
tram.execute();
assertTrue(tram.isHalted(), "Machine should halt.");
int result = tram.result();
assertEquals(1, result, "Expected ggt(37,16) = 1.");
}
} }

View File

@@ -1,22 +1,26 @@
// Quellkode: ggt(a,b) { if (b==0) then a else ggt(b, a mod b) } // Quellkode: ggt(a,b) { if (b==0) then a else ggt(b, a mod b) }
// Annahmen: Die Argumente a und b werden durch Kellerzellen 0 und 1 repräsentiert, // Annahmen: Die Argumente a und b werden durch Kellerzellen 0 und 1 repräsentiert,
// sowie PP=0, FP=0 und TOP=-1 // sowie PP=0, FP=0 und TOP=-1
INVOKE 2 LGGT 0 INVOKE 2 GGT 0
POP
HALT HALT
LGGT: GGT: LOAD 1 0
// b IFZERO RET
LOAD 1 0 LOAD 1 0
// if b != 0 -> 1
IFZERO 1
// return a
LOAD 0 0
RETURN
L1:
LOAD 1 0 // b
LOAD 0 0 LOAD 0 0
LOAD 1 0 LOAD 1 0
MOD // a mod b INVOKE 2 MOD 1
INVOKE 2 LGGT 0 // ggt(b, a mod b) INVOKE 2 GGT 0
RETURN RETURN
RET: LOAD 0 0
RETURN
MOD: LOAD 0 0
LOAD 1 0
LOAD 0 0
LOAD 1 0
DIV
MUL
SUB
RETURN

View File

@@ -3,10 +3,11 @@
INVOKE 2 MOD 0 INVOKE 2 MOD 0
HALT HALT
MOD: LOAD 1 0 MOD: LOAD 0 0
LOAD 1 0
LOAD 0 0
LOAD 1 0 LOAD 1 0
DIV DIV
LOAD 2 0
MUL MUL
SUB SUB
RETURN RETURN