;;; SPDX-License-Identifier: LGPL-3.0-or-later
;;; SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>

#lang racket/base
;; Hash-table utilities that are not (yet) part of Racket

(provide hash-set/remove
         hashset-member?
         hashset-add
         hashset-remove
         in-hashset-values)

(require (only-in racket/set set seteq seteqv))

(define (hash-set/remove ht key val [default-val #f] #:compare [compare equal?])
  (if (compare val default-val)
      (hash-remove ht key)
      (hash-set ht key val)))

(define (hashset-member? ht key val)
  (define s (hash-ref ht key #f))
  (and s (hash-has-key? s val)))

(define (make-hash-from-set-ctor ctor)
  (cond [(eq? ctor set) (hash)]
        [(eq? ctor seteq) (hasheq)]
        [(eq? ctor seteqv) (hasheqv)]
        [else (error 'hash-ctor-for-set-ctor "Cannot use hashset with set constructor ~v" ctor)]))

(define (hashset-add ht key val #:set [set-ctor set])
  (define old-set (hash-ref ht key (lambda () (make-hash-from-set-ctor set-ctor))))
  (hash-set ht key (hash-set old-set val #t)))

(define (hashset-remove ht k v)
  (define old (hash-ref ht k #f))
  (if old
      (let ((new (hash-remove old v)))
        (if (hash-empty? new)
            (hash-remove ht k)
            (hash-set ht k new)))
      ht))

(define-syntax-rule (in-hashset-values ht key)
  (in-hash-keys (hash-ref ht key hash)))
