(* Sets.mod provides a dynamic set module. Copyright (C) 2009-2023 Free Software Foundation, Inc. Contributed by Gaius Mulley . 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 . *) 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 iFinalSymbol() 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