Files
mercury/tests/hard_coded/bitmap_test.m
Zoltan Somogyi 25b4b67403 Carve io.file.m out of io.m.
library/io.file.m:
library/io.m:
    Move two sections of io.m, the "file handling predicates" section
    and the "handling temporary files" section to the new submodule io.file.m.

    Leave behind in io.m "forwarding predicates", predicates that do nothing
    except call the moved predicates in io.file.m, to provide backward
    compatibility. But do mark the forwarding predicates as obsolete,
    to tell people to update their (at their leisure, since the obsoleteness
    warning can be turned off).

    Also leave behind in io.m the definitions of the two types used
    by some parameters of some of the moved predicates. Document the reason
    why this is done.

library/MODULES_DOC:
    List the new module among the documented modules.

NEWS:
    Announce the changes.

browser/browse.m:
browser/interactive_query.m:
browser/listing.m:
compiler/analysis.file.m:
compiler/compile_target_code.m:
compiler/export.m:
compiler/fact_table.m:
compiler/file_util.m:
compiler/handle_options.m:
compiler/make.build.m:
compiler/make.module_dep_file.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make.util.m:
compiler/mercury_compile_main.m:
compiler/module_cmds.m:
compiler/parse_module.m:
compiler/passes_aux.m:
compiler/prog_event.m:
compiler/recompilation.check.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
deep_profiler/conf.m:
deep_profiler/mdprof_cgi.m:
library/dir.m:
mdbcomp/program_representation.m:
ssdb/ssdb.m:
    Call the file operation predicates directly in io.file.m, not indirectly
    through io.m.

    In two modules, add a #include of fcntl.h in C code. These modules contain
    C code that needs this #include, but until now, they got it via a copy
    in an automatically generated C header file of a foreign_decl pragma
    in io.m that contained that #include. This diff moves that foreign_decl
    to io.file.m, removing that crutch.

tests/debugger/browser_test.m:
tests/hard_coded/bit_buffer_test.m:
tests/hard_coded/bitmap_test.m:
tests/hard_coded/construct_bug.m:
tests/hard_coded/dir_fold.m:
tests/hard_coded/dir_test.m:
tests/hard_coded/read_binary_int16.m:
tests/hard_coded/read_binary_int32.m:
tests/hard_coded/read_binary_int64.m:
tests/hard_coded/read_binary_uint16.m:
tests/hard_coded/read_binary_uint32.m:
tests/hard_coded/read_binary_uint64.m:
tests/hard_coded/read_bitmap_size.m:
tests/hard_coded/remove_file.m:
tests/hard_coded/write_binary.m:
tests/hard_coded/write_binary_int8.m:
tests/hard_coded/write_binary_multibyte_int.m:
tests/hard_coded/write_binary_uint8.m:
    Call the file operation predicates directly in io.file.m, not indirectly
    through io.m.
2022-03-08 06:01:21 +11:00

592 lines
20 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ts=4 sw=4 et tw=0 wm=0 ft=mercury
%---------------------------------------------------------------------------%
:- module bitmap_test.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is cc_multi.
:- implementation.
:- import_module bitmap.
:- import_module bitmap_tester.
:- import_module bitmap_simple.
:- import_module bool.
:- import_module deconstruct.
:- import_module exception.
:- import_module int.
:- import_module io.file.
:- import_module list.
:- import_module string.
:- import_module univ.
main(!IO) :-
try_io(run_test, Res, !IO),
(
Res = succeeded(_)
;
Res = exception(Excp),
io.set_exit_status(1, !IO),
( if univ_to_type(Excp, BitmapResErr) then
io.nl(!IO),
write_bitmap_result_error(BitmapResErr, !IO)
else
rethrow(Res)
)
).
:- pred run_test({}::out, io::di, io::uo) is cc_multi.
run_test({}, !IO) :-
some [!BM] (
io.write_string("Single byte bitmap\n", !IO),
!:BM = bitmap_tester.new(4, yes),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
write(!.BM ^ bit(0), !IO),
nl(!IO),
!:BM = !.BM ^ bit(1) := no,
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
write(!.BM ^ bit(1), !IO),
nl(!IO),
!:BM = !.BM ^ bit(2) := no,
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
write_binary_string(!.BM ^ bits(0, 4), !IO),
nl(!IO),
!:BM = !.BM ^ bits(0, 2) := \ (!.BM ^ bits(0, 2)),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = !.BM ^ bits(0, 0) := !.BM ^ bits(4, 0),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO)
),
some [!BM] (
io.write_string("Multi-byte bitmap\n", !IO),
!:BM = bitmap_tester.new(20, no),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = flip(!.BM, 1),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = ((((((!.BM ^ bit(3) := yes)
^ bit(6) := yes)
^ bit(8) := yes)
^ bit(10) := yes)
^ bit(12) := yes)
^ bit(17) := yes),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
write_binary_string(!.BM ^ bits(1, 10), !IO),
nl(!IO),
write_binary_string(!.BM ^ bits(8, 12), !IO),
nl(!IO),
!:BM = !.BM ^ bits(6, 12) := \ (!.BM ^ bits(6, 12)),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = flip(!.BM, 6),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = complement(!.BM),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = resize(!.BM, 32, yes),
io.write_string(to_string(!.BM ^ fst), !IO),
nl(!IO),
functor(!.BM ^ fst, do_not_allow, Functor, _),
io.write_string(Functor, !IO),
nl(!IO)
),
some [!BM] (
io.write_string("Longer bitmap\n", !IO),
!:BM = bitmap_tester.new(160, no),
BytePattern = 0b10111001,
fill_in_alternating_pattern(BytePattern, !BM),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
!:BM = !.BM ^ bits(6, 12) := \ (!.BM ^ bits(6, 12)),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
io.write_string("non-overlapping copy_bits\n", !IO),
!:BM = copy_bits_in_bitmap(!.BM, 12, 64, 32),
io.write_string(to_byte_string(!.BM), !IO),
nl(!IO),
io.write_string("testing builtin.copy\n", !IO),
builtin.copy(!.BM ^ fst, CopyBM),
io.write_string(to_byte_string(CopyBM), !IO),
nl(!IO),
( if CopyBM = !.BM ^ fst then
io.write_string("Copy succeeded\n", !IO)
else
io.write_string("Copy failed\n", !IO)
)
),
io.write_string("Test simple aligned byte block copy.\n", !IO),
test_copy(8, 64, 32, !IO),
io.write_string("Test byte block copy with extra bits on ends.\n", !IO),
test_copy(6, 62, 36, !IO),
io.write_string("Test unaligned copy.\n", !IO),
test_copy(7, 64, 32, !IO),
io.write_string("Test unaligned copy with extra bits on ends.\n", !IO),
test_copy(7, 67, 36, !IO),
io.write_string("Test overlapping aligned byte block copy.\n", !IO),
test_copy(8, 0, 36, !IO),
io.write_string("Test overlapping aligned byte block copy.\n", !IO),
test_copy(0, 8, 36, !IO),
io.write_string("Test overlapping unaligned copy.\n", !IO),
test_copy(2, 1, 36, !IO),
io.write_string("Test overlapping unaligned copy.\n", !IO),
test_copy(1, 2, 36, !IO),
io.write_string("Test copy to same position.\n", !IO),
test_copy(1, 1, 36, !IO),
io.write_string("Test copy to end of bitmap.\n", !IO),
test_copy(0, 1, 166, !IO),
io.write_string("Test zero bit copies.\n", !IO),
test_copy(0, 1, 0, !IO),
test_copy(0, 167, 0, !IO),
test_set_op("union", union, !IO),
test_set_op("intersect", intersect, !IO),
test_set_op("difference", difference, !IO),
test_set_op("xor", xor, !IO),
test_binary_op("ordering", bitmap_tester.ordering, !IO),
test_binary_op("test_unify", bitmap_tester.test_unify, !IO),
test_text_io(!IO),
test_binary_io(!IO),
some [!BM] (
!:BM = bitmap.init(64, yes),
!:BM = !.BM ^ bits(32, 16) := 0b1011011100100101,
test_exception(
((pred) is semidet :-
_ = !.BM ^ bit(-1)
), !IO),
test_exception(
((pred) is semidet :-
_ = !.BM ^ bit(64)
), !IO),
test_exception(
((pred) is semidet :-
_ = !.BM ^ bit(73)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy_bits_in_bitmap(copy(!.BM), -1, 1, 32)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy_bits_in_bitmap(copy(!.BM), 33, 32, 32)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy_bits_in_bitmap(copy(!.BM), 32, 33, 32)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(-1, 32)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(33, 32)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(0, 65)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(0, -1)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(65, 0)
), !IO),
test_exception(
((pred) is semidet :-
_ = copy(!.BM) ^ bits(-1, 0)
), !IO)
).
:- pred test_exception((pred)::in((pred) is semidet),
io::di, io::uo) is cc_multi.
test_exception(Pred, !IO) :-
try((pred({}::out) is semidet :- Pred), Result),
(
Result = succeeded(_),
io.write_string("Error: test succeeded, expected exception\n", !IO)
;
Result = failed,
io.write_string("Error: test failed, expected exception\n", !IO)
;
Result = exception(Exception),
io.write_string("Found exception as expected: ", !IO),
io.write(univ_value(Exception), !IO),
io.nl(!IO)
).
% Do the copy tests to a few different bitmaps, to make sure
% correct results aren't a fluke of the original contents, and
% to check that the copy isn't disturbing bits outside the
% destination range.
%
:- pred test_copy(bit_index::in, bit_index::in, num_bits::in,
io::di, io::uo) is det.
test_copy(SrcStart, DestStart, NumBits, !IO) :-
BMLength = 167,
some [!DestBM, !SrcBM] (
io.format("Copy %i %i %i\n", [i(SrcStart), i(DestStart), i(NumBits)],
!IO),
!:SrcBM = bitmap_tester.new(BMLength, no),
BytePattern = 0b10111001,
fill_in_alternating_pattern(BytePattern, !SrcBM),
io.write_string("Copy to zeroed bitmap\n", !IO),
!:DestBM = bitmap_tester.new(BMLength, no),
!:DestBM = copy_bits(!.SrcBM, SrcStart, !.DestBM, DestStart, NumBits),
io.write_string(to_byte_string(!.DestBM), !IO),
nl(!IO),
io.write_string("Copy to filled bitmap\n", !IO),
!:DestBM = bitmap_tester.new(BMLength, yes),
!:DestBM = copy_bits(!.SrcBM, SrcStart, !.DestBM, DestStart, NumBits),
io.write_string(to_byte_string(!.DestBM), !IO),
nl(!IO),
io.write_string("Copy to alternating bitmap\n", !IO),
!:DestBM = bitmap_tester.new(BMLength, yes),
fill_in_alternating_pattern(0b10101010, !DestBM),
!:DestBM = copy_bits(!.SrcBM, SrcStart, !.DestBM, DestStart, NumBits),
io.write_string(to_byte_string(!.DestBM), !IO),
nl(!IO),
io.write_string("Copy to same bitmap\n", !IO),
!:DestBM = copy_bits_in_bitmap(!.SrcBM, SrcStart, DestStart, NumBits),
io.write_string(to_byte_string(!.DestBM), !IO),
nl(!IO)
).
:- pred test_set_op(string, (func(tbitmap, tbitmap) = tbitmap), io, io).
:- mode test_set_op(in, (func(tbitmap_ui, tbitmap_di) = tbitmap_uo is det),
di, uo) is det.
test_set_op(OpStr, Op, !IO) :-
test_binary_op(OpStr, Op,
(pred(TBM::in, !.IO::di, !:IO::uo) is det :-
io.write_string(to_byte_string(TBM ^ fst), !IO)
), !IO).
:- pred test_binary_op(string, (func(tbitmap, tbitmap) = T), io, io).
:- mode test_binary_op(in, (func(tbitmap_ui, tbitmap_di) = tbitmap_uo is det),
di, uo) is det.
:- mode test_binary_op(in, (func(tbitmap_ui, tbitmap_di) = out is det),
di, uo) is det.
test_binary_op(OpStr, Op, !IO) :-
test_binary_op(OpStr, Op, io.write, !IO).
:- pred test_binary_op(string, (func(tbitmap, tbitmap) = T),
pred(T, io, io), io, io).
:- mode test_binary_op(in, (func(tbitmap_ui, tbitmap_di) = tbitmap_uo is det),
(pred(in, di, uo) is det), di, uo) is det.
:- mode test_binary_op(in, (func(tbitmap_ui, tbitmap_di) = out is det),
(pred(in, di, uo) is det), di, uo) is det.
test_binary_op(OpStr, Op, Writer, !IO) :-
test_binary_op(8, OpStr, Op, Writer, !IO),
test_binary_op(64, OpStr, Op, Writer, !IO).
:- pred test_binary_op(int, string, (func(tbitmap, tbitmap) = T),
pred(T, io, io), io, io).
:- mode test_binary_op(in, in,
(func(tbitmap_ui, tbitmap_di) = tbitmap_uo is det),
(pred(in, di, uo) is det), di, uo) is det.
:- mode test_binary_op(in, in, (func(tbitmap_ui, tbitmap_di) = out is det),
(pred(in, di, uo) is det), di, uo) is det.
test_binary_op(BMLength, OpStr, Op, Writer, !IO) :-
ZeroedBM = bitmap_tester.new(BMLength, no),
OnesBM = bitmap_tester.new(BMLength, yes),
PatternBM0 = bitmap_tester.new(BMLength, no),
BytePattern = 0b10111001,
fill_in_alternating_pattern(BytePattern, PatternBM0, PatternBM),
AlternatingBM0 = bitmap_tester.new(BMLength, yes),
fill_in_alternating_pattern(0b10101010, AlternatingBM0, AlternatingBM),
test_binary_op_2("zeroes", ZeroedBM, OpStr, Op,
"pattern", PatternBM, Writer, !IO),
test_binary_op_2("ones", OnesBM, OpStr, Op,
"pattern", PatternBM, Writer, !IO),
test_binary_op_2("pattern", PatternBM, OpStr, Op,
"ones", OnesBM, Writer, !IO),
test_binary_op_2("pattern", PatternBM, OpStr, Op,
"zeroes", ZeroedBM, Writer, !IO),
test_binary_op_2("pattern", PatternBM, OpStr, Op,
"alternating", AlternatingBM, Writer, !IO),
test_binary_op_2("pattern", PatternBM, OpStr, Op,
"pattern", PatternBM, Writer, !IO),
test_binary_op_2("alternating", AlternatingBM, OpStr, Op,
"alternating", AlternatingBM, Writer, !IO).
:- pred test_binary_op_2(string, tbitmap, string, (func(tbitmap, tbitmap) = T),
string, tbitmap, pred(T, io, io), io, io).
:- mode test_binary_op_2(in, tbitmap_ui,
in, (func(tbitmap_ui, tbitmap_di) = tbitmap_uo is det),
in, tbitmap_ui, (pred(in, di, uo) is det), di, uo) is det.
:- mode test_binary_op_2(in, tbitmap_ui,
in, (func(in, in) = out is det),
in, tbitmap_ui, (pred(in, di, uo) is det), di, uo) is det.
test_binary_op_2(BMStr1, BM1, OpStr, Op, BMStr2, BM2, Writer, !IO) :-
io.write_string(OpStr, !IO),
io.write_string("(", !IO),
io.write_string(BMStr1, !IO),
io.write_string(", ", !IO),
io.write_string(BMStr2, !IO),
io.write_string(") = ", !IO),
Writer(BM1 `Op` copy(BM2), !IO),
io.nl(!IO).
:- pred test_binary_io(io::di, io::uo) is det.
test_binary_io(!IO) :-
FileName = "bitmap_test_output",
BMa0 = bitmap.init(64, yes),
BMa = BMa0 ^ bits(32, 16) := 0b1011011100100101,
BMb0 = bitmap.init(47, yes),
BMb = BMb0 ^ bits(11, 16) := 0b1011010110100101,
io.open_binary_output(FileName, OpenRes, !IO),
(
OpenRes = ok(Stream),
bitmap.write_bitmap(Stream, BMa, !IO),
bitmap.write_bitmap_range(Stream, BMb, 2, 3, !IO),
io.close_binary_output(Stream, !IO),
io.open_binary_input(FileName, OpenInputRes, !IO),
(
OpenInputRes = ok(IStream),
InputBMa0 = bitmap.init(64, no),
bitmap.read_bitmap(IStream, InputBMa0, InputBMa,
BytesReadA, ReadResA, !IO),
( if
ReadResA = ok,
BytesReadA = 8,
InputBMa = BMa
then
io.write_string("First read succeeded\n", !IO)
else
io.write_string("First read failed\n", !IO)
),
InputBMb0 = bitmap.init(32, no),
bitmap.read_bitmap(IStream, InputBMb0, InputBMb1,
BytesReadB, ReadResB, !IO),
% Check that we are at eof.
bitmap.read_bitmap(IStream, InputBMb1, InputBMb,
BytesReadB2, ReadResB2, !IO),
( if
ReadResB = ok,
BytesReadB = 3,
ReadResB2 = ok,
BytesReadB2 = 0,
BMb ^ bits(16, 24) = InputBMb ^ bits(0, 24)
then
io.write_string("Second read succeeded\n", !IO)
else
io.write_string("Second read failed\n", !IO)
),
io.close_binary_input(IStream, !IO),
io.file.remove_file(FileName, _, !IO)
;
OpenInputRes = error(Error),
throw(Error)
)
;
OpenRes = error(Error),
throw(Error)
).
:- pred test_text_io(io::di, io::uo) is det.
test_text_io(!IO) :-
FileName = "bitmap_test_output2",
BMa0 = bitmap.init(64, yes),
BMa = BMa0 ^ bits(32, 16) := 0b1011011100100101,
BMb0 = bitmap.init(47, yes),
BMb = BMb0 ^ bits(11, 16) := 0b1011010110100101,
io.write_string("BMa = ", !IO),
io.write(BMa, !IO),
io.write_string(".\n", !IO),
io.write_string("BMb = ", !IO),
io.write(BMb, !IO),
io.write_string(".\n", !IO),
io.open_output(FileName, OpenRes, !IO),
(
OpenRes = ok(Stream),
io.write(Stream, BMa, !IO),
io.write_string(Stream, ".\n", !IO),
io.write(Stream, BMb, !IO),
io.write_string(Stream, ".\n", !IO),
io.close_output(Stream, !IO),
io.open_input(FileName, OpenInputRes, !IO),
(
OpenInputRes = ok(IStream),
io.read(IStream, ReadResA, !IO),
( if ReadResA = ok(BMa) then
io.write_string("First read succeeded\n", !IO)
else
io.write_string("First read failed\n", !IO),
io.close_input(IStream, !IO),
throw(ReadResA)
),
io.read(IStream, ReadResB, !IO),
( if ReadResB = ok(BMb) then
io.write_string("Second read succeeded\n", !IO)
else
io.write_string("Second read failed\n", !IO),
io.close_input(IStream, !IO),
throw(ReadResB)
),
io.close_input(IStream, !IO),
io.file.remove_file(FileName, _, !IO)
;
OpenInputRes = error(Error),
throw(Error)
)
;
OpenRes = error(Error),
throw(Error)
).
:- pred fill_in_alternating_pattern(byte::in,
tbitmap::tbitmap_di, tbitmap::tbitmap_uo) is det.
fill_in_alternating_pattern(Byte, !BM) :-
NumBits = !.BM ^ fst ^ num_bits,
NumBytes = NumBits / bits_per_byte,
fill_in_alternating_pattern(0, NumBytes, Byte, !BM),
LastByteNumBits = NumBits `rem` bits_per_byte,
( if LastByteNumBits \= 0 then
!:BM = !.BM ^ bits(NumBytes * bits_per_byte, LastByteNumBits) := Byte
else
true
).
:- pred fill_in_alternating_pattern(byte_index::in, num_bytes::in, byte::in,
tbitmap::tbitmap_di, tbitmap::tbitmap_uo) is det.
fill_in_alternating_pattern(Index, NumBytes, Pattern, !BM) :-
( if Index >= NumBytes then
true
else
( if Index rem 2 = 0 then
BytePattern = Pattern
else
BytePattern = \Pattern
),
!:BM = !.BM ^ byte(Index) := BytePattern,
fill_in_alternating_pattern(Index + 1, NumBytes, Pattern, !BM)
).
:- pred write_binary_string(word::in, io::di, io::uo) is det.
write_binary_string(Int, !IO) :-
io.write_string(binary_string(Int), !IO).
:- func binary_string(word) = string.
binary_string(Int) = string.int_to_base_string(Int, 2).
:- pred write_bitmap_result_error(bitmap_result_error::in,
io::di, io::uo) is det.
write_bitmap_result_error(query(Op, Input, OtherArgs, Output), !IO) :-
io.write_string("Error in `", !IO),
io.write_string(Op, !IO),
io.write_string("(", !IO),
io.write(OtherArgs, !IO),
io.write_string(")'\ninput bitmap: ", !IO),
io.write_string(to_byte_string(Input), !IO),
io.nl(!IO),
io.write_string("output = ", !IO),
io.write(Output ^ fst, !IO),
io.nl(!IO),
io.write_string("expected output = ", !IO),
io.write(Output ^ snd, !IO),
io.nl(!IO).
write_bitmap_result_error(binary_query(Op, Input1, Input2, OtherArgs, Output),
!IO) :-
io.write_string("Error in `", !IO),
io.write_string(Op, !IO),
io.write_string("(", !IO),
io.write(OtherArgs, !IO),
io.write_string(")'\ninput bitmap 1: ", !IO),
io.write_string(to_byte_string(Input1), !IO),
io.write_string("\ninput bitmap 2: ", !IO),
io.write_string(to_byte_string(Input2), !IO),
io.nl(!IO),
io.write_string("output = ", !IO),
io.write(Output ^ fst, !IO),
io.nl(!IO),
io.write_string("expected output = ", !IO),
io.write(Output ^ snd, !IO),
io.nl(!IO).
write_bitmap_result_error(one_argument(Op, Input, OtherArgs, Output), !IO) :-
io.write_string("Error in `", !IO),
io.write_string(Op, !IO),
io.write_string("(", !IO),
io.write(OtherArgs, !IO),
io.write_string(")'\ninput bitmap: ", !IO),
io.write_string(to_byte_string(Input), !IO),
io.nl(!IO),
io.write_string("output = ", !IO),
io.write_string(to_byte_string(Output ^ fst), !IO),
io.nl(!IO),
io.write_string("expected output = ", !IO),
io.write_string(to_byte_string(Output ^ snd), !IO),
io.nl(!IO).
write_bitmap_result_error(two_arguments(Op, Input1, Input2, OtherArgs, Output),
!IO) :-
io.write_string("Error in `", !IO),
io.write_string(Op, !IO),
io.write_string("(", !IO),
io.write(OtherArgs, !IO),
io.write_string(")'\ninput bitmap 1: ", !IO),
io.write_string(to_byte_string(Input1), !IO),
io.nl(!IO),
io.write_string("\ninput bitmap 2: ", !IO),
io.write_string(to_byte_string(Input2), !IO),
io.nl(!IO),
io.write_string("output = ", !IO),
io.write_string(to_byte_string(Output ^ fst), !IO),
io.nl(!IO),
io.write_string("expected output = ", !IO),
io.write_string(to_byte_string(Output ^ snd), !IO),
io.nl(!IO).