• GNAT Community 2020 (20200818-93): Big_Integer

    From =?UTF-8?B?RnJhbmsgSsO4cmdlbiBKw7hyZ@21:1/5 to All on Fri Jun 30 12:28:22 2023
    Hi

    I'm running the below program with GNAT Community 2020 (20200818-93)
    on Windows 11 Home.
    I have some problems trying to save big numbers to a file - so I noticed this behaviour:
    If I open Test.dat in Visual Studio Hex editor, it seems like this program saves this big number with a different bit pattern each time.
    Is that as expected?
    I do have some problems reading back the big numbers in my real code.
    When I compile I get the warning: "Ada.Numerics.Big_Numbers.Big_Integers" is an Ada 202x unit.

    Frank

    --
    with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
    with Ada.Numerics.Big_Numbers.Big_Integers;

    procedure Test is

    B1 : Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer;
    F1 : File_Type;
    S1 : Stream_Access;
    begin
    B1 := 1;

    Ada.Streams.Stream_IO.Create (F1, Out_File, "Test.dat");
    S1 := Ada.Streams.Stream_IO.Stream (F1);
    Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Write(S1, B1);
    Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Output(S1, B1);
    Ada.Streams.Stream_IO.Close (F1);
    end Test;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dmitry A. Kazakov@21:1/5 to All on Fri Jun 30 23:07:10 2023
    On 2023-06-30 21:28, Frank Jørgen Jørgensen wrote:

    I'm running the below program with GNAT Community 2020 (20200818-93)
    on Windows 11 Home.
    I have some problems trying to save big numbers to a file - so I noticed this behaviour:
    If I open Test.dat in Visual Studio Hex editor, it seems like this program saves this big number with a different bit pattern each time.
    Is that as expected?
    I do have some problems reading back the big numbers in my real code.
    When I compile I get the warning: "Ada.Numerics.Big_Numbers.Big_Integers" is an Ada 202x unit.

    --
    with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
    with Ada.Numerics.Big_Numbers.Big_Integers;

    procedure Test is

    B1 : Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer;
    F1 : File_Type;
    S1 : Stream_Access;
    begin
    B1 := 1;

    Ada.Streams.Stream_IO.Create (F1, Out_File, "Test.dat");
    S1 := Ada.Streams.Stream_IO.Stream (F1);
    Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Write(S1, B1);
    Ada.Numerics.Big_Numbers.Big_Integers.Big_Integer'Output(S1, B1);
    Ada.Streams.Stream_IO.Close (F1);
    end Test;

    As a general rule, you should never use predefined implementations of
    stream attributes except for Stream_Element or Character. Anything else
    you must always override or else not use.

    If you want to serialize signed integers use some portable format for
    it. E.g. a chained encoding.

    Here is a test program for a straightforward implementation of chained store/restore:
    -------------------------
    with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
    with Ada.Numerics.Big_Numbers.Big_Integers;
    with Ada.Exceptions;
    with Ada.IO_Exceptions;

    procedure Test is

    use Ada.Streams;
    use Ada.Numerics.Big_Numbers.Big_Integers;
    use Ada.Exceptions;
    use Ada.Streams.Stream_IO;

    Two : constant Big_Integer := To_Big_Integer (2);

    package Conversions is new Unsigned_Conversions (Stream_Element);
    use Conversions;

    function Get
    ( Stream : in out Root_Stream_Type'Class
    ) return Big_Integer is
    Result : Big_Integer;
    Power : Natural := 6;
    Negative : Boolean;
    Buffer : Stream_Element_Array (1..1);
    Last : Stream_Element_Offset;
    This : Stream_Element renames Buffer (1);
    begin
    Stream.Read (Buffer, Last);
    if Last /= 1 then
    raise End_Error;
    end if;
    Result := To_Big_Integer ((This and 2#0111_1110#) / 2);
    Negative := 0 /= (This and 1);
    if 0 = (This and 16#80#) then
    if Negative then
    return -Result - 1;
    else
    return Result;
    end if;
    end if;
    loop
    Stream.Read (Buffer, Last);
    if Last /= 1 then
    raise End_Error;
    end if;
    Result := Result +
    Two**Power * To_Big_Integer (This and 16#7F#);
    if 0 = (This and 16#80#) then
    if Negative then
    return -Result - 1;
    else
    return Result;
    end if;
    end if;
    Power := Power + 7;
    end loop;
    end Get;

    procedure Put
    ( Stream : in out Root_Stream_Type'Class;
    Value : Big_Integer
    ) is
    Item : Big_Integer := Value;
    Buffer : Stream_Element_Array (1..1);
    This : Stream_Element renames Buffer (1);
    begin
    if Item >= 0 then
    Item := Value;
    This := From_Big_Integer (Item mod (16#40#)) * 2;
    else
    Item := -(Value + 1);
    This := From_Big_Integer (Item mod (16#40#)) * 2 + 1;
    end if;
    Item := Item / 16#40#;
    if Item = 0 then
    Stream.Write (Buffer);
    return;
    end if;
    This := This or 16#80#;
    Stream.Write (Buffer);
    loop
    This := From_Big_Integer (Item mod 16#80#) or 16#80#;
    Item := Item / 16#80#;
    if Item = 0 then
    This := This and 16#7F#;
    Stream.Write (Buffer);
    return;
    end if;
    Stream.Write (Buffer);
    end loop;
    end Put;

    F : File_Type;
    begin
    Create (F, Out_File, "Test.dat");
    for I in -1_000_000..1_000_000 loop
    Put (Stream (F).all, To_Big_Integer (I));
    end loop;
    Close (F);
    Open (F, In_File, "Test.dat");
    for I in -1_000_000..1_000_000 loop
    declare
    Value : constant Big_Integer := Get (Stream (F).all);
    begin
    if Value /= To_Big_Integer (I) then
    raise Data_Error;
    end if;
    end;
    end loop;
    Close (F);
    end Test;
    -------------------------
    The above could be optimized to work with buffers rather than
    reading/writing stream octets one by one. It is a long story, but
    normally you would implement some data blocks with the length count on
    top of the stream in order to avoid inefficient octet by octet reading
    and add an error correction layer.

    --
    Regards,
    Dmitry A. Kazakov
    http://www.dmitry-kazakov.de

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Simon Wright@21:1/5 to fjj@itema.no on Sat Jul 1 13:12:13 2023
    Frank Jørgen Jørgensen <fjj@itema.no> writes:

    I'm running the below program with GNAT Community 2020 (20200818-93)
    on Windows 11 Home.
    I have some problems trying to save big numbers to a file - so I
    noticed this behaviour:
    If I open Test.dat in Visual Studio Hex editor, it seems like this
    program saves this big number with a different bit pattern each time.
    Is that as expected?
    I do have some problems reading back the big numbers in my real code.

    Looking at the source for Big_Integer[1] I see no trace of streaming
    support; what you got streamed was the address of some allocated data
    used under the hood to contain the big integer.

    I don't know whether the ARM requires/expects Big_Integer to be
    streamable, but I think you had every reason to expect it!

    [1] https://github.com/gcc-mirror/gcc/blob/7e904d6c7f252ee947c237ed32dd43b2c248384d/gcc/ada/libgnat/a-nbnbin.ads#L171

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Randy Brukardt@21:1/5 to Simon Wright on Fri Jul 7 21:58:35 2023
    "Simon Wright" <simon@pushface.org> wrote in message news:lyr0prq19e.fsf@pushface.org...
    ...
    I don't know whether the ARM requires/expects Big_Integer to be
    streamable, but I think you had every reason to expect it!

    See 13.13.1(54/1):

    For every subtype S of a language-defined nonlimited specific type T, the output generated by S'Output or S'Write shall be readable by S'Input or
    S'Read, respectively. This rule applies across partitions if the
    implementation conforms to the Distributed Systems Annex.

    Ada.Numerics.Big_Numbers.Big_Integers.Big_integer surely is a
    language-defined nonlimited specific type, so the above applies. Streaming should work, if it doesn't, that's a bug.

    Randy.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)