predicate ValidBitString(s: string)
forall i | 0 <= i < |s| :: s[i] == '0' || s[i] == '1'
method normalizeBitString(s: string) returns(s: string)
var firstOne :| 0 <= firstOne <= |s|;
if (forall i | 0 <= i < |s| :: s[i] == '0') then
method str2int(s: string) returns (n: nat)
requires ValidBitString(s)
return if |s| == 0 { 0 } else { 2 * str2int(s[0..|s|-1]) + (if s[|s|-1] == '1' {1} else{0})}
method add(s1: string, s2: string) returns (res: string)
requires ValidBitString(s1) && ValidBitString(s2)
ensures ValidBitString(res)
ensures str2int(res) == str2int(s1) + str2int(s2)
var x := normalizeBitString(s1);
var y := normalizeBitString(s2);
while i >= 0 || j >= 0 || carry != 0
invariant 0 <= carry <= 1
invariant i <= |x| - 1 && j <= |y| - 1
bitX := if x[i] == '1' then 1 else 0;
bitY := if y[j] == '1' then 1 else 0;
var sum := bitX + bitY + carry;
if i >= 0 { i := i - 1; }
if j >= 0 { j := j - 1; }
res := normalizeBitString(rev);
method leftShift(s: string, count: nat) returns(s: string)
s + (if count == 0 then "" else leftShift("0", count - 1))
method mul(s1: string, s2: string) returns (res: string)
requires ValidBitString(s1) && ValidBitString(s2)
ensures ValidBitString(res)
ensures str2int(res) == str2int(s1) * str2int(s2)
var x := normalizeBitString(s1);
var y := normalizeBitString(s2);
if x == "0" || y == "0" {
var partial := leftShift(x, shiftCount);
product := add(product, partial);
shiftCount := shiftCount + 1;
// ----------------------------------------------------
// 4) sub: string-based subtraction (s1 >= s2)
// ----------------------------------------------------
method sub(s1: string, s2: string) returns (res: string)
requires ValidBitString(s1) && ValidBitString(s2)
requires str2int(s1) >= str2int(s2)
ensures ValidBitString(res)
ensures str2int(res) == str2int(s1) - str2int(s2)
var x := normalizeBitString(s1);
var y := normalizeBitString(s2);
// If y == "0", the difference is x
// If x == y, the difference is "0"
var i := |x| - 1; // pointer on x
var j := |y| - 1; // pointer on y
var sb := new char[0]; // reversed result
invariant 0 <= borrow <= 1
bitX := if x[i] == '1' then 1 else 0;
bitY := if y[j] == '1' then 1 else 0;
var diff := bitX - bitY - borrow;
if i >= 0 { i := i - 1; }
if j >= 0 { j := j - 1; }
res := normalizeBitString(rev);
// ----------------------------------------------------
// 2) int2str: nat -> bit-string (reference function)
// - no leading zeros otherwise
// ----------------------------------------------------
method int2str(n: nat) returns(s: string)
// Recursively build from most significant bits.
// The last character added is (n % 2).
int2str(n / 2) + (if n % 2 == 0 then "0" else "1")
// Below is a self-contained Dafny program that:
// - Represents natural numbers as binary strings consisting only of `'0'` and `'1'`.
// - Has two **conversion** functions:
// 1. `str2int(s)`: Convert a valid bit-string `s` into a natural number.
// 2. `int2str(n)`: Convert a natural number `n` into its binary representation (with no leading zeros except if `n = 0`).
// - Has three **pure string-based** arithmetic methods, each **not** using `str2int` or `int2str` inside the method body:
// 1. `add(s1, s2)`: Returns the bit-string representing the sum of `s1` and `s2`.
// 2. `sub(s1, s2)`: Returns the bit-string representing `s1 - s2`, assuming `s1 >= s2`.
// 3. `mul(s1, s2)`: Returns the bit-string representing the product `s1 * s2`.
print "a = ", a, " (decimal=", str2int(a), ")\n";
print "b = ", b, " (decimal=", str2int(b), ")\n";
print "a + b = ", s, " (decimal=", str2int(s), ")\n";
print "b - a = ", d, " (decimal=", str2int(d), ")\n";
print "a * b = ", m, " (decimal=", str2int(m), ")\n";
print a, " + 0 = ", sumZ, " (decimal=", str2int(sumZ), ")\n";
print "9999 -> ", sN, " -> ", str2int(sN), "\n";