include "bigint.s7i";
const type: bigRational is new object struct
var bigInteger: numerator is 0_;
var bigInteger: denominator is 1_;
end struct;
const proc: normalize (inout bigRational: number) is func
begin
if number.denominator < 0_ then
number.numerator := -number.numerator;
number.denominator := -number.denominator;
end if;
end func;
const proc: reduce (inout bigRational: number) is func
local
var bigInteger: gcd is 0_;
begin
gcd := gcd(number.numerator, number.denominator);
if gcd >= 2_ then
number.numerator := number.numerator div gcd;
number.denominator := number.denominator div gcd;
end if;
end func;
const func bigRational: (in bigInteger: numerator) / (in bigInteger: denominator) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := numerator;
aRational.denominator := denominator;
normalize(aRational);
reduce(aRational);
end func;
const func bigRational: + (in bigRational: number) is
return number;
const func bigRational: - (in bigRational: number) is func
result
var bigRational: negatedNumber is bigRational.value;
begin
negatedNumber.numerator := -number.numerator;
negatedNumber.denominator := number.denominator;
end func;
const func bigRational: (in bigRational: summand1) + (in bigRational: summand2) is func
result
var bigRational: sum is bigRational.value;
local
var bigInteger: gcd_denominator is 0_;
begin
gcd_denominator := gcd(summand1.denominator, summand2.denominator);
sum.numerator := (summand1.numerator * summand2.denominator +
summand2.numerator * summand1.denominator) div gcd_denominator;
sum.denominator := summand1.denominator div gcd_denominator * summand2.denominator;
reduce(sum);
end func;
const func bigRational: (in bigRational: minuend) - (in bigRational: subtrahend) is func
result
var bigRational: difference is bigRational.value;
local
var bigInteger: gcd_denominator is 0_;
begin
gcd_denominator := gcd(minuend.denominator, subtrahend.denominator);
difference.numerator := (minuend.numerator * subtrahend.denominator -
subtrahend.numerator * minuend.denominator) div gcd_denominator;
difference.denominator := minuend.denominator div gcd_denominator * subtrahend.denominator;
reduce(difference);
end func;
const func bigRational: (in bigRational: factor1) * (in bigRational: factor2) is func
result
var bigRational: product is bigRational.value;
local
var bigInteger: gcd1 is 0_;
var bigInteger: gcd2 is 0_;
begin
gcd1 := gcd(factor1.numerator, factor2.denominator);
gcd2 := gcd(factor2.numerator, factor1.denominator);
product.numerator := (factor1.numerator div gcd1) * (factor2.numerator div gcd2);
product.denominator := (factor1.denominator div gcd2) * (factor2.denominator div gcd1);
end func;
const func bigRational: (in bigRational: dividend) / (in bigRational: divisor) is func
result
var bigRational: quotient is bigRational.value;
local
var bigInteger: gcd1 is 0_;
var bigInteger: gcd2 is 0_;
begin
gcd1 := gcd(dividend.numerator, divisor.numerator);
gcd2 := gcd(divisor.denominator, dividend.denominator);
quotient.numerator := (dividend.numerator div gcd1) * (divisor.denominator div gcd2);
quotient.denominator := (dividend.denominator div gcd2) * (divisor.numerator div gcd1);
normalize(quotient);
end func;
const proc: (inout bigRational: number) +:= (in bigRational: delta) is func
local
var bigInteger: gcd_denominator is 0_;
begin
gcd_denominator := gcd(number.denominator, delta.denominator);
number.numerator := (number.numerator * delta.denominator +
delta.numerator * number.denominator) div gcd_denominator;
number.denominator *:= delta.denominator div gcd_denominator;
reduce(number);
end func;
const proc: (inout bigRational: number) -:= (in bigRational: delta) is func
local
var bigInteger: gcd_denominator is 0_;
begin
gcd_denominator := gcd(number.denominator, delta.denominator);
number.numerator := (number.numerator * delta.denominator -
delta.numerator * number.denominator) div gcd_denominator;
number.denominator *:= delta.denominator div gcd_denominator;
reduce(number);
end func;
const proc: (inout bigRational: number) *:= (in bigRational: factor) is func
begin
number.numerator *:= factor.numerator;
number.denominator *:= factor.denominator;
reduce(number);
end func;
const proc: (inout bigRational: number) /:= (in bigRational: divisor) is func
begin
number.numerator *:= divisor.denominator;
number.denominator *:= divisor.numerator;
normalize(number);
reduce(number);
end func;
const func bigRational: (in bigRational: base) ** (in integer: exponent) is func
result
var bigRational: power is bigRational.value;
begin
if exponent >= 0 then
power.numerator := base.numerator ** exponent;
power.denominator := base.denominator ** exponent;
else
power.numerator := base.denominator ** (-exponent);
power.denominator := base.numerator ** (-exponent);
normalize(power);
end if;
end func;
const func boolean: (in bigRational: number1) = (in bigRational: number2) is
return number1.numerator = number2.numerator and
number1.denominator = number2.denominator;
const func boolean: (in bigRational: number1) <> (in bigRational: number2) is
return number1.numerator <> number2.numerator or
number1.denominator <> number2.denominator;
const func boolean: (in bigRational: number1) < (in bigRational: number2) is
return number1.numerator * number2.denominator <
number2.numerator * number1.denominator;
const func boolean: (in bigRational: number1) > (in bigRational: number2) is
return number1.numerator * number2.denominator >
number2.numerator * number1.denominator;
const func boolean: (in bigRational: number1) <= (in bigRational: number2) is
return number1.numerator * number2.denominator <=
number2.numerator * number1.denominator;
const func boolean: (in bigRational: number1) >= (in bigRational: number2) is
return number1.numerator * number2.denominator >=
number2.numerator * number1.denominator;
const func integer: compare (in bigRational: number1, in bigRational: number2) is
return compare(number1.numerator * number2.denominator,
number2.numerator * number1.denominator);
const func integer: hashCode (in bigRational: number) is
return hashCode(number.numerator) mod 16#40000000 + hashCode(number.denominator) mod 16#40000000;
const func bigRational: rat (in bigInteger: number) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := number;
aRational.denominator := 1_;
end func;
const func bigRational: bigRational (in integer: number) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := bigInteger(number);
aRational.denominator := 1_;
end func;
const func bigRational: (attr bigRational) conv (in integer: number) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := bigInteger(number);
aRational.denominator := 1_;
end func;
const func bigRational: bigRational (in bigInteger: number) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := number;
aRational.denominator := 1_;
end func;
const func bigRational: (attr bigRational) conv (in bigInteger: number) is func
result
var bigRational: aRational is bigRational.value;
begin
aRational.numerator := number;
aRational.denominator := 1_;
end func;
const func bigRational: abs (in bigRational: number) is func
result
var bigRational: absoluteValue is bigRational.value;
begin
absoluteValue.numerator := abs(number.numerator);
absoluteValue.denominator := number.denominator;
end func;
const func bigInteger: floor (in bigRational: number) is
return number.numerator mdiv number.denominator;
const func bigInteger: ceil (in bigRational: number) is
return -(number.numerator mdiv -number.denominator);
const func bigInteger: trunc (in bigRational: number) is
return number.numerator div number.denominator;
const func bigInteger: round (in bigRational: number) is func
result
var bigInteger: int_val is 0_;
begin
if number.numerator >= 0_ then
int_val := (2_ * number.numerator + number.denominator) div (2_ * number.denominator);
else
int_val := (2_ * number.numerator - number.denominator) div (2_ * number.denominator);
end if;
end func;
const func bigRational: round10 (in bigRational: number, in integer: precision) is func
result
var bigRational: rounded is bigRational.value;
begin
if precision < 0 then
rounded.numerator := (abs(number.numerator) div 10_ ** pred(-precision) +
number.denominator * 5_) div (number.denominator * 10_) *
10_ ** (-precision);
rounded.denominator := 1_;
else
rounded.numerator := (abs(number.numerator) * 10_ ** succ(precision) +
number.denominator * 5_) div (number.denominator * 10_);
rounded.denominator := 10_ ** precision;
end if;
if number.numerator < 0_ then
rounded.numerator := -rounded.numerator;
end if;
end func;
const type: BIG_REMAINDER_HASH_TYPE is hash [bigInteger] integer;
const func string: str (in bigRational: number) is func
result
var string: stri is "";
local
var BIG_REMAINDER_HASH_TYPE: remainderHash is BIG_REMAINDER_HASH_TYPE.value;
var quotRem: quotAndRem is quotRem.value;
var integer: pos is 0;
begin
if number.denominator = 0_ then
if number.numerator > 0_ then
stri := "Infinity";
elsif number.numerator = 0_ then
stri := "NaN";
else
stri := "-Infinity";
end if;
else
quotAndRem := abs(number.numerator) divRem number.denominator;
stri := str(quotAndRem.quotient);
stri &:= ".";
if quotAndRem.remainder = 0_ then
stri &:= "0";
else
repeat
remainderHash @:= [quotAndRem.remainder] length(stri);
quotAndRem.remainder *:= 10_;
quotAndRem := quotAndRem.remainder divRem number.denominator;
stri &:= str(quotAndRem.quotient);
until quotAndRem.remainder = 0_ or quotAndRem.remainder in remainderHash;
if quotAndRem.remainder <> 0_ then
pos := remainderHash[quotAndRem.remainder];
stri := stri[.. pos] & "(" & stri[succ(pos) ..] & ")";
end if;
end if;
if number.numerator < 0_ then
stri := "-" & stri;
end if;
end if;
end func;
const func string: literal (in bigRational: number) is
return "bigRational(" & literal(str(number)) & ")";
const func string: fraction (in bigRational: number) is
return str(number.numerator) & "/" & str(number.denominator);
const func string: (in bigRational: number) digits (in integer: precision) is func
result
var string: stri is "";
local
var bigInteger: mantissa is 0_;
begin
if precision < 0 then
raise RANGE_ERROR;
elsif number.denominator = 0_ then
if number.numerator > 0_ then
stri := "Infinity";
elsif number.numerator = 0_ then
stri := "NaN";
else
stri := "-Infinity";
end if;
else
mantissa := (abs(number.numerator) * 10_ ** succ(precision) +
number.denominator * 5_) div (number.denominator * 10_);
stri := str(mantissa);
if precision >= length(stri) then
stri := "0" mult (precision - length(stri) + 1) & stri;
end if;
if precision <> 0 then
stri := stri[ .. length(stri) - precision] & "." &
stri[length(stri) - precision + 1 .. ];
end if;
if number.numerator < 0_ and mantissa <> 0_ then
stri := "-" & stri;
end if;
end if;
end func;
const func integer: decimalExponent (in bigRational: number) is func
result
var integer: exponent is 0;
begin
if abs(number.numerator) >= number.denominator then
exponent := ord(log10(abs(number.numerator) div number.denominator));
else
exponent := -ord(log10(number.denominator div abs(number.numerator))) - 1;
end if;
end func;
const func string: (in bigRational: number) sci (in integer: precision) is func
result
var string: stri is "";
local
var integer: exponent is 0;
var bigInteger: mantissa is 0_;
begin
if precision < 0 then
raise RANGE_ERROR;
elsif number.denominator = 0_ then
if number.numerator > 0_ then
stri := "Infinity";
elsif number.numerator = 0_ then
stri := "NaN";
else
stri := "-Infinity";
end if;
elsif number.numerator = 0_ then
if precision = 0 then
stri := "0e+0";
else
stri := "0." & "0" mult precision & "e+0";
end if;
else
exponent := decimalExponent(number);
if succ(precision) >= exponent then
mantissa := (abs(number.numerator) * 10_ ** succ(precision - exponent) +
number.denominator * 5_) div (number.denominator * 10_);
else
mantissa := (abs(number.numerator) div 10_ ** pred(exponent - precision) +
number.denominator * 5_) div (number.denominator * 10_);
end if;
stri := str(mantissa);
if length(stri) > succ(precision) then
incr(exponent);
stri := stri[.. succ(precision)];
end if;
if precision <> 0 then
stri := stri[1 fixLen 1] & "." & stri[2 .. ];
end if;
if exponent >= 0 then
stri &:= "e+" <& exponent;
else
stri &:= "e" <& exponent;
end if;
if number.numerator < 0_ then
stri := "-" & stri;
end if;
end if;
end func;
const func bigRational: bigRational (in var string: stri) is func
result
var bigRational: aRational is bigRational.value;
local
var boolean: negative is FALSE;
var string: fraction is "";
var string: period is "";
begin
if stri <> "" then
if stri[1] = '-' then
stri := stri[2 ..];
negative := TRUE;
elsif stri[1] = '+' then
stri := stri[2 ..];
end if;
aRational.numerator := bigInteger(getint(stri));
if stri[1] = '/' then
stri := stri[2 ..];
aRational.denominator := bigInteger(getint(stri));
reduce(aRational);
elsif stri[1] = '.' then
stri := stri[2 ..];
if startsWith(stri, "(") then
stri := stri[2 ..];
period := getint(stri);
aRational.denominator := 1_;
aRational +:= bigInteger(period) / pred(10_ ** length(period));
if stri[1] = ')' then
stri := stri[2 ..];
end if;
else
fraction := getint(stri);
aRational.denominator := 10_ ** length(fraction);
aRational.numerator *:= aRational.denominator;
aRational.numerator +:= bigInteger(fraction);
if startsWith(stri, "(") then
stri := stri[2 ..];
period := getint(stri);
aRational +:= bigInteger(period) / (pred(10_ ** length(period)) * aRational.denominator);
if stri[1] = ')' then
stri := stri[2 ..];
end if;
end if;
end if;
reduce(aRational);
end if;
if stri <> "" then
raise RANGE_ERROR;
elsif negative then
aRational.numerator := -aRational.numerator;
end if;
else
raise RANGE_ERROR;
end if;
end func;
const func bigRational: (attr bigRational) parse (in string: stri) is
return bigRational(stri);
enable_io(bigRational);
DECLARE_TERNARY(bigRational);
DECLARE_MIN_MAX(bigRational);
const type: _bigRationalArray is array bigRational;