BIT ARRAYS AND THE ERATOSTHENES SIEVE

using System, System.Reflection, System.Text;

namespace Freya.Demos;

public

    <DefaultMember('Items')>
    BitsArray = class
    public
        constructor(Min, Max: Integer);
        constructor(Max: Integer) : Self(0, Max);

        property Min, Max: Integer; readonly;
        property Items[Index: Integer]: Boolean;
        property Count: Integer; readonly;

        iterator GetItems: Integer;
        method Invert;
        method ToString: String; override;
    end;

    Sieve = static class
    public
        method GetPrimes(Max: Integer): BitsArray;
    end;

implementation for Sieve is

    method GetPrimes(Max: Integer): BitsArray;
    begin
        Result := new BitsArray(2, Max);
        for i in 2 .. Max div 2 : not Result[i]
        begin
            var j := i + i;
            while j <= Max
            begin
                Result[j] := true;
                j += i;
            end;
        end;
        Result.Invert;
    end;

implementation for BitsArray is

    Bits: Array[Integer];
    High: Integer;

    constructor(Min, Max: Integer);
    begin
        Self.Min := Min;
        Self.Max := Max;
        Self.High := Max - Min;
        var Len := Math.Max((Max - Min + 32) div 32, 0);
        Bits := new Array[Integer](Len);
    end;

    property Count: Integer;
    begin
        for j in Bits
        using k: Integer := 1
            repeat
                if j & k <> 0 then
                      Result++;
                // I do prefer C style shift operators,
                // but you can use shr and shl if you like it.
                k := k << 1;
            until k = 0;
    end;

    iterator GetItems: Integer;
    begin
        for i in Min .. Max : Self[i]
            yield i;
    end;

    property Items(Index: Integer): Boolean;
    begin
        Index -= Min;
        if Index < 0 or Index > High
            raise new ArgumentException('Index');
        Result := (Bits[Index div 32] & (1 << (Index mod 32))) <> 0;
    end;

    property Items(Index: Integer; Value: Boolean);
    begin
        Index -= Min;
        if Index < 0 or Index > High
            raise new ArgumentException('Index');
        if Value then
            Bits[Index div 32] |= 1 << (Index mod 32)
        else
            Bits[Index div 32] &= ~(1 << (Index mod 32));
    end;

    method Invert;
    begin
        for i in 0..Bits.Length - 1 do
            Bits[i] := ~Bits[i];
    end;

    method ToString: String;
    begin
        var sb := new StringBuilder;
        sb.Append('{');
        for i in Min..Max : Self[i]
            if sb.Length = 1 then
                sb.Append(i)
            else
                sb.Append(',').Append(i);
        Result := sb.Append('}').ToString;
    end;
implementation

    method Main;
    begin
        const TIMES = 1_000_000;
        var bits := Sieve.GetPrimes(TIMES);
        Console.WriteLine(
            String.Format('{0:#,0} prime numbers before {1:#,0}',
            bits.Count, TIMES));
        // Try this:
        // Console.WriteLine(Sieve.GetPrimes(1000));
    end;

end.