blob: 2afad8bbe4119a7bad80fa0e065dd22aa54fd89e [file] [log] [blame]
(* Sets.mod provides a dynamic set module.
Copyright (C) 2009-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE Sets ;
FROM SYSTEM IMPORT ADDRESS, BYTE ;
FROM SymbolTable IMPORT FinalSymbol ;
FROM M2Error IMPORT InternalError ;
FROM Storage IMPORT ALLOCATE, REALLOCATE, DEALLOCATE ;
FROM libc IMPORT memset, memcpy ;
FROM M2Printf IMPORT printf0, printf1, printf2 ;
FROM Assertion IMPORT Assert ;
CONST
BitsetSize = SIZE (BITSET) ;
MaxBitset = MAX (BITSET) ;
BitsPerByte = (MaxBitset + 1) DIV BitsetSize ;
Debugging = FALSE ;
TYPE
PtrToByte = POINTER TO BYTE ;
PtrToBitset = POINTER TO BITSET ;
Set = POINTER TO RECORD
init,
start,
end : CARDINAL ;
pb : PtrToBitset ;
bytes : CARDINAL ;
elements: CARDINAL ;
END ;
(*
growSet -
*)
PROCEDURE growSet (i: CARDINAL; bytes: CARDINAL) ;
BEGIN
printf2("i = %d, bytes = %d\n", i, bytes)
END growSet ;
(*
checkRange - checks to make sure, i, is within range and
it will extend the set bitmap if required.
*)
PROCEDURE checkRange (s: Set; i: CARDINAL) ;
VAR
bits,
o, j: CARDINAL ;
b : PtrToBitset ;
v : PtrToByte ;
BEGIN
WITH s^ DO
IF i<init
THEN
InternalError ('set element is too low and out of bounds')
ELSIF i>FinalSymbol()
THEN
InternalError ('set element is too high and out of bounds')
ELSE
j := bytes * BitsPerByte ;
IF i>=j
THEN
o := bytes ;
IF Debugging
THEN
printf2("previous bitset size %d bytes, need %d bits\n",
o, i)
END ;
IF bytes=0
THEN
bytes := BitsetSize
END ;
WHILE i >= bytes*BitsPerByte DO
IF Debugging
THEN
growSet(i, bytes)
END ;
bytes := bytes * 2
END ;
ALLOCATE(b, bytes) ;
IF Debugging
THEN
bits := bytes*8 ;
printf2("new allocated bitset size %d bytes, holds %d bits\n", bytes, bits) ;
IF i>bits
THEN
InternalError ('buffer is too small')
END
END ;
(* a := memset(b, 0, bytes) ; *)
v := PtrToByte(b) ;
INC(v, o) ;
Assert (memset (v, 0, bytes-o) = v) ;
Assert (memcpy (b, pb, o) = b) ;
IF Debugging
THEN
printf1("deallocating old bitset size %d bytes\n", o)
END ;
IF o>0
THEN
DEALLOCATE(pb, o)
END ;
pb := b
END
END
END
END checkRange ;
(*
findPos - returns a pointer to the BITSET which will contain, i.
*)
PROCEDURE findPos (pb: PtrToBitset; i: CARDINAL) : PtrToBitset ;
VAR
v: PtrToByte ;
BEGIN
IF (((i DIV (MaxBitset+1)) * (MaxBitset+1)) DIV BitsPerByte) MOD BitsetSize#0
THEN
InternalError ('must be a multiple of bitset size')
END ;
v := PtrToByte(pb) ;
INC(v, ((i DIV (MaxBitset+1)) * (MaxBitset+1)) DIV BitsPerByte) ;
pb := PtrToBitset(v) ;
RETURN( pb )
END findPos ;
(*
InitSet - initializes and returns a set. The set will
never contain an element less than, low.
*)
PROCEDURE InitSet (low: CARDINAL) : Set ;
VAR
s: Set ;
BEGIN
NEW(s) ;
WITH s^ DO
init := low ;
start := 0 ;
end := 0 ;
pb := NIL ;
bytes := 0 ;
elements := 0
END ;
RETURN( s )
END InitSet ;
(*
KillSet - deallocates Set, s.
*)
PROCEDURE KillSet (s: Set) : Set ;
BEGIN
WITH s^ DO
IF bytes>0
THEN
DEALLOCATE(pb, bytes)
END
END ;
DISPOSE(s) ;
RETURN( NIL )
END KillSet ;
(*
DuplicateSet - returns a new duplicated set.
*)
PROCEDURE DuplicateSet (s: Set) : Set ;
VAR
t: Set ;
BEGIN
NEW(t) ;
t^ := s^ ;
WITH t^ DO
ALLOCATE(pb, bytes) ;
Assert (memcpy (pb, s^.pb, bytes) = pb)
END ;
RETURN( t )
END DuplicateSet ;
(*
ForeachElementInSetDo - for each element e in, s, call, p(e).
*)
PROCEDURE ForeachElementInSetDo (s: Set; p: PerformOperation) ;
VAR
i, j, c: CARDINAL ;
b : PtrToBitset ;
v : PtrToByte ;
BEGIN
WITH s^ DO
i := start ;
c := elements ;
b := findPos(pb, i) ;
j := i MOD (MaxBitset+1) ;
WHILE (i<=end) AND (c>0) DO
IF j IN b^
THEN
DEC(c) ;
p(i)
END ;
IF j=MaxBitset
THEN
v := PtrToByte(b) ;
INC(v, BitsetSize) ; (* avoid implications of C address arithmetic in mc PtrToByte *)
b := PtrToBitset(v) ;
j := 0
ELSE
INC(j)
END ;
INC(i)
END
END
END ForeachElementInSetDo ;
(*
IsElementInSet - returns TRUE if element, i, is in set, s.
*)
PROCEDURE IsElementInSet (s: Set; i: CARDINAL) : BOOLEAN ;
VAR
b: PtrToBitset ;
BEGIN
checkRange(s, i) ;
WITH s^ DO
b := findPos(pb, i) ;
RETURN( (i MOD (MaxBitset+1)) IN b^ )
END
END IsElementInSet ;
(*
NoOfElementsInSet - returns the number of elements in a set, s.
*)
PROCEDURE NoOfElementsInSet (s: Set) : CARDINAL ;
BEGIN
RETURN( s^.elements )
END NoOfElementsInSet ;
(*
ExcludeElementFromSet - excludes element, i, from set, s.
*)
PROCEDURE ExcludeElementFromSet (s: Set; i: CARDINAL) ;
VAR
b: PtrToBitset ;
BEGIN
checkRange(s, i) ;
WITH s^ DO
b := findPos(pb, i) ;
IF (i MOD (MaxBitset+1)) IN b^
THEN
DEC(elements) ;
EXCL(b^, i MOD (MaxBitset+1))
END
END
END ExcludeElementFromSet ;
(*
IncludeElementIntoSet - includes element, i, into set, s.
*)
PROCEDURE IncludeElementIntoSet (s: Set; i: CARDINAL) ;
VAR
b: PtrToBitset ;
BEGIN
checkRange(s, i) ;
WITH s^ DO
b := findPos(pb, i) ;
IF NOT ((i MOD (MaxBitset+1)) IN b^)
THEN
INC(elements) ;
INCL(b^, i MOD (MaxBitset+1)) ;
IF (start=0) OR (start>i)
THEN
start := i
END ;
IF (end=0) OR (end<i)
THEN
end := i
END
END
END
END IncludeElementIntoSet ;
(*
EqualSet - return TRUE if left = right.
*)
PROCEDURE EqualSet (left, right: Set) : BOOLEAN ;
VAR
v : PtrToByte ;
lptr,
rptr: PtrToBitset ;
last,
el : CARDINAL ;
BEGIN
IF (left^.init = right^.init) AND
(left^.start = right^.start) AND
(left^.end = right^.end) AND
(left^.elements = right^.elements)
THEN
(* Now check contents. *)
el := left^.start ;
last := left^.end ;
WHILE el <= last DO
lptr := findPos (left^.pb, el) ;
rptr := findPos (right^.pb, el) ;
IF el + BitsetSize < last
THEN
(* We can check complete bitset, *)
IF lptr^ # rptr^
THEN
RETURN FALSE
END ;
INC (el, BitsetSize) ;
v := PtrToByte (lptr) ;
INC (v, BitsetSize) ; (* Avoid implications of C address arithmetic in mc PtrToByte *)
lptr := PtrToBitset (v) ;
v := PtrToByte (rptr) ;
INC (v, BitsetSize) ; (* Avoid implications of C address arithmetic in mc PtrToByte *)
rptr := PtrToBitset (v)
ELSE
(* We must check remaining bits only. *)
WHILE (el <= last) AND (el >= left^.init) DO
IF IsElementInSet (left, el) # IsElementInSet (right, el)
THEN
RETURN FALSE
END ;
INC (el)
END ;
RETURN TRUE
END
END ;
RETURN TRUE
END ;
RETURN FALSE
END EqualSet ;
END Sets.