| (* 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. |