Recent

Author Topic: Count elements in set  (Read 8262 times)

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #15 on: August 08, 2025, 11:58:20 am »
@avk
So my initial approach was correct?
But here the size is the size in bytes, which is 1,2,4,8,16,32 for the current set sizes
I reduced it to:
Code: Pascal  [Select][+][-]
  1. function setcount<T{:set}>(aSet:T): integer; inline;
  2. type
  3.   poword = ^oword;oword  = record a,b:qword;end;
  4.   phword = ^hword;hword  = record a,b,c,d:qword;end;
  5. begin
  6.    if GetTypeKind(T) = tkSet then
  7.    // *must* be dereferenced
  8.    case SizeOf(aSet) of
  9.    1     : Result := popcnt(PByte(@aSet)^);                              // 1..8 bits
  10.    2     : Result := popcnt(PWord(@aSet)^);                              // 9..16 bits  
  11.    4     : Result := popcnt(PCardinal(@aSet)^);                          //17..32 bits
  12.    8     : Result := popcnt(PQword(@aSet)^);                             //33..64 bits
  13.    16    : Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);  //65..128 bits
  14.    32    : Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) + //129..256 bits;
  15.                      popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  16.    else
  17.      raise ESetException.Create('Set too large');
  18.    end
  19.  else raise ESetException.Create('Not a set');
  20. end;
OK?
Btw, I heard credible rumours that in the next Delphi there will be larger sets than max 256.
Anyway, the above does not work in Delphi.
« Last Edit: August 08, 2025, 12:24:01 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Kays

  • Hero Member
  • *****
  • Posts: 632
  • Whasup!?
    • KaiBurghardt.de
Re: Count elements in set
« Reply #16 on: August 08, 2025, 12:24:34 pm »
Sets are implemented as one or several integers, […]
Exactly, they are implemented not defined as a series of integers.

Code: Pascal  [Select][+][-]
  1. []
LemonParty wants an intrinsic. LemonParty does not want to program an own function (and I bet he/she can come up with several ways on his/her own anyway).
Yours Sincerely
Kai Burghardt

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #17 on: August 08, 2025, 01:04:33 pm »
@Kays
Using implicit specialization comes a long way.
It treats it much like an intrinsic.
That is the point of my code.
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

avk

  • Hero Member
  • *****
  • Posts: 826
Re: Count elements in set
« Reply #18 on: August 08, 2025, 01:28:24 pm »
@avk
So my initial approach was correct?
...

To be honest, your question is not entirely clear to me.
Trying to use your PopCount
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode delphi}
  3. {$modeswitch implicitfunctionspecialization}
  4. {$packset 1}
  5. uses
  6.   SysUtils;
  7.  
  8. type
  9.   ESetException = class(Exception);
  10.  
  11. function PopCount<T{:set}>(aSet:T): integer; inline;
  12. type
  13.   poword = ^oword;oword  = record a,b:qword;end;
  14.   phword = ^HWord;HWord  = record a,b,c,d:qword;end;
  15. begin
  16.    if GetTypeKind(T) = tkSet then
  17.    // *must* be dereferenced
  18.    case SizeOf(aSet) of
  19.    1     : Result := popcnt(PByte(@aSet)^);                              // 1..8 bits
  20.    2     : Result := popcnt(PWord(@aSet)^);                              // 9..16 bits
  21.    4     : Result := popcnt(PCardinal(@aSet)^);                          //17..32 bits
  22.    8     : Result := popcnt(PQword(@aSet)^);                             //33..64 bits
  23.    16    : Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);  //65..128 bits
  24.    32    : Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) + //129..256 bits;
  25.                      popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  26.    else
  27.      raise ESetException.Create('Set too large');
  28.    end
  29.  else raise ESetException.Create('Not a set');
  30. end;
  31.  
  32. var
  33.   s: set of 0..100;
  34.  
  35. begin
  36.   s := [42];
  37.   try
  38.     WriteLn(PopCount(s));
  39.   except
  40.     WriteLn('Something went wrong');
  41.   end;
  42. end.  
  43.  

prints
Code: [Select]
Something went wrong

If this is the expected result, then everything is fine.

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #19 on: August 08, 2025, 02:05:29 pm »
If this is the expected result, then everything is fine.
This prints 1:
Code: Pascal  [Select][+][-]
  1. program testavkbug;
  2. {$mode delphi}
  3. {$modeswitch implicitfunctionspecialization}
  4. {$packset 1}
  5. uses
  6.   SysUtils;
  7. resourcestring
  8.   rsSetTooLarge = 'Set too large';
  9.   rsNotaSet = 'Not a set';
  10.  
  11. type
  12.   ESetException = class(Exception);
  13.  
  14. function setcount<T{:set}>(aSet:T): integer; inline;
  15. type
  16.   poword = ^oword;oword  = record a,b:qword;end;
  17.   phword = ^HWord;HWord  = record a,b,c,d:qword;end;
  18. begin
  19.    if GetTypeKind(T) = tkSet then
  20.    case SizeOf(aSet) of
  21.    1     : Result := popcnt(PByte(@aSet)^);    
  22.    2..3  : Result := popcnt(PWord(@aSet)^);    
  23.    4..7  : Result := popcnt(PCardinal(@aSet)^);
  24.    8..15 : Result := popcnt(PQword(@aSet)^);    
  25.    16..31: Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);
  26.    32..63: Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) +
  27.                         popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  28.    else
  29.      raise ESetException.Create(rsSetTooLarge);
  30.    end
  31.  else raise ESetException.Create(rsNotaSet);
  32. end;
  33.  
  34. var
  35.   s: set of 0..100;
  36. begin
  37.   s := [42];
  38.   try
  39.     WriteLn(SetCount(s));
  40.   except
  41.     WriteLn('Something went wrong');
  42.   end;
  43. end.

You probably missed my last edit, but I need to investigate {$packset}(tested)
 
« Last Edit: August 08, 2025, 02:23:19 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

avk

  • Hero Member
  • *****
  • Posts: 826
Re: Count elements in set
« Reply #20 on: August 08, 2025, 02:21:24 pm »
...
You probably missed my last edit...

Yes, exactly. :-[
Nevertheless, this
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode delphi}
  3. {$modeswitch implicitfunctionspecialization}
  4. {$packset 1}
  5. uses
  6.   SysUtils;
  7.  
  8. resourcestring
  9.   rsSetTooLarge = 'Set too large';
  10.   rsNotaSet = 'Not a set';
  11.  
  12. type
  13.   ESetException = class(Exception);
  14.  
  15. function SetCount<T{:set}>(aSet:T): integer; inline;
  16. type
  17.   poword = ^oword;oword  = record a,b:qword;end;
  18.   phword = ^HWord;HWord  = record a,b,c,d:qword;end;
  19. begin
  20.    if GetTypeKind(T) = tkSet then
  21.    case SizeOf(aSet) of
  22.    1     : Result := popcnt(PByte(@aSet)^);
  23.    2..3  : Result := popcnt(PWord(@aSet)^);
  24.    4..7  : Result := popcnt(PCardinal(@aSet)^);
  25.    8..15 : Result := popcnt(PQword(@aSet)^);
  26.    16..31: Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);
  27.    32..63: Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) +
  28.                         popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  29.    else
  30.      raise ESetException.Create(rsSetTooLarge);
  31.    end
  32.  else raise ESetException.Create(rsNotaSet);
  33. end;
  34.  
  35. var
  36.   s: set of 0..100;
  37.  
  38. begin
  39.   s := [77];
  40.   try
  41.     WriteLn(SetCount(s));
  42.   except
  43.     WriteLn('Something went wrong');
  44.   end;
  45.   ReadLn;
  46. end.
  47.  
prints
Code: [Select]
0

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #21 on: August 08, 2025, 04:42:53 pm »
@avk

Found it:
Code: Pascal  [Select][+][-]
  1. program testavkbug;
  2. {$mode delphi}
  3. {$modeswitch implicitfunctionspecialization}
  4. {$packset 1}
  5. uses
  6.   SysUtils;
  7. resourcestring
  8.   rsSetTooLarge = 'Set too large';
  9.   rsNotaSet = 'Not a set';
  10.  
  11. type
  12.   ESetException = class(Exception);
  13.  
  14. function setcount<T{:set}>(aSet:T): integer; inline;
  15. type
  16.   poword = ^oword;oword  = packed record a,b:qword;end;
  17.   phword = ^HWord;HWord  = record a,b,c,d:qword;end;
  18. begin
  19.    if GetTypeKind(T) = tkSet then
  20.    case SizeOf(T) of
  21.    1  : Result := popcnt(PByte(@aSet)^);     {8}
  22.    2  : Result := popcnt(PWord(@aSet)^);     {16}
  23.    3..4  : Result := popcnt(PCardinal(@aSet)^); {32}
  24.    5..8  : Result := popcnt(PQword(@aSet)^);    {64}
  25.    9..16 : Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);{128}
  26.    17..32:  Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) +{256}
  27.                         popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  28.    else
  29.      raise ESetException.Create(rsSetTooLarge);
  30.    end
  31.  else raise ESetException.Create(rsNotaSet);
  32. end;
  33. type
  34.   range = 0..101;
  35. var
  36.   s: set of range;
  37.   i: integer;
  38. begin
  39.   s := [];
  40.   try
  41.     for i in range do
  42.     begin
  43.       include(s,i);
  44.       WriteLn(SizeOf(s):4,SetCount(s):4);
  45.      end;
  46.   except
  47.     WriteLn('Something went wrong');
  48.   end;
  49. end.
The growth with {$packset 1} is one byte at a time, not a power of two expansion.
« Last Edit: August 08, 2025, 04:53:57 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #22 on: August 08, 2025, 05:20:53 pm »
Beaten at my own game. After code verification at DeepSeek we decided on:
Code: Text  [Select][+][-]
  1. { POPCOUNT FOR SETS
  2.  
  3.   Copyright (c) 2025 Thaddy de Koning, DeepSeek
  4.  
  5.   Permission is hereby granted, free of charge, to any person obtaining a copy
  6.   of this software and associated documentation files (the "Software"), to
  7.   deal in the Software without restriction, including without limitation the
  8.   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9.   sell copies of the Software, and to permit persons to whom the Software is
  10.   furnished to do so, subject to the following conditions:
  11.  
  12.   The above copyright notice and this permission notice shall be included in
  13.   all copies or substantial portions of the Software.
  14.  
  15.   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16.   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17.   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18.   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19.   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20.   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21.   IN THE SOFTWARE.
  22. }
  23. {$mode delphi}{$Q+}{$R+}
  24. {$modeswitch implicitfunctionspecialization}
  25. {$packset 1}
  26. uses
  27.   SysUtils;
  28.  
  29. type
  30. // Helper function for bit counting in a byte (DeepSeek)
  31. function BytePopCount(b: Byte): Integer; inline;
  32. const
  33.   NibbleBits: array[0..15] of Byte = (
  34.     0, 1, 1, 2, 1, 2, 2, 3,
  35.     1, 2, 2, 3, 2, 3, 3, 4
  36.   );
  37. begin
  38.   Result := NibbleBits[b and $0F] + NibbleBits[b shr 4];
  39. end;
  40.  
  41. // Safe set element counter
  42. function SetCount<T>(aSet: T): Integer; inline;
  43. var
  44.   p: PByte;
  45.   i: Integer;
  46. begin
  47.   if GetTypeKind(T) <> tkSet then
  48.     raise ESetException.Create(rsNotaSet);
  49.  
  50.   Result := 0;
  51.   p := @aSet;
  52.   for i := 0 to SizeOf(T) - 1 do
  53.   begin
  54.     Result := Result + BytePopCount(p^);
  55.     Inc(p);
  56.   end;
  57. end;
  58.  
  59. type
  60.   range = 0..255;
  61. var
  62.   s: set of range;
  63.   i: Integer;
  64. begin
  65.   s := [];
  66.   try
  67.     for i in range do
  68.     begin
  69.       Include(s, i);
  70.       WriteLn('Size in bytes: ', SizeOf(s):2, '  Elements: ', SetCount(s):2);
  71.     end;
  72.   except
  73.     on E: Exception do
  74.       WriteLn('Error: ', E.Message);
  75.   end;
  76.   readln;
  77. end.
« Last Edit: August 08, 2025, 05:49:55 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

avk

  • Hero Member
  • *****
  • Posts: 826
Re: Count elements in set
« Reply #23 on: August 08, 2025, 05:58:04 pm »
But it's slow.
And in the previous version, there may be problems with alignment and random garbage on the stack.

Thaddy

  • Hero Member
  • *****
  • Posts: 18786
  • To Europe: simply sell USA bonds: dollar collapses
Re: Count elements in set
« Reply #24 on: August 08, 2025, 06:23:14 pm »
@avk
Make it faster  :D
I don't think that in this case the stack can run havoc.(register size)
It is only slow on processors with no native popcnt, where the compiler reverts to software popcnt.
« Last Edit: August 08, 2025, 06:28:43 pm by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

gues1

  • Guest
Re: Count elements in set
« Reply #25 on: August 08, 2025, 07:12:17 pm »
This can be usefull (I don't know if works in FPC) ?

I had this piece of code archived (taken from somewhere), I used it only one time.

Code: Pascal  [Select][+][-]
  1. uses Rtti, TypInfo;
  2.  
  3. type
  4.   TASetOfData = (First, Second, Third);
  5.  
  6. var
  7.   a: set of TASetOfData;
  8.  
  9. procedure FindAllSets(const Test: TValue);
  10. var
  11.   eType: PTypeInfo;
  12.   eData: PTypeData;
  13.   sbuffer: set of Byte; // This is the most larger set available
  14.   i: Integer;
  15. begin
  16.   sbuffer := [];
  17.   Test.ExtractRawData(@sbuffer);
  18.   eType := Test.TypeInfo.TypeData.CompType^;
  19.   eData := eType.TypeData;
  20.   //write true o false for every set "member"
  21.   for i := eData.MinValue to eData.MaxValue do
  22.     Writeln(GetEnumName(eType, i) + ' = ' + (i in sbuffer).ToString(TUseBoolStrs.True));
  23. end;
  24.  
  25. begin
  26.   include(a, Second);
  27.   FindAllSets(TValue.From(a));
  28. end;

Quote
First = False
Second = True
Third = False
« Last Edit: August 08, 2025, 07:42:28 pm by gues1 »

avk

  • Hero Member
  • *****
  • Posts: 826
Re: Count elements in set
« Reply #26 on: August 08, 2025, 07:18:03 pm »
@avk
Make it faster  :D
I don't think that in this case the stack can run havoc.(register size)
It is only slow on processors with no native popcnt, where the compiler reverts to software popcnt.

Slow is the DeepSeek version.
As for the garbage on the stack:
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode delphi}
  3. {$modeswitch implicitfunctionspecialization}
  4. {$packset 1}
  5. uses
  6.   SysUtils;
  7.  
  8. resourcestring
  9.   rsSetTooLarge = 'Set too large';
  10.   rsNotaSet = 'Not a set';
  11.  
  12. type
  13.   ESetException = class(Exception);
  14.  
  15. function SetCount<T{:set}>(aSet:T): integer; inline;
  16. type
  17.   poword = ^oword;oword  = record a,b:qword;end;
  18.   phword = ^HWord;HWord  = record a,b,c,d:qword;end;
  19. begin
  20.    if GetTypeKind(T) = tkSet then
  21.    case SizeOf(aSet) of
  22.    1     : Result := popcnt(PByte(@aSet)^);     {8}
  23.    2     : Result := popcnt(PWord(@aSet)^);     {16}
  24.    3..4  : Result := popcnt(PCardinal(@aSet)^); {32}
  25.    5..8  : Result := popcnt(PQword(@aSet)^);    {64}
  26.    9..16 : Result := popcnt(poword(@aSet)^.a)+popcnt(poword(@aSet)^.b);{128}
  27.    17..32: Result := popcnt(phword(@aSet)^.a)+popcnt(phword(@aSet)^.b) +{256}
  28.                         popcnt(phword(@aSet)^.c)+popcnt(phword(@aSet)^.d);
  29.    else
  30.      raise ESetException.Create(rsSetTooLarge);
  31.    end
  32.  else raise ESetException.Create(rsNotaSet);
  33. end;
  34.  
  35. var
  36.   s1: set of Byte;
  37.   s2: set of 0..150;
  38. begin
  39.   s1 := [0..255];
  40.   s2 := [];
  41.   try
  42.     WriteLn('SetCount(s1) = ', SetCount(s1));
  43.     WriteLn('SetCount(s2) = ', SetCount(s2));
  44.   except
  45.     WriteLn('Something went wrong');
  46.   end;
  47. end.
  48.  
it prints
Code: [Select]
SetCount(s1) = 256
SetCount(s2) = 40

Bart

  • Hero Member
  • *****
  • Posts: 5706
    • Bart en Mariska's Webstek
Re: Count elements in set
« Reply #27 on: August 09, 2025, 12:11:18 am »
Poor man's implementation:
Code: Pascal  [Select][+][-]
  1. function Card(const ASet; SizeOfSet: Cardinal): Integer;
  2. const
  3.   MaxSetSize = 32;
  4. type
  5.   TSetBytes = array [0..MaxSetSize-1] of byte;
  6. var
  7.   Bytes: TSetBytes absolute ASet;
  8.   i: Integer;
  9. begin
  10.   Assert(((SizeOfSet > 0) and (SizeOfSet <= MaxSetSize)),'Card: invalid value for SizeOfSet');
  11.   Result := 0;
  12.   for i := 0 to SizeOfSet-1 do Result := Result + PopCnt(Bytes[i]);
  13. end;

For the example sets in #26 it prints:
256
0
(SizeOf(S2) is 19 with PACKSET 1)

It'll be the slowes implementation of all implementations so far I guess.
I can make it slower if needed  :)

Bart
« Last Edit: August 09, 2025, 12:16:38 am by Bart »

avk

  • Hero Member
  • *****
  • Posts: 826
Re: Count elements in set
« Reply #28 on: August 09, 2025, 11:12:51 am »
Maybe something like:
Code: Pascal  [Select][+][-]
  1. generic function Card<TSet>(const s: TSet): Integer;
  2. var
  3.   p: PByte;
  4.   Count: Integer;
  5. begin
  6.   if GetTypeKind(s) <> tkSet then exit(-1);
  7.   p := @s;
  8.   Count := SizeOf(s);
  9.   case SizeOf(s) div 8 of
  10.     1:
  11.       begin
  12.         Result := PopCnt(PQWord(p)^);
  13.         Dec(Count, 8);
  14.         Inc(p, 8);
  15.       end;
  16.     2:
  17.       begin
  18.         Result := PopCnt(PQWord(p)^) + PopCnt(PQWord(p+8)^);
  19.         Dec(Count, 16);
  20.         Inc(p, 16);
  21.       end;
  22.     3:
  23.       begin
  24.         Result := PopCnt(PQWord(p)^) + PopCnt(PQWord(p+8)^) + PopCnt(PQWord(p+16)^);
  25.         Dec(Count, 24);
  26.         Inc(p, 24);
  27.       end;
  28.     4: exit(PopCnt(PQWord(p)^) + PopCnt(PQWord(p+8)^) + PopCnt(PQWord(p+16)^) + PopCnt(PQWord(p+24)^));
  29.   else
  30.     Result := 0;
  31.   end;
  32.  
  33.   if Count >= 4 then begin
  34.     Inc(Result, PopCnt(PDWord(p)^));
  35.     Dec(Count, 4);
  36.     Inc(p, 4);
  37.   end;
  38.  
  39.   case Count of
  40.     1: Inc(Result, PopCnt(p^));
  41.     2: Inc(Result, PopCnt(PWord(p)^));
  42.     3:
  43.       begin
  44.         Inc(Result, PopCnt(PWord(p)^));
  45.         Inc(Result, PopCnt(p[2]));
  46.       end;
  47.   end;
  48. end;
  49.  

Don't know about the alignment issues though.
« Last Edit: August 09, 2025, 12:52:56 pm by avk »

LemonParty

  • Sr. Member
  • ****
  • Posts: 417
Re: Count elements in set
« Reply #29 on: August 09, 2025, 04:18:59 pm »
Thank you for answers. I was looking for build in method. Solutions with Popcnt seems most handy.

Probably developer team should add such intrinsic to FPC.
Lazarus v. 4.99. FPC v. 3.3.1. Windows 11

 

TinyPortal © 2005-2018