// Наработки в области длинной арифметики (даже не пытался оптимизировать)
// Число представляется в виде цифр в 100^n-ричной системе
// Показатель n определяется размером ячейки, отведенной для хранения цифры
// Формат выбран как наиболее удобный для вывода десятичных чисел (не требует деления по модулю 10)
// Реализованы функции сложения и умножения цифр в такой системе счисления
// CopyLeft (c) 2015 Alexey Kuzminov
program VeryLongInteger;

type
  // Тип ячейки может быть любым:
  //   Integer, ShortInt, SmallInt, Longint, Byte, Word, LongWord, Cardinal
  // Теоретически возможен QWord и Int64, но не компилится FullTest
  TDigit = type SmallInt; // размер - 2 байта

var
  MaxDigit: TDigit;  // максимальное число, кратное степени 100 и не превосходящее High(TDigit)
  HalfDigit: TDigit; // половина MaxDigit
  SqrtDigit: TDigit; // квадратный корень из MaxDigit
  DigitLength: Integer; // длина текста при выводе цифры

// Определяет MaxDigit и другие константы
procedure Init;
var
  t: TDigit;
begin
  t := High(TDigit);
  MaxDigit := 1;
  SqrtDigit := 1;
  DigitLength := 0;
  while t > 100 do begin
    t := t div 100;
    MaxDigit := MaxDigit * 100;
    SqrtDigit := SqrtDigit * 10;
    Inc(DigitLength, 2);
  end;
  HalfDigit := MaxDigit div 2;
end;

// a := (a + b) mod MaxDigit, результат 1 - есть переполнение, 0 - нет
function Add(var a: TDigit; b: TDigit): TDigit; 
var
  p: Byte;
begin
  // Во избежание переполнения ЗАРАНЕЕ отнимем половину MaxDigit от a и b
  p := 0;
  if (a > HalfDigit) then begin Dec(a, HalfDigit); Inc(p); end;
  if (b > HalfDigit) then begin Dec(b, HalfDigit); Inc(p); end;
  
  // Теперь эта операция переполнения не вызовет
  Inc(a, b);
  if p = 0 then Add := 0       // оба слагаемых маленькие, переполнения нет
  else if p = 2 then Add := 1  // оба слагаемых большие, переполнение гарантировано
  else if a < HalfDigit then begin  // одно слагаемое большое, но переполнения нет
  	Inc(a, HalfDigit);
  	Add := 0
  end
  else begin  // сумма превысила MaxDigit, поэтому переполнеие
  	Dec(a, HalfDigit);
  	Add := 1
  end;
end;

// a := (a * b) mod MaxDigit, результат (a * b) div MaxDigit - старшая часть
function Mul(var a: TDigit; b: TDigit): TDigit;
var
  ah, al, bh, bl: TDigit;
  t: TDigit;
begin
  // разложим a = ah*SqrtDigit + al и b = bh*SqrtDigit + bl
  al := a mod SqrtDigit; ah := a div SqrtDigit;
  bl := b mod SqrtDigit; bh := b div SqrtDigit;

  // результат: ah*bh*SqrtDigit^2 + (ah*bl+bh*al)*SqrtDigit + al*bl
  // в процессе сложения нужно не допускать переполнения MaxDigit
  
  // крайние слагаемые перепонения не вызовут
  a := al*bl;
  Mul := ah*bh; // SqrtDigit^2 = MaxDigit
  
  // а вот со средним всё интереснее...
  t := ah*bl;
  if Add(t, bh*al) > 0 then Inc(Mul, SqrtDigit); // сложение может вызвать переполнение
  // среднее слагаемое бьется на части: старшая в результат, mладшая в a
  Inc(Mul, t div SqrtDigit);                     
  Inc(Mul, Add(a, (t mod SqrtDigit)*SqrtDigit)); // и тут переполнение
  // Замечание: ни при каком вызове Inc Mul не переполнится (не станет > MaxDigit)
end;

// дополняет нужное количество нулей левее числа
function DigitToStr(a: TDigit): String;
begin
  Str(a, DigitToStr);
  while DigitLength > Length(DigitToStr) do
    DigitToStr := '0'+DigitToStr;
end;

// значение с коррекцией ошибок
function MakeDigit(Value: Int64): TDigit;
begin
  MakeDigit := Value mod MaxDigit;
  if MakeDigit < 0 then Inc(MakeDigit, MaxDigit); 
end;

var
  a, b, c, d: TDigit;

// процедура полномасштабного тестирования
procedure FullTest;
var
  r, t: Int64;
begin
  for a := 0 to MaxDigit - 1 do
    for b := 0 to MaxDigit - 1 do begin
      c := a;
      d := Add(c, b);
      t := Int64(d)*MaxDigit + Int64(c);
      r := Int64(a)+Int64(b);
      if (t <> r) or (c < 0) then begin
        WriteLn('Ошибка сложения: ', a, ' + ', b, ' = ', d, '''', DigitToStr(c),
          ' должно быть: ', r);
        Exit;
      end;
      
      c := a;
      d := Mul(c, b);
      t := Int64(d)*MaxDigit + Int64(c);
      r := Int64(a)*Int64(b);
      if (t <> r) or (c < 0) then begin
        WriteLn('Ошибка умножения: ', a, ' * ', b, ' = ', d, '''', DigitToStr(c),
          ' должно быть: ', r);
        Exit;
      end;
    end;
  WriteLn('Проверка сложения и умножения успешна');
end;

begin
  Init;
  
  WriteLn('MaxDigit = ', MaxDigit, ' (length = ', DigitLength, ')');
  
  a := MakeDigit(912223443222);
  b := MakeDigit(123223448326);
  Write(DigitToStr(a), ' + ', DigitToStr(b), ' = ');
  Write(DigitToStr(Add(a, b)), ''''); WriteLn(DigitToStr(a));

  a := MakeDigit(673993403459);
  b := MakeDigit(381212100238);
  Write(DigitToStr(a), ' * ', DigitToStr(b), ' = ');
  Write(DigitToStr(Mul(a, b)), ''''); WriteLn(DigitToStr(a));

  // полная проверка сложения и умножения, только для коротких типов
  if SizeOf(TDigit) <= 1 then
    FullTest;
end.