Tikfinity/Runtime/TikfinityDeduplicator.cs
using System.Collections.Generic;

namespace TikfinitySbox;

sealed class TikfinityDeduplicator
{
    readonly Dictionary<string, double> _expiresAt = new();

    public bool TryRemember( string fingerprint, double now, double lifetime, int maximumEntries )
    {
        if ( string.IsNullOrWhiteSpace( fingerprint ) ) return true;
        Prune( now, maximumEntries );

        if ( _expiresAt.TryGetValue( fingerprint, out var expiry ) && expiry > now )
            return false;

        _expiresAt[fingerprint] = now + (lifetime < 0.1 ? 0.1 : lifetime);
        return true;
    }

    void Prune( double now, int maximumEntries )
    {
        if ( _expiresAt.Count == 0 ) return;

        var remove = new List<string>();
        foreach ( var pair in _expiresAt )
        {
            if ( pair.Value <= now ) remove.Add( pair.Key );
        }

        foreach ( var key in remove ) _expiresAt.Remove( key );

        var maximum = maximumEntries < 64 ? 64 : maximumEntries;
        if ( _expiresAt.Count < maximum ) return;

        remove.Clear();
        var target = _expiresAt.Count - maximum + 1;
        foreach ( var pair in _expiresAt )
        {
            remove.Add( pair.Key );
            if ( remove.Count >= target ) break;
        }

        foreach ( var key in remove ) _expiresAt.Remove( key );
    }
}