diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index a038822..a60db23 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,19 @@ This repository contains the implementation of The Innovation Game (TIG). ## Important Links +* [TIG Documentation](https://docs.tig.foundation/) * [TIG Whitepaper](docs/whitepaper.pdf) * [TIG Tech Explainer](docs/tech/1_basics.md) * [TIG Licensing Explainer](docs/guides/anatomy.md) * [Getting Started with Innovating](docs/guides/innovating.md) -* [Challenge Descriptions](docs/challenges/satisfiability.md) +* [Implementations vs Breakthroughs](docs/guides/breakthroughs.md) +* [Voting Guidelines for Token Holders](docs/guides/voting.md) ## Repo Contents * [tig-algorithms](./tig-algorithms/README.md) - A Rust crate that hosts algorithm submissions made by Innovators in TIG * [tig-benchmarker](./tig-benchmarker/README.md) - Python scripts for running TIG's benchmarker in master/slave configuration +* [tig-breakthroughs](./tig-breakthroughs/README.md) - A folder that hosts submissions of algorithmic methods made by Innovators in TIG. * [tig-challenges](./tig-challenges/README.md) - A Rust crate that contains the implementation of TIG's challenges (computational problems adapted for proof-of-work) * [tig-protocol](./tig-protocol/README.md) - A Rust crate that contains the implementation of TIG's core protocol logic. * [tig-structs](./tig-structs/README.md) - A Rust crate that contains the definitions of structs used throughout TIG diff --git a/docs/agreements/game_rules.pdf b/docs/agreements/game_rules.pdf index 5709173..ef990fa 100644 Binary files a/docs/agreements/game_rules.pdf and b/docs/agreements/game_rules.pdf differ diff --git a/docs/agreements/invention_assignment.doc b/docs/agreements/invention_assignment.doc new file mode 100644 index 0000000..b74d27f Binary files /dev/null and b/docs/agreements/invention_assignment.doc differ diff --git a/docs/guides/breakthroughs.md b/docs/guides/breakthroughs.md new file mode 100644 index 0000000..0c8d1f4 --- /dev/null +++ b/docs/guides/breakthroughs.md @@ -0,0 +1,341 @@ +# Rewarding Innovation in The Innovation Game + +## Introduction + +The rewards for innovation in The Innovation Game (the "**Game**") are +designed to be **sufficient** to incentivise innovators to submit their +innovation ("**Contributions**") to the Game on the understanding that +the Contributions will be made available to others as the basis for +**open innovation** under the *TIG Open Data License*, and for further +innovation by participants in the Game under the terms of the *TIG +Innovator Outbound Game License*. + +By **sufficiency** we mean that the reward must be at least enough to +cause commercially valuable innovation (by implication, what is +commercially valuable should, at least, be novel over the state of the +art, innovative and free from encumbrances) to be submitted to the Game. +What is sufficient reward may not necessarily be equitable in terms of +fully compensating an innovator for their contribution of value (that is +almost impossible to determine at the point of submission of the +Contribution), at least not immediately, it just needs to be enough to +cause the innovator to submit their Contribution. + +In an ideal world the rewards to innovators would be exactly +commensurate with the value added by an innovator's Contribution. +Because the Game seeks to reward Contributors for improved algorithm +performance, it is intuitively attractive to look for a method of +quantitatively measuring algorithmic performance but this would add +significant (and what we consider unnecessary) complexity and also fail +to recognise a number of realities including, the difficulties of +objectively measuring and verifying the quantum of immediate commercial +value of a particular Contribution, its spill over value and any latent +value realised only later in time after the rewards have been assessed. +Accordingly, we have decided, for now, to keep it as simple as possible. +We will seek only what is **sufficient** in terms of reward to an +innovator to compensate them for their effort and ingenuity to +incentivise them to submit their innovation to the Game. This assessment +of reward may not necessarily look objectively fair in relative terms +(when comparing one algorithm to another), particularly in hindsight, +but any apparent inequity is mitigated by the fact that the value of the +TIG token will represent **all** off the accrued value of all +Contributions over time and so each innovator will have the chance to +benefit (if they hold some or all of their tokens) from the value +created not just by their Contribution but also that from the +Contributions of everyone else throughout the life of the Game. + +One of the most obvious rewards offered by the Innovation Game for +Contributions is the TIG token. Whether the token rewards are sufficient +to incentivise the submission of Contributions will depend on the token +price prevailing at the time of submission, the number of challenges to +which rewards are allocated and, also to some extent, on the expected +future value of the token. Some submissions will also be motivated by +other forms of reward such as academic impact. + +Because the token price will vary from time to time, we will only know +if, as the basis of reward it is sufficient, by gathering empirical +evidence provided by the quality of submissions in practice, and by +feedback from potential innovators. For this reason, the Team will +always be on alert to adjusting the rewards for the Game to make them +sufficient. Whilst the protocol is still in development innovator +rewards will be under constant review. + +If an innovator decides to withhold their innovation from the Game +because they assess that the reward is not sufficient incentive, they +will be exposed to the jeopardy that, whilst they wait, someone else may +submit an equivalent or better Contribution and resultingly then be +deprived of the rewards that they may have earned themselves by +submitting their algorithm. + +A fundamental cornerstone of the Game is the synthetic market that is +created by incentivising benchmarkers to create demand for the most +performant algorithm implementations. The value of a Contribution is +measured in that market by benchmarkers; the more a Contribution is +adopted, the more reward is allocated to it. If a change of a single +line of code in an existing code implementation (whether it relates to a +change to the algorithmic method or to a code optimisation) makes +sufficient difference that benchmarkers, acting rationally to improve +their performance in the Game, adopt it instead of other available +alternatives, then the market is designed to reward that change. + +In the context of the Game, with the exception below related to the TIG +Game Rules\*, it is crooked thinking to simply assess the quantum of +lines of code present or changed for determining the appropriateness for +reward or what the quantum of that reward should be. If the synthetic +market is functioning properly the best algorithm implementation will +prevail and an enquiry into "why" it is prevailing is not relevant. It +is the job of the Team to ensure that the synthetic market is the +closest proxy that it can be for rewarding the algorithmic performance +and utility most valued by commercial enterprise. + +\**The TIG Game Rules do mandate that a Contribution must make a +"meaningful difference" over existing implementations in the Game. This +will stop straightforward plagiarism where no or insignificant +additional value is added by a Contributor and this aspect of the Game +Rules will be policed.* + +## Defining and Classifying Innovation + +Generally, improvements in algorithmic methods tend to yield exponential +or order-of-magnitude efficiency gains by changing the problem-solving +approach, while the way in which algorithmic methods are implemented in +code tend to provide incremental improvements. It is important +therefore, that TIG places emphasis on incentivising the submission of +innovative algorithmic methods. TIG intends to do that by introducing a +process for identifying innovative algorithmic methods that will then be +eligible for additional rewards. + +We propose to distinguish two different types of Contribution in the +context of the Game and rewarding them differently: (1) **Algorithm +Implementations** (code); and (2) **Breakthrough Algorithms** +(methods). + +Implementations of algorithms comprise two elements; **(i)** an +algorithmic method (the fundamental approach or strategy for solving a +problem, independent of specific code or language); and **(ii)** an +expression of the algorithmic method in code. + +In the Game the algorithmic method element may be a Breakthrough +Algorithm or an algorithmic method that is not a Breakthrough Algorithm. + +### Breakthrough Algorithms + +TIG believes that the determination of whether an algorithmic method is +a Breakthrough Algorithm should be done by a combination of; **(i)** a +token weighted vote assessing novelty and inventiveness\*; and **(ii)** +an assessment of the performance of the algorithmic method determined +solely by the extent of its adoption by benchmarkers in the "TIG +synthetic market" (see: *Accessing Breakthrough Rewards* below). + +\* *For a discussion and explanation of novelty and inventiveness in the +context of the Game see [Breakthrough Rewards Guide for +Token Holders](./voting.md).* + +### Algorithm Implementations + +Algorithm Implementations are expressions of algorithmic methods in code +and in the context of the Game they may be an expression of an +algorithmic method which is a Breakthrough Algorithm or an expression of +an algorithmic method that is not a Breakthrough Algorithm. + +## Token Rewards for Innovation + +To reflect the patentability and significant impact on performance that +innovative algorithmic methods can have compared with algorithm +implementations, TIG will offer additional rewards for Breakthrough +Algorithms. These additional rewards (so called "**Breakthrough +Rewards")**, are brought about through the design of the protocol +enabling the rewards for Breakthrough Algorithms to persist for multiple +Rounds where they continue to provide the basis of algorithm +implementations adopted by benchmarkers. + +An Algorithm Implementation which does not embody an algorithmic method +which is eligible for potential Breakthrough Rewards, only rewards the +innovator that submitted the Algorithm Implementation with Standard +Rewards (subject to satisfaction of adoption thresholds). Because of the +absence of a Breakthrough Algorithm, Breakthrough Rewards notionally +allocated for Breakthrough Algorithms will, instead, be allocated to the +TIG treasury and used to bootstrap Breakthrough Algorithm development +(these are referred to in the Game as "**Bootstrap Rewards**"). + +For Algorithm Implementations that embody an algorithmic method which is +eligible for potential Breakthrough Rewards, two types of reward are +potentially available (subject to satisfaction of adoption thresholds): + +- **Standard Rewards** will be available for the innovator that + submitted the adopted implementation of the Breakthrough Algorithm. + +- **Breakthrough Rewards** will be available for the innovator that + submitted the Breakthrough Algorithm embodied in the implementation. + +The innovator(s) earning Standard Rewards and Breakthrough Rewards in +respect of an Algorithm Implementation may be the same or different +entities. An innovator can earn both Standard Rewards and Breakthrough +Rewards simultaneously or at different times. + +### Standard Rewards for Algorithm Implementations + +> **15%** of the total rewards available in a Round will be allocated to +> Standard Rewards for Algorithm Implementations. + +### Breakthrough Rewards for Breakthrough Algorithms + +> **15%** of the total rewards available in a Round will be allocated to +> Breakthrough Rewards for Breakthrough Algorithms. + +The most significant difference between Standard Rewards and +Breakthrough Rewards is that the Breakthrough Rewards will continue to +be earned where the Breakthrough Algorithm is inherited by an +implementation, meaning that Breakthrough Rewards can persist for longer +because they will be earned in connection with any, and all adopted +implementations of that Breakthrough Algorithm. Standard Rewards will be +earned only for as long as the respective implementation is adopted by +benchmarkers. Notwithstanding that the allocations of rewards are prima +facie equal for both Breakthrough Rewards and Standard Rewards (at 15% +each), because of the factor of persistence of adoption in calculating +aggregate rewards over time, Breakthrough Rewards in respect of +significant algorithmic breakthroughs are likely to far exceed Standard +Rewards. + +Breakthrough Rewards are expected to be greater than Standard Rewards to +reflect four attributes of Breakthrough Algorithms; + +(i) they are potentially patentable (bringing greater commercial value + to the TIG ecosystem); + +(ii) they are novel (there is no novelty test in the Game for Code + Optimisations which means they may have lower commercial value than + Breakthrough Algorithms); + +(iii) they generally have greater potential to make significant + performance improvements over the state of the art (bringing + greater value to the TIG ecosystem); and + +(iv) they are more certain to have value extrinsic to the Game. + +It is the Team's belief that sufficiency of reward can be achieved in +respect of Standard Rewards by appropriately setting the period during +which a Code Optimisation will be guaranteed not to be used as the basis +for a subsequent Code Optimisation contributed by a third party. At +present this period is two Rounds. Only the behaviour of innovators can +ultimately signal to us whether, at a minimum, this "protected" period +for a Code Optimisation is sufficient to incentivise its submission to +the game and we will be monitoring this as the protocol develops. + +## Accessing Breakthrough Rewards + +If an Innovator believes that their Contribution is or embodies a +potential Breakthrough Algorithm, then they may request that their +Contribution is considered for approval as a Breakthrough Algorithm. For +a Contribution to be a Breakthrough Algorithm, the following must +**ALL** be satisfied with respect to the Contribution: + +(i) It must be declared, by Token Holder Vote that the Algorithm + implementation subject to review embodies an algorithmic method that + is eligible for potential Breakthrough Rewards; + +(ii) The intellectual property rights embodied in the Contribution must + be irrevocably assigned to TIG in accordance with the TIG IP + Policy; + +(iii) The Contributor must burn two hundred and fifty (250) TIG tokens + for each requested Token Holder Vote; and + +(iv) In a rolling 1 week window the sum of all algorithm implementations + which embody the algorithmic method subject to review must achieve + a sufficient degree of adoption by Benchmarkers (where for Standard + rewards the adoption threshold by benchmarkers is presently 25% + before the algorithm implementation is merged to obtain Standard + Rewards, the corresponding adoption threshold by benchmarkers for + potential Breakthrough Algorithms will be 50%). The reason why the + threshold of adoption is higher for potential Breakthrough + Algorithms is that TIG wishes to reserve Breakthrough Rewards for + algorithmic improvements that offer a significant universal + improvement in performance (which will be valued more highly by + commercial licensees). + +Essentially the **Token Holder Vote** is assessing the novelty and +inventiveness of a contributed algorithmic method. To maximise the +likelihood that a Breakthrough Algorithm will be ratified by Token +Holder Vote, an innovator seeking Breakthrough Rewards is advised to +disclose the following information so that it may be assessed by token +holders: + +- **Novelty:** Provide results of a prior art search to identify any + existing disclosures that might affect the novelty of your algorithmic + method. + +- **Inventiveness:** Clearly document how the algorithmic method differs + from existing solutions (i.e. is non-obvious) and any new technical + effect it achieves. + +- **Technical Effect:** Document of how the algorithmic method offers + the potential to provide the basis for significant technical + advancements. Document how the algorithmic method might have **real + world application** e.g. in fields such as computer security, medical + imaging, autonomous systems. or data compression. + +## Encumbrances + +The value of Contributions to TIG will be compromised if the +Contributions are encumbered by third party intellectual property rights +and terms of the TIG Inbound Game License seeks to deal with this. + +The terms of the TIG Inbound Game License state: + +"Submitter represents that Submitter is legally entitled to grant the +above licenses and that, to the best of Submitter's knowledge, the Work +does not infringe any rights of a third party." + +## Attribution of Copyright Owner + +The header file for each algorithm implementation submitted to the Game +must comply with the terms of the TIG Inbound Game License. This +establishes the identity of the copyright owner (which may or may not be +the original author of the code) of the implementation of the +algorithmic method in source code. + +## Unique Algorithmic Method Identifier (UAI) + +With the introduction of Breakthrough Rewards, the significance of the +correct identification of the original creator of the algorithmic method +embodied in an algorithm implementation has even greater significance. + +It is important that the identification is not only correct, but that it +appears in all algorithm implementations which embody the relevant +algorithmic method. + +The copyright notice does not necessarily identify the original creator +of the algorithmic method embodied in the implementation (because the +creator of the algorithmic method and the author of the code +implementing the algorithmic method may be different). For this reason, +the header file must also include information which identifies the +creator of the algorithmic method. To do this TIG will issue a Unique +Algorithmic Method Identifier (UAI) for any algorithmic method submitted +to TIG. This UAI will be included in the header file of the algorithm +implementation embodying the algorithmic method before it is published +by TIG. The UAI will enable TIG to identify which algorithmic methods +are eligible for Breakthrough Rewards and which ones are not. + +The Game Rules will mandate that where an Algorithm Implementation +(Algorithm B) is a modified implementation of an existing Algorithm +Implementation (Algorithm A) and continues to embody an algorithmic +method substantially similar to that embodied in Algorithm A, if +Algorithm A has an associated UAI then that UAI must be entered into the +header file of Algorithm B. This should create a correct indication of +the heritage of the code back to the original creator of the relevant +embodied algorithmic method. + +There will be a very strong incentive for innovators that benefit from +Breakthrough Rewards to ensure that they protect the persistence of the +Breakthrough Rewards associated with their Breakthrough Algorithm, by +carefully scrutinising the novelty of any challenger algorithmic methods +submitted to TIG. + +## Attribution of Contributor + +The header file must also include an attribution of the name of the +Contributor (referred to as "Submitter" in the TIG Inbound Game License) +of the Contribution and the Game Rules will be amended such that any +code based on the work submitted by that Contributor is required to +attribute the Contributor thus creating an indication of the heritage of +the code back to the original Contributor. diff --git a/docs/guides/innovating.md b/docs/guides/innovating.md index 86da316..06768f5 100644 --- a/docs/guides/innovating.md +++ b/docs/guides/innovating.md @@ -69,9 +69,13 @@ git pull public 2. Make a copy of `tig-algorithms//template.rs` or an existing algorithm (see notes) 3. Make sure your file has the following notice in its header if you intend to submit it to TIG: ``` -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -82,6 +86,10 @@ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` + * If your implementation is based on an algorithmic method submitted to TIG, you must attribute your implementation to it (example UAI: `c001_b001`) + * UAI of a method is detailed inside `tig-breakthroughs//.md` + * Methods have branch name `/method/` + * If your implementation is based on an algorithmic method outside of TIG, set UAI to `null` 4. Rename the file with your own `` 5. Edit `tig-algorithms//mod.rs` to export your algorithm and test it: ``` @@ -141,95 +149,8 @@ language governing permissions and limitations under the License. * If you are copying and modifying an algorithm that has been submitted to TIG, make sure to use the `innovator_outbound` version * Do not include tests in your algorithm file. TIG will reject your algorithm submission. * Only your algorithm's rust code gets submitted. You should not be modifying `Cargo.toml` in `tig-algorithms`. Any extra dependencies you add will not be available when TIG compiles your algorithm -* If you need to use random number generation be sure to use `let mut rng = StdRng::seed_from_u64(challenge.seed as u64)` to ensure your algorithm is deterministic. -* To test cuda, edit the following test, and use the command `cargo test -p tig-algorithms --features cuda -- --nocapture`: -``` -#[cfg(feature = "cuda")] -#[cfg(test)] -mod cuda_tests { - use std::collections::HashMap; - - use super::*; - use cudarc::driver::*; - use cudarc::nvrtc::compile_ptx; - use std::{sync::Arc, collections::HashMap}; - use tig_challenges::{::*, *}; - - fn load_cuda_functions( - dev: &Arc, - kernel: &CudaKernel, - key: &str, - ) -> HashMap<&'static str, CudaFunction> { - let start = std::time::Instant::now(); - println!("Compiling CUDA kernels for {}", key); - let ptx = compile_ptx(kernel.src).expect("Cuda Kernel failed to compile"); - dev.load_ptx(ptx, key, &kernel.funcs) - .expect("Failed to load CUDA functions"); - let funcs = kernel - .funcs - .iter() - .map(|&name| (name, dev.get_func(key, name).unwrap())) - .collect(); - println!( - "CUDA kernels for '{}' compiled in {}ms", - key, - start.elapsed().as_millis() - ); - funcs - } - - #[test] - fn test_cuda_() { - let dev = CudaDevice::new(0).expect("Failed to create CudaDevice"); - let challenge_cuda_funcs = match &::KERNEL { - Some(kernel) => load_cuda_functions(&dev, &kernel, "challenge"), - None => { - println!("No CUDA kernel for challenge"); - HashMap::new() - } - }; - let algorithm_cuda_funcs = match &::KERNEL { - Some(kernel) => load_cuda_functions(&dev, &kernel, "algorithm"), - None => { - println!("No CUDA kernel for algorithm"); - HashMap::new() - } - }; - - let difficulty = Difficulty { - // Uncomment the relevant fields. - // Modify the values for different difficulties - - // -- satisfiability -- - // num_variables: 50, - // clauses_to_variables_percent: 300, - // -- vehicle_routing -- - // num_nodes: 40, - // better_than_baseline: 250, - - // -- knapsack -- - // num_items: 50, - // better_than_baseline: 10, - - // -- vector_search -- - // num_queries: 10, - // better_than_baseline: 350, - }; - let seed = [0u8; 32]; // change this to generate different instances - let challenge = - Challenge::cuda_generate_instance(seed, &difficulty, &dev, challenge_cuda_funcs) - .unwrap(); - match ::cuda_solve_challenge(&challenge, &dev, algorithm_cuda_funcs) { - Ok(Some(solution)) => match challenge.verify_solution(&solution) { - Ok(_) => println!("Valid solution"), - Err(e) => println!("Invalid solution: {}", e), - }, - Ok(None) => println!("No solution"), - Err(e) => println!("Algorithm error: {}", e), - }; - } -} -``` +* If you need to use random number generation, ensure that it is seeded so that your algorithm is deterministic. + * Suggest to use `let mut rng = SmallRng::from_seed(StdRng::from_seed(challenge.seed).gen())` ## Locally Compiling Your Algorithm into WASM @@ -251,7 +172,8 @@ git push origin / ## Making Your Submission -You will need to burn 0.001 ETH to make a submission. Visit https://play.tig.foundation/innovator and follow the instructions. + +10 TIG will be deducted from your Available Fee Balance to make a submission. You can topup via the [Benchmarker page](https://play.tig.foundation/benchmarker) **IMPORTANT:** * Submissions are final and cannot be modified after they are made diff --git a/docs/guides/voting.md b/docs/guides/voting.md new file mode 100644 index 0000000..a29d426 --- /dev/null +++ b/docs/guides/voting.md @@ -0,0 +1,426 @@ + +# Breakthrough Rewards Guide for Token Holders + +## SUMMARY + +- **Vote on whether an algorithmic method is eligible to earn potential Breakthrough Rewards.** + +- **One token one vote.** + +- **Assumed that vote will be exercised to maximise token value.** + + - **Token holders** determine patentability through an assessment of novelty and inventiveness + + - **Benchmarkers** determine performance through adoption (the “TIG synthetic market”) + +- **Relevant considerations for token holders:** + + - **Distinguishing algorithmic method from algorithmic implementation** + + - **Assessing algorithmic method for novelty** + + - **Assessing algorithmic method for inventiveness** + + - **Assessing the impact of your voting decision on token value** + +## What are Breakthrough Rewards and why does TIG offer them ? + +- Breakthrough Rewards are a notional class of TIG token reward that is reserved for rewarding ONLY innovation in algorithmic methods. + +- The efficiency gains from improved algorithmic methods versus code optimizations in implementations can differ greatly in impact and scale. + +**Performance** + +- Generally, improvements in algorithmic methods tend to yield exponential or order-of-magnitude efficiency gains by changing the problem-solving approach, while code optimizations tend to provide incremental improvements by refining an existing algorithm's implementation [*For illustrative examples see Appendix 1*]. It is important therefore, that TIG places emphasis on incentivising the submission of innovative algorithmic methods. TIG intends to do that by introducing a process for identifying algorithmic methods that may be eligible for higher rewards. + +- To reflect the difference in impact and scale that improvements in algorithmic methods can have compared with code optimisations, TIG seeks to offer higher rewards for innovation in algorithmic methods. These higher rewards, so called “**Breakthrough Rewards”**, are made available through both**; (i)** a proportion of rewards in each Round being available to reward innovative algorithmic methods; and **(ii)** the design of the protocol enabling the rewards for innovative algorithmic methods to persist for multiple Rounds where they continue to provide the basis of algorithm implementations adopted by Benchmarkers. + +**Intellectual Property** + +- In addition to the significant performance gains potentially available from innovative algorithmic methods, whilst not patentable per se, they can also, subject to certain criteria (including being applied to provide a technical effect), provide the basis for a patent application. If a patent is applied for and granted it will contribute to the value of the intellectual property that underpins the value of TIG tokens. + + +## What is a Token Holder Vote ? + +- TIG believes that the determination of whether an algorithmic method is eligible for Breakthrough Rewards should be done by a token weighted vote. + +- Because the value of TIG tokens should correlate with the acquisition, by TIG, of innovative algorithmic methods and associated valuable intellectual property and because both of these are incentivised by the offer of Breakthrough Rewards, TIG believes that the interests of TIG token holders are very well aligned with the objective of correctly determining when eligibility for potential Breakthrough Rewards is appropriate. + +- Only votes cast will be counted for the purposes of determining the result of the Token Holder Vote. Abstentions and uncast votes will not be counted. + +- Delegation of token votes will not be enabled initially but may be introduced at some point in the future. + +- If 50% or more of the votes cast vote affirmatively, then the subject algorithmic method will be eligible for potential Breakthrough Rewards from the beginning of the Round in which it is made available for benchmarking. Whether an algorithmic method with the potential to earn Breakthrough Rewards does earn those rewards will depend on adoption by Benchmarkers sufficient to reach the threshold for merger. + +- Algorithmic methods which are the subject of the vote will be made public for review at the beginning of the Round commencing one Round after the Round in which the algorithmic method was submitted to TIG. + +- Voting in each Token Holder Vote will be open for the Round commencing two Rounds after the Round in which the algorithmic method subject to the vote was submitted to TIG. + +- The result of the vote will be final. There will be no appeals process. + +## When does a Token Holder Vote occur ? + +- A Token Holder Vote will be called by TIG each time an innovator requests a determination of whether their submitted algorithmic method is eligible for potential Breakthrough Rewards. + +## Do you qualify to vote ? + +- If you own a TIG token and subject that token to a lock for a minimum determined period, then you are entitled to exercise one vote with respect to that token in the Token Holder Vote. Your influence on the outcome of the vote will therefore be weighted according to how many TIG tokens you hold **and** lock when voting. + +## How do I vote ? + +- Please see the [Token Holder page](https://play.tig.foundation/token-holder) for details. + +## What are you voting on ? + +- **You are voting on whether you think the algorithmic method subject to review is eligible for potential Breakthrough Rewards.** + +## What should you consider when exercising your vote ? + +- TIG believes that, amongst other things, the value of the TIG token will be best enhanced by the capture of innovative algorithmic methods which are patentable (and therefore novel and inventive) according to respective patent laws in different jurisdictions. + +- **How you vote is entirely up to you.** + +- When voting; assuming you wish to enhance the value of the TIG tokens that you hold, it is important that you are equipped to recognise the kind of innovation that Breakthrough Rewards are intended to incentivise. + +- Where an algorithmic method is submitted to the game embodied in a code implementation, first you should distinguish the algorithmic method embodied in the algorithm implementation from the way the algorithmic method is expressed in the code implementation. [***See Appendix 2***]. + +- Having identified the algorithmic method, you should then determine the following: + + - Is the algorithmic method **novel** ? [***See Appendix 3***]. + + - Is the algorithmic method **innovative** (i.e. is the method not obvious to someone skilled in the art) ? [***See Appendix 3***]. + +- ` `Algorithmic methods are the proper subject of a Token Holder Vote, whilst code implementations are not. + +- TIG anticipates that the contributor of the incumbent algorithmic method will advocate **against** the novelty and inventiveness of the challenger algorithmic method where appropriate, and the challenger will advocate **for** its novelty and inventiveness. We will establish a discord channel for each Token Holder Vote so that these arguments on each side can be put, examined, and challenged. This should provide a rich, case specific, body of knowledge to help with the assessment that you will be making. + +- TIG accepts that not all TIG token holders will have the necessary skills or experience to make an informed decision on whether eligibility for potential Breakthrough Rewards is appropriate in every specific case, but we hope that sufficient people with the requisite skill, knowledge and experience will engage, advocate and vote in such a way that eligibility is appropriately awarded. In the future we may introduce delegation of votes so that token holders can delegate analysis to experts that they trust to vote in a way that maximises token value. + +- Essentially, we expect a rational token holder when voting in the Token Holder Vote to be assessing the novelty and inventiveness of a contributed algorithmic method, and by so doing, its patentability. To maximise the likelihood that an innovative algorithmic method will be ratified by Token Holder Vote as eligible for potential Breakthrough Rewards, contributors seeking Breakthrough Rewards have been advised to disclose the following information so that it may be assessed by token holders: + + - **Novelty:** Results of a prior art search to identify any existing disclosures that might affect the novelty of your algorithmic method. + + - **Inventiveness:** Documentation supporting how the algorithmic method differs from existing solutions (i.e. is non-obvious) and any new technical effect it achieves. + + - **Technical Effect:** Documentation supporting patentability showing how the algorithmic method offers the potential to provide the basis for significant technical advancements. Document how the algorithmic method might have **real world application** e.g. in fields such as computer security, medical imaging, autonomous systems. or data compression. + + +## The implications of the outcome of the token holder vote for the TIG protocol + +To further assist you in exercising your vote in an informed way, we have set out below some of the implications of affirming an algorithmic method as being eligible for potential Breakthrough Rewards. + +### Impact on Innovator Incentives + +- A voter may be tempted to default to voting affirmatively on each Token Holder Vote with the intent of making an algorithmic method eligible for Breakthrough Rewards to ensure that there is at least an option for TIG to capture intellectual property with respect to the algorithmic method. This might seem the safe option to ensure that TIG can capture innovation presented to it. But you should note that TIG has the right to apply for patents based on all algorithmic methods contributed to the game with an associated request for a Token Holder Vote regardless of the outcome of the vote. + + You should endeavour to avoid affirming eligibility for potential Breakthrough Rewards for algorithmic methods that are not, novel and inventive because this can have serious consequences by prejudicing and stifling innovation that does satisfy those criteria. + + An algorithmic method that is incorrectly made eligible for potential Breakthrough Rewards may, if sufficiently adopted by benchmarkers, either; **(i)** deprive an innovator of their continuing Breakthrough Rewards thus disincentivising future innovation from that innovator and other observers; or **(ii)** deprive the TIG Treasury of bootstrap rewards (see below). Bootstrap Rewards should be used to fund/incentivise research towards an algorithmic breakthrough corresponding to challenges featured in TIG. + +### Bootstrap Rewards + +- It is possible, particularly in the early phases of TIG, that certain challenges will **not** be addressed in the Game by algorithmic methods that are eligible for Breakthrough Rewards because the algorithmic methods lack novelty or inventiveness or because no relevant algorithmic method has reached the threshold to be merged. In this situation those TIG token rewards ordinarily available for innovators as Breakthrough Rewards will be unallocated (we call these unallocated rewards, “**Bootstrap Rewards**”). + +- Bootstrap Rewards will be allocated to the TIG Treasury. Bootstrap Rewards will be “ringfenced” and used very specifically by TIG (through distribution of bounty rewards) to incentivise the development of algorithmic methods which address challenges for which there are not presently algorithmic methods in the TIG Game which are eligible for potential Breakthrough Rewards or not merged. This should help algorithmic methods in the Game reach state of the art more quickly and this will accelerate the capture of meaningful value by TIG. This form of “centralised” allocation should only persist for as long as there are challenges in the game which are not solved by algorithmic methods that are earning Breakthrough Rewards. + +## Intellectual Property + +- TIG will only have the rights to file patent applications based on algorithmic methods where the contributor has sought Breakthrough Rewards by making a request for a Token Holder Vote. Innovators will only request Token Holder Votes if they have reasonable confidence that the process is fair and consistent allowing them to assess their chances of success in the vote with reasonable certainty. + +## Commercial Value + +- Algorithmic methods have the greatest potential to create the most value for TIG and any decisions should seek to ensure that innovators are sufficiently incentivised to submit them. Inappropriately voting against eligibility of an algorithmic method in a Token Holder Vote may cause valuable innovation to be lost to TIG. + + + +# Appendix 1 + +**Comparison of Algorithmic Methods and Code Optimisations** + +## 1. Order of Magnitude Differences (Big-O Complexity) + +- **Algorithmic Improvement**: An improved algorithm can shift the complexity class of a solution, significantly impacting runtime, especially as input sizes grow. For instance, switching from a quadratic algorithm O(n2)O(n2)to a linear one O(n)O(n) can make a previously impractical solution feasible for large datasets. + + - **Example**: Mergesort (O(nlog⁡n)O(nlogn)) vs. Bubble Sort (O(n2)O(n2)) for sorting large lists. A dataset of 1,000,000 items might take Mergesort milliseconds, while Bubble Sort would take hours or even days. + + - **Indicator:** A useful indicator of whether the underlying algorithmic method of an algorithm implementation has changed will be to test for any change in the efficiency of the implementation (where efficiency means the number of solutions per nonce). If the number of solutions per nonce has changed that will be a sufficient condition to conclude that there has been a change in the underlying algorithmic method. Note however that it is not always the case that a change to the underlying algorithm will result in a change in the number of solutions per nonce and so caution should be exercised not to reject an algorithmic method based on this indicator alone. + +- **Code Optimization**: Within a given complexity class, optimizations yield speedups by improving constant factors or reducing overhead, often providing 10-50% performance gains rather than orders of magnitude. + + - **Example**: Optimizing Mergesort by using in-place operations or efficient memory management can make the same O(nlog⁡n)O(nlogn) algorithm faster but without changing its overall complexity. + +## 2. Scalability for Large Datasets + +- **Algorithmic Improvement**: Algorithmic advancements typically show exponential scalability. For instance, switching from a brute-force search (O(2n)O(2n)) to a dynamic programming solution (O(n2)O(n2)) for certain problems can enable the handling of very large datasets that were previously infeasible. + + - **Example**: A naive brute-force solution for the traveling salesperson problem (TSP) takes O(n!)O(n!), but an approximation algorithm can reduce it to polynomial time, making the problem solvable for hundreds of cities rather than just a few dozen. + +- **Code Optimization**: While code optimizations improve runtime, they rarely enable handling fundamentally larger datasets, as they don’t change the growth rate. They mostly improve efficiency for smaller to moderate-sized datasets. + + - **Example**: Optimizing a brute-force TSP solution with efficient memoization and loop unrolling can improve performance by a constant factor but won’t make it feasible for large datasets, as the factorial growth remains. + +## 3. Resource Efficiency (Memory, Power, etc.) + +- **Algorithmic Improvement**: Changes in algorithms can drastically reduce memory usage, which is critical in constrained environments. For instance, algorithms that use **space-efficient data structures** (e.g., Bloom filters for probabilistic set membership) provide functionality with far lower memory requirements than traditional methods. + + - **Example**: Dijkstra’s algorithm for shortest paths uses a priority queue, which reduces both time and memory usage compared to simpler graph traversal techniques. + +- **Code Optimization**: Code optimizations help by refining memory usage through strategies like data locality, loop unrolling, and inlining functions. Gains tend to be moderate but can add up when processing high-frequency data or embedded systems. + + - **Example**: Optimizing Dijkstra’s algorithm by reducing redundant allocations and using in-place updates can yield a noticeable speedup but won’t reduce memory asymptotically. + +## 4. Impact of Problem Complexity on Efficiency Gains + +- **Algorithmic Improvement**: The more complex the problem, the larger the potential gain from algorithmic innovation. NP-hard or NP-complete problems benefit greatly from efficient approximation algorithms or heuristics that deliver "good enough" solutions in polynomial time instead of exponential time. + - **Example**: An approximation algorithm for the knapsack problem can yield near-optimal solutions in polynomial time, making it usable for large-scale decision-making tasks that brute-force methods could never handle. + +- **Code Optimization**: For simpler problems, code optimizations are more effective because there is often less room for improvement with algorithm changes. Optimizations can be substantial in domains like graphics or signal processing where real-time performance is critical, and the underlying algorithms are already optimized. + + - **Example**: In video processing, optimizations like SIMD (single instruction, multiple data) processing can achieve high-speed frame rendering, though the basic algorithms (e.g., filtering, transformation) may stay constant. + +## Summary Comparison Table + +|**Aspect**|**Algorithmic Improvement**|**Code Optimization**| +| :- | :- | :- | +|**Impact on Big-O Complexity**|Can reduce asymptotic complexity (e.g., O(n2)→O(nlog⁡n)O(n2)→O(nlogn))|Doesn’t change Big-O; improves constant factors| +|**Scalability**|Enables handling much larger datasets or complex problems|Limited scalability improvements; helps with small to moderate datasets| +|**Typical Performance Gain**|Orders of magnitude (e.g., 10x to 1000x speedups)|Typically 10-50% improvement, up to 2-5x in extreme cases| +|**Applicability**|Effective for complex or large-scale problems|Effective for refining already chosen algorithms| +|**Resource Efficiency**|Reduces memory, power, or processing needs at a fundamental level|Reduces resource consumption at implementation level| + +## Real-World Example: Sorting Large Datasets + +- **Algorithmic Method**: Switching from a sorting algorithm like Bubble Sort (O(n2)O(n2)) to QuickSort (O(nlog⁡n)O(nlogn)) can reduce runtime from hours to seconds for large datasets, demonstrating order-of-magnitude improvement. + +- **Code Optimization**: Within QuickSort, optimizations like pivot selection improvements or reducing recursion depth provide further speed-ups but at a smaller scale (often 10-20% faster). + + + +# Appendix 2 + +## Distinguishing between an Algorithmic Method and a Code Implementation + +Algorithmic improvements offer substantial scalability and efficiency gains, especially for large or complex problems, by changing the approach to solving the problem. Code optimizations, on the other hand, fine-tune the implementation for moderate speed or resource improvements without fundamentally altering how the problem is addressed. + +To distinguish between an algorithmic method and a code optimization of an implementation of an algorithmic method, it is helpful to focus on their purpose and level of abstraction: + +## Algorithmic Method + +- **Definition**: An algorithmic method is the fundamental approach or strategy for solving a problem, independent of specific code or language. It’s the "recipe" for how the problem is tackled. + +- **Characteristics**: + - **Conceptual Focus**: Concerned with the *steps* or *processes* required to solve the problem at a high level. + + - **Algorithm Complexity**: Includes decisions about the algorithm's efficiency (e.g., using a divide-and-conquer algorithm rather than a brute-force approach). + + - **Language-Agnostic**: The method itself is not bound to a particular programming language or code style. + +- **Example**: Choosing between a binary search (O(log n)) and a linear search (O(n)) for searching in an array. This choice fundamentally changes the performance characteristics based on the algorithm. + +## Code Optimization + +- **Definition**: Code optimization involves making improvements to an existing implementation of an algorithm to enhance its efficiency, readability, or maintainability, without altering the core steps of the algorithm itself. + +- **Characteristics**: + - **Implementation Focus**: Works on refining the code that executes the algorithm, often targeting lower-level details like memory usage, computational overhead, or specific language/library features. + + - **Micro-Efficiency**: Often about making the code faster or more efficient within the constraints of the chosen algorithm, such as reducing loop overhead, or minimizing function calls. + + - **Language-Specific**: Tied to how the algorithm is implemented in code, considering the features and limitations of the language and runtime environment. + +- **Example**: In a binary search implementation, an optimization could involve using bitwise shifts instead of division to calculate the midpoint, or unrolling loops to reduce function call overhead. + +## Key Differences + +|**Aspect**|**Algorithmic Method**|**Code Optimization**| +| :- | :- | :- | +|**Level of Abstraction**|High-level strategy|Low-level, specific to code and language| +|**Goal**|Selects a method based on theoretical efficiency|Refines the chosen algorithm’s efficiency| +|**Change in Complexity**|Alters the Big-O complexity (e.g., O(n) to O(log n))|Doesn't usually change Big-O complexity| +|**Examples**|Choosing binary vs. linear search|Replacing division with shifts in midpoint calc| + +You may consider automated ways of distinguishing between algorithmic methods and code optimisations, but this is challenging is due to the abstract nature of algorithmic methods versus the concrete nature of code optimizations. However, automated analysis can help, especially when focused on examining patterns in code structure, performance metrics, and complexity analysis. Here are some techniques and tools that could contribute to an automated distinction: + +## Static Code Analysis for Complexity Analysis + +- **Purpose**: This approach can identify algorithmic-level changes by analyzing code complexity. + +- **Method**: Use tools to automatically calculate the time complexity of code snippets (e.g., Big-O Notation) based on control structures (loops, recursive calls) and data structures. Significant differences in complexity before and after changes often indicate an algorithmic method change. + +- **Tools**: **Big-O Calculator** (like SymPy for theoretical analysis), **Cyclomatic Complexity Tools** (like SonarQube or Radon for Python). These tools can flag significant structural changes that may represent shifts in the underlying algorithm. + +## Performance Profiling and Runtime Analysis + +- **Purpose**: Automated profiling can reveal whether optimizations impact the entire algorithm or are local refinements. + +- **Method**: Using profiling tools, track execution times and memory usage across different parts of the code. Look for specific functions or lines with changed CPU or memory usage patterns. Smaller, localized performance improvements are often code optimizations, while more significant performance shifts suggest algorithmic changes. + +- **Tools**: **Profilers** (like Python’s cProfile, Py-Spy, or Intel VTune) can help monitor function-level runtime changes and memory usage, providing insights on where the optimizations are applied. + +## Code Similarity and Structure Analysis + +- **Purpose**: To identify structural changes in code that may imply a different algorithmic approach versus fine-tuning within the same structure. + +- **Method**: Using abstract syntax trees (ASTs) or control flow graphs, compare the structure of two versions of code to detect major changes, such as different types of loops, recursion, or conditional branches. Major structural changes suggest an algorithmic shift, while minor adjustments usually indicate code optimization. + +- **Tools**: **AST-based tools** (like Python’s ast module, Clang AST Matcher) to analyze differences in tree structures across code versions. + +## Automated Benchmark Testing + +- **Purpose**: Detect performance shifts at different input sizes to distinguish changes that affect complexity (algorithmic) versus fixed efficiency improvements (code optimization). + +- **Method**: Set up a range of input sizes and measure the execution time and memory usage. Plot these results to identify the growth rate. A new algorithm will often show a different trend in growth rate, while a code optimization will display similar trends with only faster execution. + +- **Tools**: **Benchmarking libraries** (like Google Benchmark for C++, pytest-benchmark for Python) to automate tests and capture metrics across different input sizes. + +## Machine Learning for Pattern Recognition (Experimental) + +- **Purpose**: Use machine learning to recognize patterns associated with algorithmic changes versus code optimizations based on historical examples. + +- **Method**: Train models on labeled datasets of code differences, using features like complexity changes, structural edits, and performance metrics. Though still experimental, this approach could potentially identify trends that characterize algorithmic shifts versus optimizations. + +- **Tools**: Machine learning libraries, such as **TensorFlow** or **scikit-learn**, can be trained to analyze code features if you have an adequately labeled dataset. + +## Putting It All Together + +An effective automated process would likely combine several of these methods: + +1. **Complexity Analysis and Structure Comparison** to flag potential algorithm changes. + +1. **Profiling and Benchmarking** to confirm performance changes and their scope. + +1. **Pattern Recognition Models** to predict the nature of changes if trained models are available. + +By layering these methods, an automated system could more accurately differentiate between high-level algorithmic changes and lower-level code optimizations. + +An algorithmic method can be reasonably considered innovative over an existing method when it demonstrates clear, meaningful advancements in solving a particular problem, typically by improving performance, applicability, or problem-solving capabilities in ways that existing methods cannot. + + + +# Appendix 3 + +## Novelty and Inventiveness + +An algorithmic method is generally considered innovative when it either introduces a novel way to solve a problem or extends applicability or efficiency beyond prior methods**.** + +- **Novelty:** The algorithm must be new and not previously disclosed in any prior art (publications, patents, or publicly accessible methods). + +- **Application**: If an algorithm introduces a novel approach to solving a problem or achieves results in a way that has not been previously documented, it may meet this criterion. + + - **Example**: A new method for efficiently compressing data using an original algorithm could be considered novel if it achieves results that other compression algorithms do not. + +**Prior art** refers to any evidence that an invention or idea was already known before a particular date, in this case the date of submission of the algorithmic method to TIG. This includes previous patents, publications, products, demonstrations, or any public disclosures that describe similar technology, processes, or concepts. + +## Practical Definition of Prior Art + +**Prior Art**: Any publicly accessible information that describes the same or a similar invention, idea, or concept, which could indicate that the invention in question is not entirely new. Prior art can be anything publicly available in any form, including patents, journal articles, books, websites, products, and even conference presentations. + +In simpler terms, if the core idea behind a new invention or concept is already documented in any format that others can access or view, it qualifies as prior art. + +## How Prior Art is Used to Assess Innovation + +Prior art plays a critical role in determining **whether an idea is truly innovative (or patentable) by helping assess two main criteria**: + +1. **Novelty**: An invention must be novel, meaning it must differ in some meaningful way from what is already known. If prior art describes the invention’s main features, the invention is not considered novel + +1. **Non-Obviousness**: Even if the invention isn’t exactly described in prior art, it must also be non-obvious, meaning it cannot be a trivial or obvious extension of existing ideas. The concept of "obviousness" is often subjective but generally means that someone skilled in the relevant field would not have easily thought of the invention. For example: + + 1. If an idea for a new app closely resembles an existing app but adds minor features anyone skilled in app design might think of, it may be deemed obvious. + +## Practical Process to Determine Innovation Using Prior Art + +1. **Search for Similar Inventions**: Perform prior art searches across patent databases, scientific publications, trade literature, and other sources. + +1. **Analyze Differences**: Compare the proposed invention’s core claims against prior art. Look for any significant technical or functional distinctions. + +1. **Assess Obviousness**: Determine if the invention would be an obvious adaptation of known solutions based on the prior art. + +By systematically assessing whether an invention introduces new and non-obvious features beyond what prior art reveals, it’s possible to determine whether the idea qualifies as innovative. + +## Prior art serves as the baseline against which inventiveness is evaluated + +Here's how they relate to each other in detail: + +**1. Prior Art Establishes the Known Landscape** + +Prior art encompasses all known information in a particular field, including prior patents, published research, public demonstrations, and commercially available products. This body of knowledge sets the groundwork for assessing a new invention, essentially defining what is already known or has been achieved. + +**2. Inventiveness Requires a Significant Advancement Over Prior Art** + +For an invention to be considered **inventive** (or non-obvious), it must not only be new but also involve an element of ingenuity beyond what someone skilled in the field might naturally derive from prior art. The invention needs to solve a problem or offer a benefit in a way that isn’t apparent or trivial based on existing knowledge. In other words, if an invention closely resembles what is described in prior art, or could be easily derived from it by someone familiar with the field, it lacks inventiveness. However, if it introduces a novel approach or a surprising solution, it may meet the inventiveness criterion. + +**3. Prior Art is Used to Determine Obviousness (Lack of Inventiveness)** + +Prior art is used to assess whether an invention is **obvious**: + +- **Direct Similarity**: If prior art describes an identical or nearly identical invention, the new invention is not novel or inventive. + +- **Logical Extensions**: If the new invention is a predictable modification or extension of prior art, it may be deemed obvious. For example, changing the size, material, or shape of a known device without introducing a new function would likely be considered obvious. + +- **Combination of Prior Art**: If the invention combines elements from different prior art sources in a straightforward way, it might also be considered obvious unless it leads to an unexpected or surprising result. For instance, combining two known mechanisms in a way that anyone in the field would naturally think of would likely be deemed an obvious combination of prior art. + +**4. Inventiveness (Non-Obviousness) Often Requires a "Surprising" Element** + +The more unexpected or surprising the solution provided by the invention, the stronger the case for inventiveness. If an invention introduces a solution that a person skilled in the art would not have thought to derive from the prior art, it is more likely to be deemed inventive. + +## Summary of the Relationship + +In short: + +- **Prior art sets the reference point** against which a new invention’s inventiveness is measured. + +- **Inventiveness** (non-obviousness) reflects how the invention goes beyond the known prior art in a non-trivial, unexpected way. + +Thus, prior art is essential in judging inventiveness because it helps differentiate between truly innovative advancements and mere adaptations of existing knowledge. + +An algorithmic method can be reasonably considered innovative over an existing method when it demonstrates clear, meaningful advancements in solving a particular problem, typically by improving performance, applicability, or problem-solving capabilities in ways that existing methods cannot. + +Here are the key criteria for determining when an algorithmic method is genuinely innovative: + +## Improved Asymptotic Complexity + +- **Criteria**: The new algorithm shows a better Big-O complexity (e.g., improving from O(n2)O(n2) to O(nlog⁡n)O(nlogn) or O(n)O(n)), especially in worst-case or average-case scenarios. + +- **Example**: The development of QuickSort (average O(nlog⁡n)O(nlogn)) offered an innovative improvement over Bubble Sort (O(n2)O(n2)) for sorting, making it more practical for larger datasets. Similarly, Dijkstra’s algorithm for shortest paths was later innovatively improved with A\* for specific scenarios. + +## Significant Performance Gains on Practical Inputs + +- **Criteria**: Even if the Big-O complexity is similar, the new method shows substantially faster performance or lower resource consumption on real-world datasets, often due to reduced constants, more efficient data handling, or better memory locality. + +- **Example**: The Strassen algorithm for matrix multiplication has a better theoretical complexity than standard matrix multiplication. However, some algorithms, even without better complexity, may still be innovative if they perform more efficiently in real-world use cases. + +## Extended Applicability to Broader Problem Classes + +- **Criteria**: The new algorithm applies to a wider range of scenarios, input types, or constraints than previous methods, extending the range of problems that can be solved effectively. + +- **Example**: The invention of dynamic programming extended the applicability of algorithms to optimization problems with overlapping subproblems, such as in the knapsack problem or sequence alignment, which weren’t solvable efficiently with previous methods. + +## Enhanced Robustness and Practicality + +- **Criteria**: The new algorithm handles edge cases or special cases (e.g., large-scale data, sparse data, highly variable data distributions) more gracefully than its predecessors, which makes it more robust for general use. + +- **Example**: K-Means++ is an innovation over the standard K-Means clustering algorithm because it initializes centroids in a way that avoids poor clustering outcomes, especially on skewed data, enhancing robustness. + +## New Approach or Paradigm + +- **Criteria**: The algorithm introduces a fundamentally new approach, technique, or paradigm that shifts how a problem is conceptualized or solved, opening up new avenues for solutions. + +- **Example**: The development of neural networks and backpropagation introduced a paradigm shift in machine learning, allowing problems that were previously considered infeasible to be solved with learning-based approaches. + +## Improved Scalability or Feasibility for Large Data Sets + +- **Criteria**: The algorithm is designed to be more scalable, specifically addressing issues like memory constraints, parallelization, or distributed computing. + +- **Example**: MapReduce, by Google, introduced an innovative algorithmic method for parallel and distributed processing, making it feasible to process massive datasets in a way that standard methods couldn’t handle efficiently. + +## Reduction of Resource Requirements (Memory, Power, etc.) + +- **Criteria**: The new method substantially reduces resource requirements, such as memory usage, power consumption, or communication overhead, often by optimizing data structures or computational pathways. + +- **Example**: Bloom filters are an innovative space-efficient probabilistic data structure that allows for quick set membership testing with minimal memory usage, which is valuable in constrained environments. + +## Proof of Optimality or Approximation Guarantees + +- **Criteria**: If an algorithm provides a proven optimal solution or a guaranteed approximation ratio for NP-hard or difficult problems, it represents a strong innovation. + +- **Example**: Approximation algorithms for NP-hard problems, like the Traveling Salesman Problem (TSP), where algorithms can find solutions within a guaranteed percentage of the optimal, are considered innovative because they provide useful solutions where exact methods are impractical. diff --git a/docs/licenses/benchmarker_outbound_license.pdf b/docs/licenses/benchmarker_outbound_license.pdf index db84bf8..57e9c31 100644 Binary files a/docs/licenses/benchmarker_outbound_license.pdf and b/docs/licenses/benchmarker_outbound_license.pdf differ diff --git a/docs/licenses/commercial_license.pdf b/docs/licenses/commercial_license.pdf index a4ad93d..1ddf464 100644 Binary files a/docs/licenses/commercial_license.pdf and b/docs/licenses/commercial_license.pdf differ diff --git a/docs/licenses/inbound_license.pdf b/docs/licenses/inbound_license.pdf index a3da2d1..8c29470 100644 Binary files a/docs/licenses/inbound_license.pdf and b/docs/licenses/inbound_license.pdf differ diff --git a/docs/licenses/innovator_outbound_license.pdf b/docs/licenses/innovator_outbound_license.pdf index 51d8b99..cdb1869 100644 Binary files a/docs/licenses/innovator_outbound_license.pdf and b/docs/licenses/innovator_outbound_license.pdf differ diff --git a/docs/licenses/open_data_license.pdf b/docs/licenses/open_data_license.pdf index a1f7359..38310da 100644 Binary files a/docs/licenses/open_data_license.pdf and b/docs/licenses/open_data_license.pdf differ diff --git a/extra/explo b/extra/explo deleted file mode 100644 index 1c34713..0000000 Binary files a/extra/explo and /dev/null differ diff --git a/funding.json b/funding.json new file mode 100644 index 0000000..3956747 --- /dev/null +++ b/funding.json @@ -0,0 +1,5 @@ +{ + "opRetro": { + "projectId": "0xb0898866c2c537c61b37d04916e3879ef78fed630181c1ffaf492499d174f0bf" + } +} diff --git a/swagger.yaml b/swagger.yaml index e5e20d5..1d63d4d 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.3 +openapi: 3.0.0 info: title: The Innovation Game API description: |- @@ -31,15 +31,15 @@ paths: description: |- # Notes - * Query parameter `` can be latest block for **ANY** round. Use `/get-block` endpoint - - * Fields `algorithm.code` and `wasm.wasm_blob` will always be `null` + * Query parameter `` must be the latest block. Use `/get-block` endpoint * If `algorithm.round_pushed != null`, the data can be accessed via: * `code`: `https://github.com/tig-foundation/tig-monorepo/blob//tig-algorithms/src//benchmarker_outbound.rs` * `wasm_blob`: `https://github.com/tig-foundation/tig-monorepo/blob//tig-algorithms/wasm/.wasm` where `` is `/` + + * `names` is a map of `` to ENS name (only if one exists) parameters: - name: block_id in: query @@ -61,7 +61,7 @@ paths: description: |- # Notes - * Query parameter `` must be latest block for **CURRENT** round. Use `/get-block` endpoint + * Query parameter `` must be the latest block. Use `/get-block` endpoint * Query parameter `` must be lowercase and start with `0x` @@ -112,132 +112,11 @@ paths: application/json: schema: $ref: '#/components/schemas/GetBenchmarkDataResponse' - /get-block: + /get-binary-blob: get: tags: - GET - summary: Get latest block for a round - description: |- - # Notes - - * Specify query parameter `` to get the latest block for that round. - - * Omit query parameter `` to get the latest block for the current round. - - * Set `` to `true` to include data for `block.data`. - parameters: - - name: round - in: query - required: false - schema: - type: integer - - name: include_data - in: query - required: false - schema: - type: boolean - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/GetBlockResponse' - /get-challenges: - get: - tags: - - GET - summary: Get latest data for challenges - description: |- - # Notes - - * Query parameter `` must be latest block for **CURRENT** round. Use `/get-block` endpoint - parameters: - - name: block_id - in: query - required: true - schema: - $ref: '#/components/schemas/MD5' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/GetChallengesResponse' - /get-fee-balance: - get: - tags: - - GET - summary: Get fee balance for a player - description: |- - # Notes - - * Query parameter `` must be latest block for **CURRENT** round. Use `/get-block` endpoint - - * Query parameter `` must be lowercase and start with `0x` - - * Returns current fee balance and all topups for the player - parameters: - - name: block_id - in: query - required: true - schema: - $ref: '#/components/schemas/MD5' - - name: player_id - in: query - required: true - schema: - $ref: '#/components/schemas/Address' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/GetFeeBalanceResponse' - /get-players: - get: - tags: - - GET - summary: Get latest round data for all players - description: |- - # Notes - - * Query parameter `` can be latest block for **ANY** round. Use `/get-block` endpoint - - * If query parameter `` is set to `benchmarker`, returns players who had active benchmarks in this round - - * If query parameter `` is set to `innovator`, returns players who had active algorithms in this round - - * `player.round_earnings` will always have a value. All other fields will be `null` unless the player has active benchmarks - parameters: - - name: block_id - in: query - required: true - schema: - $ref: '#/components/schemas/MD5' - - name: player_type - in: query - description: Type of player - required: true - schema: - type: string - enum: - - benchmarker - - innovator - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/GetPlayersResponse' - /get-wasm-blob: - get: - tags: - - GET - summary: Get WASM blob for a specific algorithm + summary: Get binary blob for a specific algorithm parameters: - name: algorithm_id in: query @@ -252,6 +131,164 @@ paths: schema: type: string format: binary + /get-block: + get: + tags: + - GET + summary: Get latest block + description: |- + # Notes + * Returns latest block + * Set `` to `true` to include data for `block.data`. + parameters: + - name: include_data + in: query + required: false + schema: + type: boolean + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetBlockResponse' + /get-difficulty-data: + get: + tags: + - GET + summary: Get latest difficulty data for a specific challenge id + description: |- + # Notes + + * Returns aggregated num_nonces, num_solutions, difficulty data from benchmarks in the last 120 blocks. + + * Query parameter `` must be the latest block. Use `/get-block` endpoint + + * Query parameter `` must be a valid challenge id + parameters: + - name: block_id + in: query + required: true + schema: + $ref: '#/components/schemas/MD5' + - name: challenge_id + in: query + required: true + schema: + $ref: '#/components/schemas/ChallengeId' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetDifficultyDataResponse' + /get-challenges: + get: + tags: + - GET + summary: Get latest data for all challenges + description: |- + # Notes + + * Query parameter `` must be the latest block. Use `/get-block` endpoint + parameters: + - name: block_id + in: query + required: true + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetChallengesResponse' + /get-opow: + get: + tags: + - GET + summary: Get latest OPoW data + description: |- + # Notes + + * Query parameter `` must be the latest block. Use `/get-block` endpoint + * Each opow is for a specific Benchmarker. `opow.block_data.reward` is the reward before any sharing amongst delegators + + * `names` is a map of `` to ENS name (only if one exists) + parameters: + - name: block_id + in: query + required: true + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetOPoWResponse' + /get-player-data: + get: + tags: + - GET + summary: Get latest data for a specific player + description: |- + # Notes + + * Query parameter `` must be the latest block. Use `/get-block` endpoint + + * Query parameter `` must be a valid player id + + * Only includes active deposits + parameters: + - name: block_id + in: query + required: true + schema: + $ref: '#/components/schemas/MD5' + - name: player_id + in: query + description: Id of player + required: true + schema: + $ref: '#/components/schemas/Address' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetPlayerDataResponse' + /get-round-earnings: + get: + tags: + - GET + summary: Get round earnings for algorithms, opow, and players for a specific round + description: |- + # Notes + * `algorithms` is a map of `` to `uint256` + * `opow` is a map of `` to `uint256` + * `players` is a map of `` to dict of reward types (algorithm, benchmark, breakthroughs, delegator) + * `names` is a map of `` to ENS name (only if one exists) + parameters: + - name: round + in: query + description: Id of round + required: true + schema: + type: integer + format: uint32 + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/GetRoundEarningsResponse' /request-api-key: post: tags: @@ -276,6 +313,69 @@ paths: application/json: schema: $ref: '#/components/schemas/RequestApiKeyResponse' + /set-delegatees: + post: + tags: + - POST + summary: Sets your delegatees + description: |- + # Notes + * Can only be updated once every `block.config.deposits.delegatee_update_period` blocks + + * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. + + * If `` is invalid, a `401` error will be returned + + * `` is a map of `` to `float`, where the floats must sum to at most `1.0` + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetDelegateesRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SetDelegateeResponse' + /set-reward-share: + post: + tags: + - POST + summary: Sets your reward share + description: |- + # Notes + * Can only be updated once every `block.config.deposits.reward_share_update_period` blocks + * Must be between 0 to `block.config.deposits.max_reward_share` + + * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. + + * If `` is invalid, a `401` error will be returned + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SetRewardShareRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SetRewardShareResponse' /submit-algorithm: post: tags: @@ -307,6 +407,77 @@ paths: responses: '200': description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitAlgorithmResponse' + /submit-benchmark: + post: + tags: + - POST + summary: Submit a benchmark + description: |- + # Notes + + * This endpoint can only be invoked once every few seconds + + * Order of `solution_nonces` does not matter + + * When a benchmark is confirmed (`benchmark.state != null`), up to `3` nonces will be sampled (`benchmark.state.sampled_nonces`) for which the corresponding `merkle_proof` must be submitted via `/submit-proof` endpoint + + * Header `X-Api-Key` is required. Use `/request-api-key` endpoint. + + * If `` is invalid, a `401` error will be returned + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitBenchmarkRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitBenchmarkResponse' + /submit-deposit: + post: + tags: + - POST + summary: Submit a deposit + description: |- + # Notes + + * `` is the id of the transaction that has sent at least `block.config.deposits.min_lock_amount` TIG to `block.config.deposits.lock_address`. Additionally: + * Must be non-cancellable + * Must be non-transferrable + * Minimum lock duration must be at least 1 week (604800 seconds) + * `` is the log index of the CreateLockupLinearStream event + * If `` is null, then the first CreateLockupLinearStream event will be used + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitDepositRequest' + parameters: + - in: header + name: X-Api-Key + description: from /request-api-key endpoint + schema: + $ref: '#/components/schemas/MD5' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/SubmitDepositResponse' /submit-precommit: post: tags: @@ -336,37 +507,6 @@ paths: application/json: schema: $ref: '#/components/schemas/SubmitPrecommitResponse' - /submit-benchmark: - post: - tags: - - POST - summary: Submit a benchmark - description: |- - # Notes - - * This endpoint can only be invoked once every few seconds - - * Order of `solution_nonces` does not matter - - * When a benchmark is confirmed (`benchmark.state != null`), up to `3` nonces will be sampled (`benchmark.state.sampled_nonces`) for which the corresponding `merkle_proof` must be submitted via `/submit-proof` endpoint - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitBenchmarkRequest' - parameters: - - in: header - name: X-Api-Key - description: from /request-api-key endpoint - schema: - $ref: '#/components/schemas/MD5' - responses: - '200': - description: Success - content: - application/json: - schema: - $ref: '#/components/schemas/SubmitBenchmarkResponse' /submit-proof: post: tags: @@ -403,10 +543,11 @@ paths: summary: Submit a topup transaction description: |- # Notes + + * `` is the id of the transaction that has sent at least `block.config.topups.topup_amount` TIG to the `block.config.topups.topup_address` + * `` is the log index of the CreateLockupLinearStream event + * If `` is null, then the first CreateLockupLinearStream event will be used - * This endpoint can only be invoked once every few seconds - - * `` is the id of the transaction that has burnt the required `block.config.precommit_submissions.topup_amount` TIG to the `block.config.erc20.burn_address` requestBody: content: application/json: @@ -434,15 +575,29 @@ components: type: object properties: id: - type: string + $ref: '#/components/schemas/AlgorithmId' details: $ref: '#/components/schemas/AlgorithmDetails' state: $ref: '#/components/schemas/AlgorithmState' block_data: $ref: '#/components/schemas/AlgorithmBlockData' - code: - type: string + AlgorithmBlockData: + type: object + properties: + num_qualifiers_by_player: + type: object + additionalProperties: + type: integer + format: uint32 + example: {"0x1234...": 5, "0x5678...": 3} + adoption: + $ref: '#/components/schemas/PreciseNumber' + merge_points: + type: integer + format: uint32 + reward: + $ref: '#/components/schemas/PreciseNumber' AlgorithmDetails: type: object properties: @@ -452,8 +607,12 @@ components: $ref: '#/components/schemas/Address' challenge_id: $ref: '#/components/schemas/ChallengeId' - tx_hash: - $ref: '#/components/schemas/TxHash' + breakthrough_id: + $ref: '#/components/schemas/MD5' + type: + $ref: '#/components/schemas/AlgorithmType' + fee_paid: + $ref: '#/components/schemas/PreciseNumber' AlgorithmId: type: string pattern: ^c[0-9]{3}_a[0-9]{3}$ @@ -477,28 +636,16 @@ components: round_merged: type: integer format: uint32 - banned: - type: boolean - AlgorithmBlockData: - type: object - properties: - num_qualifiers_by_player: - type: object - additionalProperties: - type: integer - format: uint32 - example: - "0x100af566d1b9809915fd03ebee5e02e2e7926d98": 10 - "0x0000000000000000000000000000000000000001": 15 - adoption: - $ref: '#/components/schemas/PreciseNumber' - merge_points: + round_active: type: integer format: uint32 - reward: - $ref: '#/components/schemas/PreciseNumber' - round_earnings: - $ref: '#/components/schemas/PreciseNumber' + banned: + type: boolean + AlgorithmType: + type: string + enum: + - wasm + - ptx Benchmark: type: object properties: @@ -513,6 +660,19 @@ components: items: type: integer format: uint64 + BenchmarkDetails: + type: object + properties: + num_solutions: + type: integer + format: uint32 + merkle_root: + $ref: '#/components/schemas/MerkleHash' + sampled_nonces: + type: array + items: + type: integer + format: uint64 BenchmarkSettings: type: object properties: @@ -526,25 +686,34 @@ components: $ref: '#/components/schemas/AlgorithmId' difficulty: $ref: '#/components/schemas/Difficulty' - BenchmarkDetails: - type: object - properties: - num_solutions: - type: integer - format: uint32 - merkle_root: - $ref: '#/components/schemas/MerkleHash' BenchmarkState: + type: object + properties: + block_confirmed: + type: integer + format: uint32 + Binary: + type: object + properties: + algorithm_id: + $ref: '#/components/schemas/AlgorithmId' + details: + $ref: '#/components/schemas/BinaryDetails' + state: + $ref: '#/components/schemas/BinaryState' + BinaryDetails: + type: object + properties: + compile_success: + type: boolean + download_url: + type: string + BinaryState: type: object properties: block_confirmed: type: integer format: uint32 - sampled_nonces: - type: array - items: - type: integer - format: uint64 Block: type: object properties: @@ -555,7 +724,7 @@ components: data: $ref: '#/components/schemas/BlockData' config: - $ref: '#/components/schemas/ProtocolConfig' + type: object BlockDetails: type: object properties: @@ -567,57 +736,202 @@ components: round: type: integer format: uint32 + num_confirmed: + type: object + properties: + algorithm: + type: integer + format: uint32 + benchmark: + type: integer + format: uint32 + binary: + type: integer + format: uint32 + breakthrough: + type: integer + format: uint32 + challenge: + type: integer + format: uint32 + deposit: + type: integer + format: uint32 + fraud: + type: integer + format: uint32 + precommit: + type: integer + format: uint32 + proof: + type: integer + format: uint32 + topup: + type: integer + format: uint32 + num_active: + type: object + properties: + algorithm: + type: integer + format: uint32 + benchmark: + type: integer + format: uint32 + breakthrough: + type: integer + format: uint32 + challenge: + type: integer + format: uint32 + deposit: + type: integer + format: uint32 + opow: + type: integer + format: uint32 + player: + type: integer + format: uint32 + timestamp: + type: integer + format: uint64 BlockData: type: object properties: - confirmed_challenge_ids: - type: array - items: - $ref: '#/components/schemas/ChallengeId' - confirmed_algorithm_ids: - type: array - items: - $ref: '#/components/schemas/AlgorithmId' - confirmed_benchmark_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - confirmed_precommit_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - confirmed_proof_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - confirmed_fraud_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - confirmed_topup_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - confirmed_wasm_ids: - type: array - items: - $ref: '#/components/schemas/AlgorithmId' - active_challenge_ids: - type: array - items: - $ref: '#/components/schemas/ChallengeId' - active_algorithm_ids: - type: array - items: - $ref: '#/components/schemas/AlgorithmId' - active_benchmark_ids: - type: array - items: - $ref: '#/components/schemas/MD5' - active_player_ids: - type: array - items: - $ref: '#/components/schemas/Address' + confirmed_ids: + type: object + properties: + algorithm: + type: array + items: + $ref: '#/components/schemas/AlgorithmId' + benchmark: + type: array + items: + $ref: '#/components/schemas/MD5' + binary: + type: array + items: + $ref: '#/components/schemas/AlgorithmId' + breakthrough: + type: array + items: + $ref: '#/components/schemas/BreakthroughId' + challenge: + type: array + items: + $ref: '#/components/schemas/ChallengeId' + deposit: + type: array + items: + $ref: '#/components/schemas/Address' + fraud: + type: array + items: + $ref: '#/components/schemas/Address' + precommit: + type: array + items: + $ref: '#/components/schemas/Address' + proof: + type: array + items: + $ref: '#/components/schemas/Address' + topup: + type: array + items: + $ref: '#/components/schemas/Address' + active_ids: + type: object + properties: + algorithm: + type: array + items: + $ref: '#/components/schemas/AlgorithmId' + benchmark: + type: array + items: + $ref: '#/components/schemas/MD5' + breakthrough: + type: array + items: + $ref: '#/components/schemas/BreakthroughId' + challenge: + type: array + items: + $ref: '#/components/schemas/ChallengeId' + deposit: + type: array + items: + $ref: '#/components/schemas/MD5' + opow: + type: array + items: + $ref: '#/components/schemas/Address' + player: + type: array + items: + $ref: '#/components/schemas/Address' + Breakthrough: + type: object + properties: + id: + $ref: '#/components/schemas/BreakthroughId' + details: + $ref: '#/components/schemas/BreakthroughDetails' + state: + $ref: '#/components/schemas/BreakthroughState' + block_data: + $ref: '#/components/schemas/BreakthroughBlockData' + BreakthroughBlockData: + type: object + properties: + adoption: + $ref: '#/components/schemas/PreciseNumber' + merge_points: + type: integer + format: uint32 + reward: + $ref: '#/components/schemas/PreciseNumber' + BreakthroughDetails: + type: object + properties: + name: + type: string + player_id: + $ref: '#/components/schemas/Address' + challenge_id: + $ref: '#/components/schemas/ChallengeId' + BreakthroughId: + type: string + pattern: ^c[0-9]{3}_b[0-9]{3}$ + example: c002_b001 + BreakthroughState: + type: object + properties: + block_confirmed: + type: integer + format: uint32 + round_submitted: + type: integer + format: uint32 + round_pushed: + type: integer + format: uint32 + round_active: + type: integer + format: uint32 + round_merged: + type: integer + format: uint32 + vote_tally: + type: object + properties: + true: + $ref: '#/components/schemas/PreciseNumber' + false: + $ref: '#/components/schemas/PreciseNumber' Challenge: type: object properties: @@ -632,9 +946,6 @@ components: ChallengeBlockData: type: object properties: - solution_signature_threshold: - type: integer - format: uint32 num_qualifiers: type: integer format: uint32 @@ -665,10 +976,40 @@ components: ChallengeState: type: object properties: - block_confirmed: + round_active: type: integer format: uint32 - round_active: + Deposit: + type: object + properties: + id: + $ref: '#/components/schemas/MD5' + details: + $ref: '#/components/schemas/DepositDetails' + state: + $ref: '#/components/schemas/DepositState' + DepositDetails: + type: object + properties: + player_id: + $ref: '#/components/schemas/Address' + tx_hash: + $ref: '#/components/schemas/TxHash' + log_idx: + type: integer + format: uint64 + amount: + $ref: '#/components/schemas/PreciseNumber' + start_timestamp: + type: integer + format: uint64 + end_timestamp: + type: integer + format: uint64 + DepositState: + type: object + properties: + block_confirmed: type: integer format: uint32 Difficulty: @@ -677,10 +1018,109 @@ components: type: integer format: int32 example: [40, 250] + DifficultyData: + type: object + properties: + num_nonces: + type: integer + format: uint64 + num_solutions: + type: integer + format: uint64 + difficulty: + type: array + items: + type: integer + format: uint32 + Fraud: + type: object + properties: + benchmark_id: + $ref: '#/components/schemas/MD5' + state: + $ref: '#/components/schemas/FraudState' + allegation: + type: string + FraudState: + type: object + properties: + block_confirmed: + type: integer + format: uint32 Frontier: type: array items: $ref: '#/components/schemas/Difficulty' + MD5: + type: string + pattern: ^[a-f0-9]{32}$ + MerkleBranch: + type: string + pattern: ^([a-f0-9]{66}){0,32}$ + MerkleHash: + type: string + pattern: ^[a-f0-9]{64}$ + MerkleProof: + type: object + properties: + leaf: + $ref: '#/components/schemas/OutputData' + branch: + $ref: '#/components/schemas/MerkleBranch' + OPoW: + type: object + properties: + player_id: + $ref: '#/components/schemas/Address' + block_data: + $ref: '#/components/schemas/OPoWBlockData' + OPoWBlockData: + type: object + properties: + num_qualifiers_by_challenge: + type: object + additionalProperties: + type: integer + format: uint32 + example: {"c001": 5, "c002": 3} + cutoff: + type: integer + format: uint32 + associated_deposit: + $ref: '#/components/schemas/PreciseNumber' + delegators: + type: array + items: + $ref: '#/components/schemas/Address' + reward_share: + type: number + format: double + imbalance: + $ref: '#/components/schemas/PreciseNumber' + influence: + $ref: '#/components/schemas/PreciseNumber' + reward: + $ref: '#/components/schemas/PreciseNumber' + OutputData: + type: object + properties: + nonce: + type: integer + format: uint64 + runtime_signature: + type: array + items: + type: array + items: + type: integer + format: uint64 + fuel_consumed: + type: integer + format: uint64 + solution: + type: object + additionalProperties: + type: string Player: type: object properties: @@ -695,33 +1135,25 @@ components: PlayerBlockData: type: object properties: - num_qualifiers_by_challenge: + delegatee: + type: string + nullable: true + reward_by_type: type: object - additionalProperties: - type: integer - format: uint32 - example: - c001: 10 - c002: 15 - c003: 20 - cutoff: - type: integer - format: uint32 - deposit: - $ref: '#/components/schemas/PreciseNumber' - rolling_deposit: - $ref: '#/components/schemas/PreciseNumber' - qualifying_percent_rolling_deposit: - $ref: '#/components/schemas/PreciseNumber' - imbalance: - $ref: '#/components/schemas/PreciseNumber' - imbalance_penalty: - $ref: '#/components/schemas/PreciseNumber' - influence: - $ref: '#/components/schemas/PreciseNumber' - reward: - $ref: '#/components/schemas/PreciseNumber' - round_earnings: + properties: + algorithm: + $ref: '#/components/schemas/PreciseNumber' + benchmarker: + $ref: '#/components/schemas/PreciseNumber' + breakthrough: + $ref: '#/components/schemas/PreciseNumber' + delegator: + $ref: '#/components/schemas/PreciseNumber' + deposit_by_locked_period: + type: array + items: + $ref: '#/components/schemas/PreciseNumber' + weighted_deposit: $ref: '#/components/schemas/PreciseNumber' PlayerDetails: type: object @@ -737,9 +1169,33 @@ components: $ref: '#/components/schemas/PreciseNumber' available_fee_balance: $ref: '#/components/schemas/PreciseNumber' + delegatee: + $ref: '#/components/schemas/PlayerValueDelegatee' + votes: + type: object + additionalProperties: + type: object + properties: + value: + type: boolean + block_set: + type: integer + format: uint32 + reward_share: + type: number + format: double + PlayerValueDelegatee: + type: object + properties: + value: + $ref: '#/components/schemas/Address' + block_set: + type: integer + format: uint32 PreciseNumber: - type: integer - format: uint256 + type: string + pattern: ^[0-9]+$ + example: "123456789123456789" Precommit: type: object properties: @@ -760,6 +1216,8 @@ components: num_nonces: type: integer format: uint32 + rand_hash: + $ref: '#/components/schemas/MD5' fee_paid: $ref: '#/components/schemas/PreciseNumber' PrecommitState: @@ -768,64 +1226,37 @@ components: block_confirmed: type: integer format: uint32 - rand_hash: - type: string - MerkleProof: - type: object - properties: - leaf: - $ref: '#/components/schemas/OutputData' - branch: - $ref: '#/components/schemas/MerkleBranch' Proof: type: object properties: benchmark_id: $ref: '#/components/schemas/MD5' + details: + $ref: '#/components/schemas/ProofDetails' state: $ref: '#/components/schemas/ProofState' merkle_proofs: type: array items: $ref: '#/components/schemas/MerkleProof' + ProofDetails: + type: object + properties: + submission_delay: + type: integer + format: uint32 ProofState: type: object properties: block_confirmed: type: integer format: uint32 - submission_delay: - type: integer - format: uint32 - OutputData: - type: object - properties: - nonce: - type: integer - format: uint64 - runtime_signature: - type: integer - format: uint64 - fuel_consumed: - type: integer - format: uint64 - solution: - type: object - Fraud: - type: object - properties: - benchmark_id: - $ref: '#/components/schemas/MD5' - state: - $ref: '#/components/schemas/FraudState' - allegation: - type: string - FraudState: - type: object - properties: - block_confirmed: + block_active: type: integer format: uint32 + Signature: + type: string + pattern: ^0x([a-f0-9]{130})+$ TopUp: type: object properties: @@ -840,6 +1271,11 @@ components: properties: player_id: $ref: '#/components/schemas/Address' + tx_hash: + $ref: '#/components/schemas/TxHash' + log_idx: + type: integer + format: uint64 amount: $ref: '#/components/schemas/PreciseNumber' TopUpState: @@ -848,56 +1284,28 @@ components: block_confirmed: type: integer format: uint32 - Wasm: - type: object - properties: - algorithm_id: - $ref: '#/components/schemas/MD5' - details: - $ref: '#/components/schemas/WasmDetails' - state: - $ref: '#/components/schemas/WasmState' - WasmDetails: - type: object - properties: - compile_success: - type: boolean - download_url: - type: string - WasmState: - type: object - properties: - block_confirmed: - type: integer - format: uint32 - MD5: - type: string - pattern: ^[a-f0-9]{32}$ - Signature: - type: string - pattern: ^0x([a-f0-9]{130})+$ TxHash: type: string pattern: ^0x[a-f0-9]{64}$ - MerkleHash: - type: string - pattern: ^[a-f0-9]{64}$ - MerkleBranch: - type: string - pattern: ^([a-f0-9]{66}){0,32}$ - ProtocolConfig: - type: object GetAlgorithmsResponse: type: object properties: - block_id: - $ref: '#/components/schemas/MD5' - block_details: - $ref: '#/components/schemas/BlockDetails' algorithms: type: array items: $ref: '#/components/schemas/Algorithm' + breakthroughs: + type: array + items: + $ref: '#/components/schemas/Breakthrough' + binaries: + type: array + items: + $ref: '#/components/schemas/Binary' + names: + type: object + additionalProperties: + type: string GetBenchmarksResponse: type: object properties: @@ -936,41 +1344,77 @@ components: GetChallengesResponse: type: object properties: - block_id: - $ref: '#/components/schemas/MD5' - block_details: - $ref: '#/components/schemas/BlockDetails' challenges: type: array items: $ref: '#/components/schemas/Challenge' - GetPlayersResponse: + GetDifficultyDataResponse: type: object properties: - block_id: - $ref: '#/components/schemas/MD5' - block_details: - $ref: '#/components/schemas/BlockDetails' - players: + difficulty_data: type: array items: - $ref: '#/components/schemas/Player' - GetFeeBalanceResponse: + $ref: '#/components/schemas/DifficultyData' + GetOPoWResponse: type: object properties: - state: - $ref: '#/components/schemas/PlayerState' + opow: + type: array + items: + $ref: '#/components/schemas/OPoW' + names: + type: object + additionalProperties: + type: string + GetPlayerDataResponse: + type: object + properties: + player: + $ref: '#/components/schemas/Player' + deposits: + type: array + items: + $ref: '#/components/schemas/Deposit' topups: type: array items: $ref: '#/components/schemas/TopUp' + GetRoundEarningsResponse: + type: object + properties: + algorithms: + type: object + additionalProperties: + $ref: '#/components/schemas/PreciseNumber' + opow: + type: object + additionalProperties: + $ref: '#/components/schemas/PreciseNumber' + players: + type: object + additionalProperties: + type: object + properties: + algorithm: + $ref: '#/components/schemas/PreciseNumber' + benchmarker: + $ref: '#/components/schemas/PreciseNumber' + breakthrough: + $ref: '#/components/schemas/PreciseNumber' + delegator: + $ref: '#/components/schemas/PreciseNumber' + names: + type: object + additionalProperties: + type: string + RequestApiKeyRequest: type: object properties: - address: - $ref: '#/components/schemas/Address' signature: $ref: '#/components/schemas/Signature' + address: + $ref: '#/components/schemas/Address' RequestApiKeyResponse: type: object properties: @@ -978,6 +1422,29 @@ components: $ref: '#/components/schemas/MD5' name: type: string + SetDelegateesRequest: + type: object + properties: + delegatees: + type: object + additionalProperties: + type: float + SetDelegateeResponse: + type: object + properties: + ok: + type: boolean + SetRewardShareRequest: + type: object + properties: + reward_share: + type: number + format: double + SetRewardShareResponse: + type: object + properties: + ok: + type: boolean SubmitAlgorithmRequest: type: object properties: @@ -985,22 +1452,12 @@ components: $ref: '#/components/schemas/AlgorithmName' challenge_id: $ref: '#/components/schemas/ChallengeId' - tx_hash: - $ref: '#/components/schemas/TxHash' code: type: string - SubmitPrecommitRequest: + SubmitAlgorithmResponse: type: object properties: - settings: - $ref: '#/components/schemas/BenchmarkSettings' - num_nonces: - type: integer - format: uint32 - SubmitPrecommitResponse: - type: object - properties: - benchmark_id: + algorithm_id: $ref: '#/components/schemas/MD5' SubmitBenchmarkRequest: type: object @@ -1019,6 +1476,32 @@ components: properties: ok: type: boolean + SubmitDepositRequest: + type: object + properties: + tx_hash: + $ref: '#/components/schemas/TxHash' + log_idx: + type: integer + format: uint32 + SubmitDepositResponse: + type: object + properties: + deposit_id: + $ref: '#/components/schemas/MD5' + SubmitPrecommitRequest: + type: object + properties: + settings: + $ref: '#/components/schemas/BenchmarkSettings' + num_nonces: + type: integer + format: uint32 + SubmitPrecommitResponse: + type: object + properties: + benchmark_id: + $ref: '#/components/schemas/MD5' SubmitProofRequest: type: object properties: @@ -1038,8 +1521,11 @@ components: properties: tx_hash: $ref: '#/components/schemas/TxHash' + log_idx: + type: integer + format: uint32 SubmitTopupResponse: type: object properties: - ok: - type: boolean \ No newline at end of file + topup_id: + $ref: '#/components/schemas/MD5' \ No newline at end of file diff --git a/tig-algorithms/README.md b/tig-algorithms/README.md index d20bd6e..ec0e769 100644 --- a/tig-algorithms/README.md +++ b/tig-algorithms/README.md @@ -20,10 +20,11 @@ WASM blobs for an algorithm are stored in the `wasm` subfolder and can be downlo 1. New submissions get their branch pushed to a private version of this repository 2. CI will compile submissions into WASM -3. A new submission made during round `X` will have its branch pushed to the public version of this repository at the start of round `X + 3` -4. Once public, benchmarkers can use the algorithm for benchmarking -5. Every block, algorithms with at least 25% adoption earn a merge point -6. At the end of a round, a the algorithm from each challenge with the most merge points, meeting the minimum threshold of 5040, gets merged to the `main` branch +3. A new submission made during round `X` will have its branch pushed to the public version of this repository at the start of round `X + 2` +4. At the start of round `X + 4`, the submission becomes active, where benchmarkers can use the algorithm for benchmarking +5. Every block, algorithms with at least 25% adoption earn rewards and a merge point +6. At the end of a round, a algorithm from each challenge with the most merge points, meeting the minimum threshold of 5040, gets merged to the `main` branch + * Merged algorithms receive rewards every block where their adoption is greater than 0% # License diff --git a/tig-algorithms/src/knapsack/knapheudp/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/knapheudp/benchmarker_outbound.rs new file mode 100644 index 0000000..863a240 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapheudp/benchmarker_outbound.rs @@ -0,0 +1,110 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); + let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let mut dp = vec![0; max_weight + 1]; + let mut selected = vec![vec![false; max_weight + 1]; num_items]; + + for (i, &(item_index, _)) in sorted_items.iter().enumerate() { + let weight = weights[item_index]; + let value = values[item_index]; + + for w in (weight..=max_weight).rev() { + let new_value = dp[w - weight] + value; + if new_value > dp[w] { + dp[w] = new_value; + selected[i][w] = true; + } + } + + if dp[max_weight] >= min_value { + break; + } + } + + if dp[max_weight] < min_value { + return Ok(None); + } + + let mut items = Vec::new(); + let mut w = max_weight; + for i in (0..num_items).rev() { + if selected[i][w] { + let item_index = sorted_items[i].0; + items.push(item_index); + w -= weights[item_index]; + } + if w == 0 { + break; + } + } + + Ok(Some(Solution { items })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapheudp/commercial.rs b/tig-algorithms/src/knapsack/knapheudp/commercial.rs new file mode 100644 index 0000000..49af535 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapheudp/commercial.rs @@ -0,0 +1,110 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); + let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let mut dp = vec![0; max_weight + 1]; + let mut selected = vec![vec![false; max_weight + 1]; num_items]; + + for (i, &(item_index, _)) in sorted_items.iter().enumerate() { + let weight = weights[item_index]; + let value = values[item_index]; + + for w in (weight..=max_weight).rev() { + let new_value = dp[w - weight] + value; + if new_value > dp[w] { + dp[w] = new_value; + selected[i][w] = true; + } + } + + if dp[max_weight] >= min_value { + break; + } + } + + if dp[max_weight] < min_value { + return Ok(None); + } + + let mut items = Vec::new(); + let mut w = max_weight; + for i in (0..num_items).rev() { + if selected[i][w] { + let item_index = sorted_items[i].0; + items.push(item_index); + w -= weights[item_index]; + } + if w == 0 { + break; + } + } + + Ok(Some(Solution { items })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapheudp/inbound.rs b/tig-algorithms/src/knapsack/knapheudp/inbound.rs new file mode 100644 index 0000000..7a516c2 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapheudp/inbound.rs @@ -0,0 +1,110 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); + let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let mut dp = vec![0; max_weight + 1]; + let mut selected = vec![vec![false; max_weight + 1]; num_items]; + + for (i, &(item_index, _)) in sorted_items.iter().enumerate() { + let weight = weights[item_index]; + let value = values[item_index]; + + for w in (weight..=max_weight).rev() { + let new_value = dp[w - weight] + value; + if new_value > dp[w] { + dp[w] = new_value; + selected[i][w] = true; + } + } + + if dp[max_weight] >= min_value { + break; + } + } + + if dp[max_weight] < min_value { + return Ok(None); + } + + let mut items = Vec::new(); + let mut w = max_weight; + for i in (0..num_items).rev() { + if selected[i][w] { + let item_index = sorted_items[i].0; + items.push(item_index); + w -= weights[item_index]; + } + if w == 0 { + break; + } + } + + Ok(Some(Solution { items })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapheudp/innovator_outbound.rs b/tig-algorithms/src/knapsack/knapheudp/innovator_outbound.rs new file mode 100644 index 0000000..927bd2b --- /dev/null +++ b/tig-algorithms/src/knapsack/knapheudp/innovator_outbound.rs @@ -0,0 +1,110 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); + let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let mut dp = vec![0; max_weight + 1]; + let mut selected = vec![vec![false; max_weight + 1]; num_items]; + + for (i, &(item_index, _)) in sorted_items.iter().enumerate() { + let weight = weights[item_index]; + let value = values[item_index]; + + for w in (weight..=max_weight).rev() { + let new_value = dp[w - weight] + value; + if new_value > dp[w] { + dp[w] = new_value; + selected[i][w] = true; + } + } + + if dp[max_weight] >= min_value { + break; + } + } + + if dp[max_weight] < min_value { + return Ok(None); + } + + let mut items = Vec::new(); + let mut w = max_weight; + for i in (0..num_items).rev() { + if selected[i][w] { + let item_index = sorted_items[i].0; + items.push(item_index); + w -= weights[item_index]; + } + if w == 0 { + break; + } + } + + Ok(Some(Solution { items })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/basic/mod.rs b/tig-algorithms/src/knapsack/knapheudp/mod.rs similarity index 100% rename from tig-algorithms/src/vector_search/basic/mod.rs rename to tig-algorithms/src/knapsack/knapheudp/mod.rs diff --git a/tig-algorithms/src/knapsack/knapheudp/open_data.rs b/tig-algorithms/src/knapsack/knapheudp/open_data.rs new file mode 100644 index 0000000..f9a8ce3 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapheudp/open_data.rs @@ -0,0 +1,110 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let weights: Vec = challenge.weights.iter().map(|&w| w as usize).collect(); + let values: Vec = challenge.values.iter().map(|&v| v as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let mut dp = vec![0; max_weight + 1]; + let mut selected = vec![vec![false; max_weight + 1]; num_items]; + + for (i, &(item_index, _)) in sorted_items.iter().enumerate() { + let weight = weights[item_index]; + let value = values[item_index]; + + for w in (weight..=max_weight).rev() { + let new_value = dp[w - weight] + value; + if new_value > dp[w] { + dp[w] = new_value; + selected[i][w] = true; + } + } + + if dp[max_weight] >= min_value { + break; + } + } + + if dp[max_weight] < min_value { + return Ok(None); + } + + let mut items = Vec::new(); + let mut w = max_weight; + for i in (0..num_items).rev() { + if selected[i][w] { + let item_index = sorted_items[i].0; + items.push(item_index); + w -= weights[item_index]; + } + if w == 0 { + break; + } + } + + Ok(Some(Solution { items })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapmaxxing/benchmarker_outbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/benchmarker_outbound.rs new file mode 100644 index 0000000..86f9f7f --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/benchmarker_outbound.rs @@ -0,0 +1,115 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let max_weight_plus_one = max_weight + 1; + + let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); + let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let num_states = (num_items + 1) * (max_weight_plus_one); + let mut dp = vec![0; num_states]; + + for i in 1..=num_items { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; + let i_times_max_weight_plus_one = i * max_weight_plus_one; + for w in (item_weight..=max_weight).rev() { + let prev_state = i_minus_one_times_max_weight_plus_one + w; + let curr_state = i_times_max_weight_plus_one + w; + dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); + } + } + + let mut items = Vec::with_capacity(num_items); + let mut i = num_items; + let mut w = max_weight; + let mut total_value = 0; + while i > 0 && total_value < min_value { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let prev_state = (i - 1) * (max_weight_plus_one) + w; + let curr_state = i * (max_weight_plus_one) + w; + if dp[curr_state] != dp[prev_state] { + items.push(item_index); + w -= item_weight; + total_value += item_value; + } + i -= 1; + } + + if total_value >= min_value { + Ok(Some(Solution { items })) + } else { + Ok(None) + } +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapmaxxing/commercial.rs b/tig-algorithms/src/knapsack/knapmaxxing/commercial.rs new file mode 100644 index 0000000..9b4962b --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/commercial.rs @@ -0,0 +1,115 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let max_weight_plus_one = max_weight + 1; + + let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); + let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let num_states = (num_items + 1) * (max_weight_plus_one); + let mut dp = vec![0; num_states]; + + for i in 1..=num_items { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; + let i_times_max_weight_plus_one = i * max_weight_plus_one; + for w in (item_weight..=max_weight).rev() { + let prev_state = i_minus_one_times_max_weight_plus_one + w; + let curr_state = i_times_max_weight_plus_one + w; + dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); + } + } + + let mut items = Vec::with_capacity(num_items); + let mut i = num_items; + let mut w = max_weight; + let mut total_value = 0; + while i > 0 && total_value < min_value { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let prev_state = (i - 1) * (max_weight_plus_one) + w; + let curr_state = i * (max_weight_plus_one) + w; + if dp[curr_state] != dp[prev_state] { + items.push(item_index); + w -= item_weight; + total_value += item_value; + } + i -= 1; + } + + if total_value >= min_value { + Ok(Some(Solution { items })) + } else { + Ok(None) + } +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapmaxxing/inbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/inbound.rs new file mode 100644 index 0000000..7fd304a --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/inbound.rs @@ -0,0 +1,115 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let max_weight_plus_one = max_weight + 1; + + let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); + let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let num_states = (num_items + 1) * (max_weight_plus_one); + let mut dp = vec![0; num_states]; + + for i in 1..=num_items { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; + let i_times_max_weight_plus_one = i * max_weight_plus_one; + for w in (item_weight..=max_weight).rev() { + let prev_state = i_minus_one_times_max_weight_plus_one + w; + let curr_state = i_times_max_weight_plus_one + w; + dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); + } + } + + let mut items = Vec::with_capacity(num_items); + let mut i = num_items; + let mut w = max_weight; + let mut total_value = 0; + while i > 0 && total_value < min_value { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let prev_state = (i - 1) * (max_weight_plus_one) + w; + let curr_state = i * (max_weight_plus_one) + w; + if dp[curr_state] != dp[prev_state] { + items.push(item_index); + w -= item_weight; + total_value += item_value; + } + i -= 1; + } + + if total_value >= min_value { + Ok(Some(Solution { items })) + } else { + Ok(None) + } +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapmaxxing/innovator_outbound.rs b/tig-algorithms/src/knapsack/knapmaxxing/innovator_outbound.rs new file mode 100644 index 0000000..a730bb6 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/innovator_outbound.rs @@ -0,0 +1,115 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let max_weight_plus_one = max_weight + 1; + + let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); + let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let num_states = (num_items + 1) * (max_weight_plus_one); + let mut dp = vec![0; num_states]; + + for i in 1..=num_items { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; + let i_times_max_weight_plus_one = i * max_weight_plus_one; + for w in (item_weight..=max_weight).rev() { + let prev_state = i_minus_one_times_max_weight_plus_one + w; + let curr_state = i_times_max_weight_plus_one + w; + dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); + } + } + + let mut items = Vec::with_capacity(num_items); + let mut i = num_items; + let mut w = max_weight; + let mut total_value = 0; + while i > 0 && total_value < min_value { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let prev_state = (i - 1) * (max_weight_plus_one) + w; + let curr_state = i * (max_weight_plus_one) + w; + if dp[curr_state] != dp[prev_state] { + items.push(item_index); + w -= item_weight; + total_value += item_value; + } + i -= 1; + } + + if total_value >= min_value { + Ok(Some(Solution { items })) + } else { + Ok(None) + } +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/knapmaxxing/mod.rs b/tig-algorithms/src/knapsack/knapmaxxing/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs b/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs new file mode 100644 index 0000000..e2849d9 --- /dev/null +++ b/tig-algorithms/src/knapsack/knapmaxxing/open_data.rs @@ -0,0 +1,115 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::knapsack::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let max_weight = challenge.max_weight as usize; + let min_value = challenge.min_value as usize; + let num_items = challenge.difficulty.num_items; + + let max_weight_plus_one = max_weight + 1; + + let weights: Vec = challenge.weights.iter().map(|weight| *weight as usize).collect(); + let values: Vec = challenge.values.iter().map(|value| *value as usize).collect(); + + let mut sorted_items: Vec<(usize, f64)> = (0..num_items) + .map(|i| (i, values[i] as f64 / weights[i] as f64)) + .collect(); + sorted_items.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); + + let mut upper_bound = 0; + let mut remaining_weight = max_weight; + for &(item_index, ratio) in &sorted_items { + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + if item_weight <= remaining_weight { + upper_bound += item_value; + remaining_weight -= item_weight; + } else { + upper_bound += (ratio * remaining_weight as f64).floor() as usize; + break; + } + } + + if upper_bound < min_value { + return Ok(None); + } + + let num_states = (num_items + 1) * (max_weight_plus_one); + let mut dp = vec![0; num_states]; + + for i in 1..=num_items { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let i_minus_one_times_max_weight_plus_one = (i - 1) * max_weight_plus_one; + let i_times_max_weight_plus_one = i * max_weight_plus_one; + for w in (item_weight..=max_weight).rev() { + let prev_state = i_minus_one_times_max_weight_plus_one + w; + let curr_state = i_times_max_weight_plus_one + w; + dp[curr_state] = dp[prev_state].max(dp[prev_state - item_weight] + item_value); + } + } + + let mut items = Vec::with_capacity(num_items); + let mut i = num_items; + let mut w = max_weight; + let mut total_value = 0; + while i > 0 && total_value < min_value { + let (item_index, _) = sorted_items[i - 1]; + let item_weight = weights[item_index]; + let item_value = values[item_index]; + + let prev_state = (i - 1) * (max_weight_plus_one) + w; + let curr_state = i * (max_weight_plus_one) + w; + if dp[curr_state] != dp[prev_state] { + items.push(item_index); + w -= item_weight; + total_value += item_value; + } + i -= 1; + } + + if total_value >= min_value { + Ok(Some(Solution { items })) + } else { + Ok(None) + } +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/knapsack/mod.rs b/tig-algorithms/src/knapsack/mod.rs index 384121e..edf17b5 100644 --- a/tig-algorithms/src/knapsack/mod.rs +++ b/tig-algorithms/src/knapsack/mod.rs @@ -11,7 +11,8 @@ pub use dynamic as c003_a001; // c003_a006 -// c003_a007 +pub mod knapmaxxing; +pub use knapmaxxing as c003_a007; // c003_a008 @@ -35,7 +36,8 @@ pub use dynamic as c003_a001; // c003_a018 -// c003_a019 +pub mod knapheudp; +pub use knapheudp as c003_a019; // c003_a020 diff --git a/tig-algorithms/src/knapsack/template.rs b/tig-algorithms/src/knapsack/template.rs index dab7384..6386cbf 100644 --- a/tig-algorithms/src/knapsack/template.rs +++ b/tig-algorithms/src/knapsack/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::knapsack::{Challenge, Solution}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs new file mode 100644 index 0000000..dceb0e8 --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/benchmarker_outbound.rs @@ -0,0 +1,250 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashSet; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual = HashSet::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual.insert(i); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if let Some(&i) = residual.iter().next() { + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + for &c in &n_clauses[v] { + if variables[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } else { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } + } + for &c in &p_clauses[v] { + if variables[v] { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } else { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/commercial.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/commercial.rs new file mode 100644 index 0000000..a71cfb8 --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/commercial.rs @@ -0,0 +1,250 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashSet; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual = HashSet::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual.insert(i); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if let Some(&i) = residual.iter().next() { + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + for &c in &n_clauses[v] { + if variables[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } else { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } + } + for &c in &p_clauses[v] { + if variables[v] { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } else { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/inbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/inbound.rs new file mode 100644 index 0000000..f8bed23 --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/inbound.rs @@ -0,0 +1,250 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashSet; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual = HashSet::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual.insert(i); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if let Some(&i) = residual.iter().next() { + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + for &c in &n_clauses[v] { + if variables[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } else { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } + } + for &c in &p_clauses[v] { + if variables[v] { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } else { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/innovator_outbound.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/innovator_outbound.rs new file mode 100644 index 0000000..560c2eb --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/innovator_outbound.rs @@ -0,0 +1,250 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashSet; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual = HashSet::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual.insert(i); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if let Some(&i) = residual.iter().next() { + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + for &c in &n_clauses[v] { + if variables[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } else { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } + } + for &c in &p_clauses[v] { + if variables[v] { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } else { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/mod.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs b/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs new file mode 100644 index 0000000..c68797e --- /dev/null +++ b/tig-algorithms/src/satisfiability/fast_walk_sat/open_data.rs @@ -0,0 +1,250 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashSet; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual = HashSet::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual.insert(i); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if let Some(&i) = residual.iter().next() { + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + for &c in &n_clauses[v] { + if variables[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } else { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } + } + for &c in &p_clauses[v] { + if variables[v] { + if num_good_so_far[c] == 1 { + residual.insert(c); + } + num_good_so_far[c] -= 1; + } else { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + residual.remove(&c); + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/inbound/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/inbound/benchmarker_outbound.rs new file mode 100644 index 0000000..b08ce0d --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/benchmarker_outbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Clifford Algueraz + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/inbound/commercial.rs b/tig-algorithms/src/satisfiability/inbound/commercial.rs new file mode 100644 index 0000000..b8c9432 --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/commercial.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Clifford Algueraz + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/inbound/inbound.rs b/tig-algorithms/src/satisfiability/inbound/inbound.rs new file mode 100644 index 0000000..72ae4d8 --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/inbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Clifford Algueraz + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/inbound/innovator_outbound.rs b/tig-algorithms/src/satisfiability/inbound/innovator_outbound.rs new file mode 100644 index 0000000..ca5c0a3 --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/innovator_outbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Clifford Algueraz + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/inbound/mod.rs b/tig-algorithms/src/satisfiability/inbound/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/inbound/open_data.rs b/tig-algorithms/src/satisfiability/inbound/open_data.rs new file mode 100644 index 0000000..75d7647 --- /dev/null +++ b/tig-algorithms/src/satisfiability/inbound/open_data.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Clifford Algueraz + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/mod.rs b/tig-algorithms/src/satisfiability/mod.rs index e6bcd84..a19e4e1 100644 --- a/tig-algorithms/src/satisfiability/mod.rs +++ b/tig-algorithms/src/satisfiability/mod.rs @@ -7,7 +7,8 @@ pub use schnoing as c001_a001; // c001_a004 -// c001_a005 +pub mod walk_sat; +pub use walk_sat as c001_a005; // c001_a006 @@ -19,9 +20,11 @@ pub use schnoing as c001_a001; // c001_a010 -// c001_a011 +pub mod fast_walk_sat; +pub use fast_walk_sat as c001_a011; -// c001_a012 +pub mod sprint_sat; +pub use sprint_sat as c001_a012; // c001_a013 @@ -33,7 +36,8 @@ pub use schnoing as c001_a001; // c001_a017 -// c001_a018 +pub mod inbound; +pub use inbound as c001_a018; // c001_a019 @@ -43,7 +47,8 @@ pub use schnoing as c001_a001; // c001_a022 -// c001_a023 +pub mod sat_allocd; +pub use sat_allocd as c001_a023; // c001_a024 @@ -59,13 +64,15 @@ pub use schnoing as c001_a001; // c001_a030 -// c001_a031 +pub mod sat_optima; +pub use sat_optima as c001_a031; // c001_a032 // c001_a033 -// c001_a034 +pub mod sat_global; +pub use sat_global as c001_a034; // c001_a035 diff --git a/tig-algorithms/src/satisfiability/sat_allocd/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/benchmarker_outbound.rs new file mode 100644 index 0000000..2e6758f --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/benchmarker_outbound.rs @@ -0,0 +1,284 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_allocd/commercial.rs b/tig-algorithms/src/satisfiability/sat_allocd/commercial.rs new file mode 100644 index 0000000..53644aa --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/commercial.rs @@ -0,0 +1,284 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_allocd/inbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/inbound.rs new file mode 100644 index 0000000..8cfe0c9 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/inbound.rs @@ -0,0 +1,284 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_allocd/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_allocd/innovator_outbound.rs new file mode 100644 index 0000000..6452120 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/innovator_outbound.rs @@ -0,0 +1,284 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_allocd/mod.rs b/tig-algorithms/src/satisfiability/sat_allocd/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs b/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs new file mode 100644 index 0000000..5dc5c06 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_allocd/open_data.rs @@ -0,0 +1,284 @@ +/*! +Copyright 2024 AllFather + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_global/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_global/benchmarker_outbound.rs new file mode 100644 index 0000000..03d219f --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/benchmarker_outbound.rs @@ -0,0 +1,297 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + + + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + } else { + n_clauses[var].push(i); + } + } + } + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + let num_p = p_clauses[v].len(); + let num_n = n_clauses[v].len(); + + let vad = num_p as f32 / (num_p + num_n).max(1) as f32; + + if vad >= 1.8 { + variables[v] = true; + } else if vad <= 0.56 { + variables[v] = false; + } else { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + } + + let mut num_good_so_far: Vec = vec![0; num_clauses]; + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 && variables[var] { + num_good_so_far[i] += 1 + } else if l < 0 && !variables[var] { + num_good_so_far[i] += 1 + } + } + } + + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; + let num_vars = challenge.difficulty.num_variables as f64; + let max_fuel = 2000000000.0; + let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; + let flip_fuel = 900.0 + 1.8 * clauses_ratio; + let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; + loop { + if !residual_.is_empty() { + + let rand_val = rng.gen::(); + + let i = residual_[rand_val % residual_.len()]; + let mut min_sad = clauses.len(); + let mut v_min_sad = usize::MAX; + let c = &mut clauses[i]; + + if c.len() > 1 { + let random_index = rand_val % c.len(); + c.swap(0, random_index); + } + for &l in c.iter() { + let abs_l = l.abs() as usize - 1; + let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; + + let mut sad = 0; + for &c in clauses_to_check { + if num_good_so_far[c] == 1 { + sad += 1; + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = abs_l; + } + } + + let v = if min_sad == 0 { + v_min_sad + } else if rng.gen_bool(0.5) { + c[0].abs() as usize - 1 + } else { + v_min_sad + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= max_num_rounds { + return Ok(None); + } + } + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_global/commercial.rs b/tig-algorithms/src/satisfiability/sat_global/commercial.rs new file mode 100644 index 0000000..d026725 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/commercial.rs @@ -0,0 +1,297 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + + + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + } else { + n_clauses[var].push(i); + } + } + } + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + let num_p = p_clauses[v].len(); + let num_n = n_clauses[v].len(); + + let vad = num_p as f32 / (num_p + num_n).max(1) as f32; + + if vad >= 1.8 { + variables[v] = true; + } else if vad <= 0.56 { + variables[v] = false; + } else { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + } + + let mut num_good_so_far: Vec = vec![0; num_clauses]; + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 && variables[var] { + num_good_so_far[i] += 1 + } else if l < 0 && !variables[var] { + num_good_so_far[i] += 1 + } + } + } + + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; + let num_vars = challenge.difficulty.num_variables as f64; + let max_fuel = 2000000000.0; + let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; + let flip_fuel = 900.0 + 1.8 * clauses_ratio; + let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; + loop { + if !residual_.is_empty() { + + let rand_val = rng.gen::(); + + let i = residual_[rand_val % residual_.len()]; + let mut min_sad = clauses.len(); + let mut v_min_sad = usize::MAX; + let c = &mut clauses[i]; + + if c.len() > 1 { + let random_index = rand_val % c.len(); + c.swap(0, random_index); + } + for &l in c.iter() { + let abs_l = l.abs() as usize - 1; + let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; + + let mut sad = 0; + for &c in clauses_to_check { + if num_good_so_far[c] == 1 { + sad += 1; + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = abs_l; + } + } + + let v = if min_sad == 0 { + v_min_sad + } else if rng.gen_bool(0.5) { + c[0].abs() as usize - 1 + } else { + v_min_sad + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= max_num_rounds { + return Ok(None); + } + } + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_global/inbound.rs b/tig-algorithms/src/satisfiability/sat_global/inbound.rs new file mode 100644 index 0000000..fd50847 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/inbound.rs @@ -0,0 +1,297 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + + + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + } else { + n_clauses[var].push(i); + } + } + } + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + let num_p = p_clauses[v].len(); + let num_n = n_clauses[v].len(); + + let vad = num_p as f32 / (num_p + num_n).max(1) as f32; + + if vad >= 1.8 { + variables[v] = true; + } else if vad <= 0.56 { + variables[v] = false; + } else { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + } + + let mut num_good_so_far: Vec = vec![0; num_clauses]; + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 && variables[var] { + num_good_so_far[i] += 1 + } else if l < 0 && !variables[var] { + num_good_so_far[i] += 1 + } + } + } + + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; + let num_vars = challenge.difficulty.num_variables as f64; + let max_fuel = 2000000000.0; + let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; + let flip_fuel = 900.0 + 1.8 * clauses_ratio; + let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; + loop { + if !residual_.is_empty() { + + let rand_val = rng.gen::(); + + let i = residual_[rand_val % residual_.len()]; + let mut min_sad = clauses.len(); + let mut v_min_sad = usize::MAX; + let c = &mut clauses[i]; + + if c.len() > 1 { + let random_index = rand_val % c.len(); + c.swap(0, random_index); + } + for &l in c.iter() { + let abs_l = l.abs() as usize - 1; + let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; + + let mut sad = 0; + for &c in clauses_to_check { + if num_good_so_far[c] == 1 { + sad += 1; + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = abs_l; + } + } + + let v = if min_sad == 0 { + v_min_sad + } else if rng.gen_bool(0.5) { + c[0].abs() as usize - 1 + } else { + v_min_sad + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= max_num_rounds { + return Ok(None); + } + } + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_global/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_global/innovator_outbound.rs new file mode 100644 index 0000000..80b6edc --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/innovator_outbound.rs @@ -0,0 +1,297 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + + + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + } else { + n_clauses[var].push(i); + } + } + } + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + let num_p = p_clauses[v].len(); + let num_n = n_clauses[v].len(); + + let vad = num_p as f32 / (num_p + num_n).max(1) as f32; + + if vad >= 1.8 { + variables[v] = true; + } else if vad <= 0.56 { + variables[v] = false; + } else { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + } + + let mut num_good_so_far: Vec = vec![0; num_clauses]; + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 && variables[var] { + num_good_so_far[i] += 1 + } else if l < 0 && !variables[var] { + num_good_so_far[i] += 1 + } + } + } + + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; + let num_vars = challenge.difficulty.num_variables as f64; + let max_fuel = 2000000000.0; + let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; + let flip_fuel = 900.0 + 1.8 * clauses_ratio; + let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; + loop { + if !residual_.is_empty() { + + let rand_val = rng.gen::(); + + let i = residual_[rand_val % residual_.len()]; + let mut min_sad = clauses.len(); + let mut v_min_sad = usize::MAX; + let c = &mut clauses[i]; + + if c.len() > 1 { + let random_index = rand_val % c.len(); + c.swap(0, random_index); + } + for &l in c.iter() { + let abs_l = l.abs() as usize - 1; + let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; + + let mut sad = 0; + for &c in clauses_to_check { + if num_good_so_far[c] == 1 { + sad += 1; + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = abs_l; + } + } + + let v = if min_sad == 0 { + v_min_sad + } else if rng.gen_bool(0.5) { + c[0].abs() as usize - 1 + } else { + v_min_sad + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= max_num_rounds { + return Ok(None); + } + } + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_global/mod.rs b/tig-algorithms/src/satisfiability/sat_global/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_global/open_data.rs b/tig-algorithms/src/satisfiability/sat_global/open_data.rs new file mode 100644 index 0000000..f1cbf28 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_global/open_data.rs @@ -0,0 +1,297 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + + + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + } else { + n_clauses[var].push(i); + } + } + } + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + let num_p = p_clauses[v].len(); + let num_n = n_clauses[v].len(); + + let vad = num_p as f32 / (num_p + num_n).max(1) as f32; + + if vad >= 1.8 { + variables[v] = true; + } else if vad <= 0.56 { + variables[v] = false; + } else { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + } + + let mut num_good_so_far: Vec = vec![0; num_clauses]; + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 && variables[var] { + num_good_so_far[i] += 1 + } else if l < 0 && !variables[var] { + num_good_so_far[i] += 1 + } + } + } + + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let clauses_ratio = challenge.difficulty.clauses_to_variables_percent as f64; + let num_vars = challenge.difficulty.num_variables as f64; + let max_fuel = 2000000000.0; + let base_fuel = (2000.0 + 40.0 * clauses_ratio) * num_vars; + let flip_fuel = 900.0 + 1.8 * clauses_ratio; + let max_num_rounds = ((max_fuel - base_fuel) / flip_fuel) as usize; + loop { + if !residual_.is_empty() { + + let rand_val = rng.gen::(); + + let i = residual_[rand_val % residual_.len()]; + let mut min_sad = clauses.len(); + let mut v_min_sad = usize::MAX; + let c = &mut clauses[i]; + + if c.len() > 1 { + let random_index = rand_val % c.len(); + c.swap(0, random_index); + } + for &l in c.iter() { + let abs_l = l.abs() as usize - 1; + let clauses_to_check = if variables[abs_l] { &p_clauses[abs_l] } else { &n_clauses[abs_l] }; + + let mut sad = 0; + for &c in clauses_to_check { + if num_good_so_far[c] == 1 { + sad += 1; + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = abs_l; + } + } + + let v = if min_sad == 0 { + v_min_sad + } else if rng.gen_bool(0.5) { + c[0].abs() as usize - 1 + } else { + v_min_sad + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= max_num_rounds { + return Ok(None); + } + } + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_optima/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sat_optima/benchmarker_outbound.rs new file mode 100644 index 0000000..6a20e5b --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/benchmarker_outbound.rs @@ -0,0 +1,285 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + + let i = residual_[rng.gen_range(0..residual_.len())]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_optima/commercial.rs b/tig-algorithms/src/satisfiability/sat_optima/commercial.rs new file mode 100644 index 0000000..1c4e87a --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/commercial.rs @@ -0,0 +1,285 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + + let i = residual_[rng.gen_range(0..residual_.len())]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_optima/inbound.rs b/tig-algorithms/src/satisfiability/sat_optima/inbound.rs new file mode 100644 index 0000000..4d56d06 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/inbound.rs @@ -0,0 +1,285 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + + let i = residual_[rng.gen_range(0..residual_.len())]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_optima/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sat_optima/innovator_outbound.rs new file mode 100644 index 0000000..d718930 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/innovator_outbound.rs @@ -0,0 +1,285 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + + let i = residual_[rng.gen_range(0..residual_.len())]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sat_optima/mod.rs b/tig-algorithms/src/satisfiability/sat_optima/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sat_optima/open_data.rs b/tig-algorithms/src/satisfiability/sat_optima/open_data.rs new file mode 100644 index 0000000..19eda8b --- /dev/null +++ b/tig-algorithms/src/satisfiability/sat_optima/open_data.rs @@ -0,0 +1,285 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut rounds = 0; + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); // Preallocate with capacity + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![Vec::new(); num_variables]; + let mut n_clauses: Vec> = vec![Vec::new(); num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + // Preallocate capacity for p_clauses and n_clauses + for c in &clauses { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + if p_clauses[var].capacity() == 0 { + p_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } else { + if n_clauses[var].capacity() == 0 { + n_clauses[var] = Vec::with_capacity(clauses.len() / num_variables + 1); + } + } + } + } + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + loop { + if !residual_.is_empty() { + + let i = residual_[rng.gen_range(0..residual_.len())]; + let mut min_sad = clauses.len(); + let mut v_min_sad = Vec::with_capacity(clauses[i].len()); // Preallocate with capacity + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad.clear(); + v_min_sad.push((l.abs() - 1) as usize); + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..(c.len() as u32)) as usize]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..(v_min_sad.len() as u32)) as usize] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + rounds += 1; + if rounds >= num_variables * 35 { + return Ok(None); + } + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs index a058f95..37de9a2 100644 --- a/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs +++ b/tig-algorithms/src/satisfiability/schnoing/benchmarker_outbound.rs @@ -17,7 +17,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; use tig_challenges::satisfiability::*; pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::from_seed(challenge.seed); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); let num_variables = challenge.difficulty.num_variables; let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); diff --git a/tig-algorithms/src/satisfiability/schnoing/commercial.rs b/tig-algorithms/src/satisfiability/schnoing/commercial.rs index 0a803a8..fccc582 100644 --- a/tig-algorithms/src/satisfiability/schnoing/commercial.rs +++ b/tig-algorithms/src/satisfiability/schnoing/commercial.rs @@ -17,7 +17,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; use tig_challenges::satisfiability::*; pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::from_seed(challenge.seed); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); let num_variables = challenge.difficulty.num_variables; let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); diff --git a/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs b/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs new file mode 100644 index 0000000..908b9ed --- /dev/null +++ b/tig-algorithms/src/satisfiability/schnoing/cuda_example.rs @@ -0,0 +1,156 @@ +/*! +Copyright 2024 TIG Foundation + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(challenge.seeds[0] as u64); + let num_variables = challenge.difficulty.num_variables; + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + // Pre-generate a bunch of random integers + // IMPORTANT! When generating random numbers, never use usize! usize bytes varies from system to system + let rand_ints: Vec = (0..2 * num_variables) + .map(|_| rng.gen_range(0..1_000_000_000u32) as usize) + .collect(); + + for i in 0..num_variables { + // Evaluate clauses and find any that are unsatisfied + let substituted: Vec = challenge + .clauses + .iter() + .map(|clause| { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + let var_value = variables[var_idx]; + (literal > 0 && var_value) || (literal < 0 && !var_value) + }) + }) + .collect(); + + let unsatisfied_clauses: Vec = substituted + .iter() + .enumerate() + .filter_map(|(idx, &satisfied)| if !satisfied { Some(idx) } else { None }) + .collect(); + + let num_unsatisfied_clauses = unsatisfied_clauses.len(); + if num_unsatisfied_clauses == 0 { + break; + } + + // Flip the value of a random variable from a random unsatisfied clause + let rand_unsatisfied_clause_idx = rand_ints[2 * i] % num_unsatisfied_clauses; + let rand_unsatisfied_clause = unsatisfied_clauses[rand_unsatisfied_clause_idx]; + let rand_variable_idx = rand_ints[2 * i + 1] % 3; + let rand_variable = + challenge.clauses[rand_unsatisfied_clause][rand_variable_idx].abs() as usize - 1; + variables[rand_variable] = !variables[rand_variable]; + } + + Ok(Some(Solution { variables })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = Some(CudaKernel { + // Example CUDA code from https://github.com/coreylowman/cudarc/blob/main/examples/matmul-kernel.rs + src: r#" +extern "C" __global__ void matmul(float* A, float* B, float* C, int N) { + int ROW = blockIdx.y*blockDim.y+threadIdx.y; + int COL = blockIdx.x*blockDim.x+threadIdx.x; + + float tmpSum = 0; + + if (ROW < N && COL < N) { + // each thread computes one element of the block sub-matrix + for (int i = 0; i < N; i++) { + tmpSum += A[ROW * N + i] * B[i * N + COL]; + } + } + C[ROW * N + COL] = tmpSum; +} +"#, + funcs: &["matmul"], + }); + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + // Example CUDA code from https://github.com/coreylowman/cudarc/blob/main/examples/matmul-kernel.rs + let start = std::time::Instant::now(); + + let a_host = [1.0f32, 2.0, 3.0, 4.0]; + let b_host = [1.0f32, 2.0, 3.0, 4.0]; + let mut c_host = [0.0f32; 4]; + + let a_dev = dev.htod_sync_copy(&a_host)?; + let b_dev = dev.htod_sync_copy(&b_host)?; + let mut c_dev = dev.htod_sync_copy(&c_host)?; + + println!("Copied in {:?}", start.elapsed()); + + let cfg = LaunchConfig { + block_dim: (2, 2, 1), + grid_dim: (1, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs + .remove("matmul") + .unwrap() + .launch(cfg, (&a_dev, &b_dev, &mut c_dev, 2i32)) + }?; + + dev.dtoh_sync_copy_into(&c_dev, &mut c_host)?; + println!("Found {:?} in {:?}", c_host, start.elapsed()); + + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/schnoing/inbound.rs b/tig-algorithms/src/satisfiability/schnoing/inbound.rs index 05106b2..60efa83 100644 --- a/tig-algorithms/src/satisfiability/schnoing/inbound.rs +++ b/tig-algorithms/src/satisfiability/schnoing/inbound.rs @@ -17,7 +17,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; use tig_challenges::satisfiability::*; pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::from_seed(challenge.seed); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); let num_variables = challenge.difficulty.num_variables; let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); diff --git a/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs b/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs index 3059417..d925304 100644 --- a/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs +++ b/tig-algorithms/src/satisfiability/schnoing/innovator_outbound.rs @@ -17,7 +17,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; use tig_challenges::satisfiability::*; pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::from_seed(challenge.seed); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); let num_variables = challenge.difficulty.num_variables; let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); diff --git a/tig-algorithms/src/satisfiability/schnoing/open_data.rs b/tig-algorithms/src/satisfiability/schnoing/open_data.rs index c0c7b8b..93bef13 100644 --- a/tig-algorithms/src/satisfiability/schnoing/open_data.rs +++ b/tig-algorithms/src/satisfiability/schnoing/open_data.rs @@ -17,7 +17,7 @@ use rand::{rngs::StdRng, Rng, SeedableRng}; use tig_challenges::satisfiability::*; pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { - let mut rng = StdRng::from_seed(challenge.seed); + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); let num_variables = challenge.difficulty.num_variables; let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); diff --git a/tig-algorithms/src/satisfiability/sprint_sat/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/benchmarker_outbound.rs new file mode 100644 index 0000000..648e969 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/benchmarker_outbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sprint_sat/commercial.rs b/tig-algorithms/src/satisfiability/sprint_sat/commercial.rs new file mode 100644 index 0000000..24e2b1a --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/commercial.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sprint_sat/inbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/inbound.rs new file mode 100644 index 0000000..18ade15 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/inbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sprint_sat/innovator_outbound.rs b/tig-algorithms/src/satisfiability/sprint_sat/innovator_outbound.rs new file mode 100644 index 0000000..e71c8bb --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/innovator_outbound.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/sprint_sat/mod.rs b/tig-algorithms/src/satisfiability/sprint_sat/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs b/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs new file mode 100644 index 0000000..61e8321 --- /dev/null +++ b/tig-algorithms/src/satisfiability/sprint_sat/open_data.rs @@ -0,0 +1,267 @@ +/*! +Copyright 2024 Dominic Kennedy + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use rand::{rngs::StdRng, Rng, SeedableRng}; +use std::collections::HashMap; +use tig_challenges::satisfiability::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + + let mut p_single = vec![false; challenge.difficulty.num_variables]; + let mut n_single = vec![false; challenge.difficulty.num_variables]; + + let mut clauses_ = challenge.clauses.clone(); + let mut clauses: Vec> = Vec::with_capacity(clauses_.len()); + + let mut dead = false; + + while !(dead) { + let mut done = true; + for c in &clauses_ { + let mut c_: Vec = Vec::with_capacity(c.len()); + let mut skip = false; + for (i, l) in c.iter().enumerate() { + if (p_single[(l.abs() - 1) as usize] && *l > 0) + || (n_single[(l.abs() - 1) as usize] && *l < 0) + || c[(i + 1)..].contains(&-l) + { + skip = true; + break; + } else if p_single[(l.abs() - 1) as usize] + || n_single[(l.abs() - 1) as usize] + || c[(i + 1)..].contains(&l) + { + done = false; + continue; + } else { + c_.push(*l); + } + } + if skip { + done = false; + continue; + }; + match c_[..] { + [l] => { + done = false; + if l > 0 { + if n_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + p_single[(l.abs() - 1) as usize] = true; + } + } else { + if p_single[(l.abs() - 1) as usize] { + dead = true; + break; + } else { + n_single[(l.abs() - 1) as usize] = true; + } + } + } + [] => { + dead = true; + break; + } + _ => { + clauses.push(c_); + } + } + } + if done { + break; + } else { + clauses_ = clauses; + clauses = Vec::with_capacity(clauses_.len()); + } + } + + if dead { + return Ok(None); + } + + let num_variables = challenge.difficulty.num_variables; + let num_clauses = clauses.len(); + + let mut p_clauses: Vec> = vec![vec![]; num_variables]; + let mut n_clauses: Vec> = vec![vec![]; num_variables]; + + let mut variables = vec![false; num_variables]; + for v in 0..num_variables { + if p_single[v] { + variables[v] = true + } else if n_single[v] { + variables[v] = false + } else { + variables[v] = rng.gen_bool(0.5) + } + } + let mut num_good_so_far: Vec = vec![0; num_clauses]; + + for (i, &ref c) in clauses.iter().enumerate() { + for &l in c { + let var = (l.abs() - 1) as usize; + if l > 0 { + p_clauses[var].push(i); + if variables[var] { + num_good_so_far[i] += 1 + } + } else { + n_clauses[var].push(i); + if !variables[var] { + num_good_so_far[i] += 1 + } + } + } + } + + let mut residual_ = Vec::with_capacity(num_clauses); + let mut residual_indices = HashMap::with_capacity(num_clauses); + + for (i, &num_good) in num_good_so_far.iter().enumerate() { + if num_good == 0 { + residual_.push(i); + residual_indices.insert(i, residual_.len() - 1); + } + } + + let mut attempts = 0; + loop { + if attempts >= num_variables * 25 { + return Ok(None); + } + if !residual_.is_empty() { + let i = residual_[0]; + let mut min_sad = clauses.len(); + let mut v_min_sad = vec![]; + let c = &clauses[i]; + for &l in c { + let mut sad = 0 as usize; + if variables[(l.abs() - 1) as usize] { + for &c in &p_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } else { + for &c in &n_clauses[(l.abs() - 1) as usize] { + if num_good_so_far[c] == 1 { + sad += 1; + if sad > min_sad { + break; + } + } + } + } + + if sad < min_sad { + min_sad = sad; + v_min_sad = vec![(l.abs() - 1) as usize]; + } else if sad == min_sad { + v_min_sad.push((l.abs() - 1) as usize); + } + } + let v = if min_sad == 0 { + if v_min_sad.len() == 1 { + v_min_sad[0] + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + } else { + if rng.gen_bool(0.5) { + let l = c[rng.gen_range(0..c.len())]; + (l.abs() - 1) as usize + } else { + v_min_sad[rng.gen_range(0..v_min_sad.len())] + } + }; + + if variables[v] { + for &c in &n_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + for &c in &p_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + } else { + for &c in &n_clauses[v] { + if num_good_so_far[c] == 1 { + residual_.push(c); + residual_indices.insert(c, residual_.len() - 1); + } + num_good_so_far[c] -= 1; + } + + for &c in &p_clauses[v] { + num_good_so_far[c] += 1; + if num_good_so_far[c] == 1 { + let i = residual_indices.remove(&c).unwrap(); + let last = residual_.pop().unwrap(); + if i < residual_.len() { + residual_[i] = last; + residual_indices.insert(last, i); + } + } + } + } + + variables[v] = !variables[v]; + } else { + break; + } + attempts += 1; + } + + return Ok(Some(Solution { variables })); +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/satisfiability/template.rs b/tig-algorithms/src/satisfiability/template.rs index f11c4cd..c2d7153 100644 --- a/tig-algorithms/src/satisfiability/template.rs +++ b/tig-algorithms/src/satisfiability/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::satisfiability::{Challenge, Solution}; diff --git a/tig-algorithms/src/vector_search/basic/benchmarker_outbound.rs b/tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs similarity index 51% rename from tig-algorithms/src/vector_search/basic/benchmarker_outbound.rs rename to tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs index f9d6d0f..68650eb 100644 --- a/tig-algorithms/src/vector_search/basic/benchmarker_outbound.rs +++ b/tig-algorithms/src/satisfiability/walk_sat/benchmarker_outbound.rs @@ -1,5 +1,5 @@ /*! -Copyright 2024 Test +Copyright 2024 Chad Blanchard Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy @@ -14,28 +14,44 @@ language governing permissions and limitations under the License. */ // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use tig_challenges::vector_search::{euclidean_distance, Challenge, Solution}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tig_challenges::satisfiability::*; -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let mut indexes = Vec::::new(); - for query in challenge.query_vectors.iter() { - let mut found = false; - for (idx, v) in challenge.vector_database.iter().enumerate() { - if euclidean_distance(query, v) <= challenge.max_distance { - indexes.push(idx); - found = true; - break; - } - } - if !found { - return Ok(None); +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.difficulty.num_variables; + let max_flips = 1000; + + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + for _ in 0..max_flips { + let mut unsatisfied_clauses: Vec<&Vec> = challenge + .clauses + .iter() + .filter(|clause| !clause_satisfied(clause, &variables)) + .collect(); + + if unsatisfied_clauses.is_empty() { + return Ok(Some(Solution { variables })); } + + let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); + let literal = clause.choose(&mut rng).unwrap(); + let var_idx = literal.abs() as usize - 1; + variables[var_idx] = !variables[var_idx]; } - Ok(Some(Solution { indexes })) + + Ok(None) } -// Important! Do not include any tests in this file, it will result in your submission being rejected +fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) + }) +} #[cfg(feature = "cuda")] mod gpu_optimisation { diff --git a/tig-algorithms/src/vector_search/basic/commercial.rs b/tig-algorithms/src/satisfiability/walk_sat/commercial.rs similarity index 51% rename from tig-algorithms/src/vector_search/basic/commercial.rs rename to tig-algorithms/src/satisfiability/walk_sat/commercial.rs index d9c034c..9b49cbe 100644 --- a/tig-algorithms/src/vector_search/basic/commercial.rs +++ b/tig-algorithms/src/satisfiability/walk_sat/commercial.rs @@ -1,5 +1,5 @@ /*! -Copyright 2024 Test +Copyright 2024 Chad Blanchard Licensed under the TIG Commercial License v1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy @@ -14,28 +14,44 @@ language governing permissions and limitations under the License. */ // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use tig_challenges::vector_search::{euclidean_distance, Challenge, Solution}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tig_challenges::satisfiability::*; -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let mut indexes = Vec::::new(); - for query in challenge.query_vectors.iter() { - let mut found = false; - for (idx, v) in challenge.vector_database.iter().enumerate() { - if euclidean_distance(query, v) <= challenge.max_distance { - indexes.push(idx); - found = true; - break; - } - } - if !found { - return Ok(None); +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.difficulty.num_variables; + let max_flips = 1000; + + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + for _ in 0..max_flips { + let mut unsatisfied_clauses: Vec<&Vec> = challenge + .clauses + .iter() + .filter(|clause| !clause_satisfied(clause, &variables)) + .collect(); + + if unsatisfied_clauses.is_empty() { + return Ok(Some(Solution { variables })); } + + let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); + let literal = clause.choose(&mut rng).unwrap(); + let var_idx = literal.abs() as usize - 1; + variables[var_idx] = !variables[var_idx]; } - Ok(Some(Solution { indexes })) + + Ok(None) } -// Important! Do not include any tests in this file, it will result in your submission being rejected +fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) + }) +} #[cfg(feature = "cuda")] mod gpu_optimisation { diff --git a/tig-algorithms/src/vector_search/basic/inbound.rs b/tig-algorithms/src/satisfiability/walk_sat/inbound.rs similarity index 52% rename from tig-algorithms/src/vector_search/basic/inbound.rs rename to tig-algorithms/src/satisfiability/walk_sat/inbound.rs index 6a475f7..9f6ebfe 100644 --- a/tig-algorithms/src/vector_search/basic/inbound.rs +++ b/tig-algorithms/src/satisfiability/walk_sat/inbound.rs @@ -1,5 +1,5 @@ /*! -Copyright 2024 Test +Copyright 2024 Chad Blanchard Licensed under the TIG Inbound Game License v1.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the @@ -14,28 +14,44 @@ language governing permissions and limitations under the License. */ // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use tig_challenges::vector_search::{euclidean_distance, Challenge, Solution}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tig_challenges::satisfiability::*; -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let mut indexes = Vec::::new(); - for query in challenge.query_vectors.iter() { - let mut found = false; - for (idx, v) in challenge.vector_database.iter().enumerate() { - if euclidean_distance(query, v) <= challenge.max_distance { - indexes.push(idx); - found = true; - break; - } - } - if !found { - return Ok(None); +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.difficulty.num_variables; + let max_flips = 1000; + + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + for _ in 0..max_flips { + let mut unsatisfied_clauses: Vec<&Vec> = challenge + .clauses + .iter() + .filter(|clause| !clause_satisfied(clause, &variables)) + .collect(); + + if unsatisfied_clauses.is_empty() { + return Ok(Some(Solution { variables })); } + + let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); + let literal = clause.choose(&mut rng).unwrap(); + let var_idx = literal.abs() as usize - 1; + variables[var_idx] = !variables[var_idx]; } - Ok(Some(Solution { indexes })) + + Ok(None) } -// Important! Do not include any tests in this file, it will result in your submission being rejected +fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) + }) +} #[cfg(feature = "cuda")] mod gpu_optimisation { diff --git a/tig-algorithms/src/vector_search/basic/innovator_outbound.rs b/tig-algorithms/src/satisfiability/walk_sat/innovator_outbound.rs similarity index 51% rename from tig-algorithms/src/vector_search/basic/innovator_outbound.rs rename to tig-algorithms/src/satisfiability/walk_sat/innovator_outbound.rs index e934bee..2eba628 100644 --- a/tig-algorithms/src/vector_search/basic/innovator_outbound.rs +++ b/tig-algorithms/src/satisfiability/walk_sat/innovator_outbound.rs @@ -1,5 +1,5 @@ /*! -Copyright 2024 Test +Copyright 2024 Chad Blanchard Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy @@ -14,28 +14,44 @@ language governing permissions and limitations under the License. */ // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use tig_challenges::vector_search::{euclidean_distance, Challenge, Solution}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tig_challenges::satisfiability::*; -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let mut indexes = Vec::::new(); - for query in challenge.query_vectors.iter() { - let mut found = false; - for (idx, v) in challenge.vector_database.iter().enumerate() { - if euclidean_distance(query, v) <= challenge.max_distance { - indexes.push(idx); - found = true; - break; - } - } - if !found { - return Ok(None); +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.difficulty.num_variables; + let max_flips = 1000; + + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + for _ in 0..max_flips { + let mut unsatisfied_clauses: Vec<&Vec> = challenge + .clauses + .iter() + .filter(|clause| !clause_satisfied(clause, &variables)) + .collect(); + + if unsatisfied_clauses.is_empty() { + return Ok(Some(Solution { variables })); } + + let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); + let literal = clause.choose(&mut rng).unwrap(); + let var_idx = literal.abs() as usize - 1; + variables[var_idx] = !variables[var_idx]; } - Ok(Some(Solution { indexes })) + + Ok(None) } -// Important! Do not include any tests in this file, it will result in your submission being rejected +fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) + }) +} #[cfg(feature = "cuda")] mod gpu_optimisation { diff --git a/tig-algorithms/src/satisfiability/walk_sat/mod.rs b/tig-algorithms/src/satisfiability/walk_sat/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/satisfiability/walk_sat/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/basic/open_data.rs b/tig-algorithms/src/satisfiability/walk_sat/open_data.rs similarity index 52% rename from tig-algorithms/src/vector_search/basic/open_data.rs rename to tig-algorithms/src/satisfiability/walk_sat/open_data.rs index c35706b..4bdcf81 100644 --- a/tig-algorithms/src/vector_search/basic/open_data.rs +++ b/tig-algorithms/src/satisfiability/walk_sat/open_data.rs @@ -1,5 +1,5 @@ /*! -Copyright 2024 Test +Copyright 2024 Chad Blanchard Licensed under the TIG Open Data License v1.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. @@ -14,28 +14,44 @@ language governing permissions and limitations under the License. */ // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge -use anyhow::Result; -use tig_challenges::vector_search::{euclidean_distance, Challenge, Solution}; +use rand::prelude::*; +use rand::rngs::StdRng; +use rand::SeedableRng; +use tig_challenges::satisfiability::*; -pub fn solve_challenge(challenge: &Challenge) -> Result> { - let mut indexes = Vec::::new(); - for query in challenge.query_vectors.iter() { - let mut found = false; - for (idx, v) in challenge.vector_database.iter().enumerate() { - if euclidean_distance(query, v) <= challenge.max_distance { - indexes.push(idx); - found = true; - break; - } - } - if !found { - return Ok(None); +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut rng = StdRng::seed_from_u64(u64::from_le_bytes(challenge.seed[..8].try_into().unwrap()) as u64); + let num_variables = challenge.difficulty.num_variables; + let max_flips = 1000; + + let mut variables: Vec = (0..num_variables).map(|_| rng.gen::()).collect(); + + for _ in 0..max_flips { + let mut unsatisfied_clauses: Vec<&Vec> = challenge + .clauses + .iter() + .filter(|clause| !clause_satisfied(clause, &variables)) + .collect(); + + if unsatisfied_clauses.is_empty() { + return Ok(Some(Solution { variables })); } + + let clause = unsatisfied_clauses.choose(&mut rng).unwrap(); + let literal = clause.choose(&mut rng).unwrap(); + let var_idx = literal.abs() as usize - 1; + variables[var_idx] = !variables[var_idx]; } - Ok(Some(Solution { indexes })) + + Ok(None) } -// Important! Do not include any tests in this file, it will result in your submission being rejected +fn clause_satisfied(clause: &Vec, variables: &[bool]) -> bool { + clause.iter().any(|&literal| { + let var_idx = literal.abs() as usize - 1; + (literal > 0 && variables[var_idx]) || (literal < 0 && !variables[var_idx]) + }) +} #[cfg(feature = "cuda")] mod gpu_optimisation { diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/benchmarker_outbound.rs new file mode 100644 index 0000000..8a58f5c --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/benchmarker_outbound.rs @@ -0,0 +1,113 @@ +/*! +Copyright 2024 Louis Silva + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. + */ + +use anyhow::Result; + +use tig_challenges::vector_search::*; + +#[inline] +fn l2_norm(x: &[f32]) -> f32 { + x.iter().map(|&val| val * val).sum::().sqrt() +} + +#[inline] +fn euclidean_distance_with_precomputed_norm( + a_norm_sq: f32, + b_norm_sq: f32, + ab_dot_product: f32 +) -> f32 { + (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() +} + +pub fn solve_challenge(challenge: &Challenge) -> Result> { + let vector_database: &Vec> = &challenge.vector_database; + let query_vectors: &Vec> = &challenge.query_vectors; + let max_distance: f32 = challenge.max_distance; + + let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); + let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); + + let mut sum_norms_sq: f32 = 0.0; + let mut sum_squares: f32 = 0.0; + + for vector in vector_database { + let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); + sum_norms_sq += norm_sq.sqrt(); + sum_squares += norm_sq; + vector_norms_sq.push(norm_sq); + } + + let vector_norms_len: f32 = vector_norms_sq.len() as f32; + let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); + let norm_threshold: f32 = 2.0 * std_dev; + + for query in query_vectors { + let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); + + let mut closest_index: Option = None; + let mut closest_distance: f32 = f32::MAX; + + for (idx, vector) in vector_database.iter().enumerate() { + let vector_norm_sq = vector_norms_sq[idx]; + if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { + continue; + } + + let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); + let distance: f32 = euclidean_distance_with_precomputed_norm( + query_norm_sq, + vector_norm_sq, + ab_dot_product, + ); + + if distance <= max_distance { + closest_index = Some(idx); + break; // Early exit + } else if distance < closest_distance { + closest_index = Some(idx); + closest_distance = distance; + } + } + + if let Some(index) = closest_index { + indexes.push(index); + } else { + return Ok(None); + } + } + + Ok(Some(Solution { indexes })) +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/commercial.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/commercial.rs new file mode 100644 index 0000000..bacee69 --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/commercial.rs @@ -0,0 +1,113 @@ +/*! +Copyright 2024 Louis Silva + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. + */ + +use anyhow::Result; + +use tig_challenges::vector_search::*; + +#[inline] +fn l2_norm(x: &[f32]) -> f32 { + x.iter().map(|&val| val * val).sum::().sqrt() +} + +#[inline] +fn euclidean_distance_with_precomputed_norm( + a_norm_sq: f32, + b_norm_sq: f32, + ab_dot_product: f32 +) -> f32 { + (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() +} + +pub fn solve_challenge(challenge: &Challenge) -> Result> { + let vector_database: &Vec> = &challenge.vector_database; + let query_vectors: &Vec> = &challenge.query_vectors; + let max_distance: f32 = challenge.max_distance; + + let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); + let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); + + let mut sum_norms_sq: f32 = 0.0; + let mut sum_squares: f32 = 0.0; + + for vector in vector_database { + let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); + sum_norms_sq += norm_sq.sqrt(); + sum_squares += norm_sq; + vector_norms_sq.push(norm_sq); + } + + let vector_norms_len: f32 = vector_norms_sq.len() as f32; + let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); + let norm_threshold: f32 = 2.0 * std_dev; + + for query in query_vectors { + let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); + + let mut closest_index: Option = None; + let mut closest_distance: f32 = f32::MAX; + + for (idx, vector) in vector_database.iter().enumerate() { + let vector_norm_sq = vector_norms_sq[idx]; + if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { + continue; + } + + let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); + let distance: f32 = euclidean_distance_with_precomputed_norm( + query_norm_sq, + vector_norm_sq, + ab_dot_product, + ); + + if distance <= max_distance { + closest_index = Some(idx); + break; // Early exit + } else if distance < closest_distance { + closest_index = Some(idx); + closest_distance = distance; + } + } + + if let Some(index) = closest_index { + indexes.push(index); + } else { + return Ok(None); + } + } + + Ok(Some(Solution { indexes })) +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/inbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/inbound.rs new file mode 100644 index 0000000..fd0bf1d --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/inbound.rs @@ -0,0 +1,113 @@ +/*! +Copyright 2024 Louis Silva + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. + */ + +use anyhow::Result; + +use tig_challenges::vector_search::*; + +#[inline] +fn l2_norm(x: &[f32]) -> f32 { + x.iter().map(|&val| val * val).sum::().sqrt() +} + +#[inline] +fn euclidean_distance_with_precomputed_norm( + a_norm_sq: f32, + b_norm_sq: f32, + ab_dot_product: f32 +) -> f32 { + (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() +} + +pub fn solve_challenge(challenge: &Challenge) -> Result> { + let vector_database: &Vec> = &challenge.vector_database; + let query_vectors: &Vec> = &challenge.query_vectors; + let max_distance: f32 = challenge.max_distance; + + let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); + let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); + + let mut sum_norms_sq: f32 = 0.0; + let mut sum_squares: f32 = 0.0; + + for vector in vector_database { + let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); + sum_norms_sq += norm_sq.sqrt(); + sum_squares += norm_sq; + vector_norms_sq.push(norm_sq); + } + + let vector_norms_len: f32 = vector_norms_sq.len() as f32; + let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); + let norm_threshold: f32 = 2.0 * std_dev; + + for query in query_vectors { + let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); + + let mut closest_index: Option = None; + let mut closest_distance: f32 = f32::MAX; + + for (idx, vector) in vector_database.iter().enumerate() { + let vector_norm_sq = vector_norms_sq[idx]; + if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { + continue; + } + + let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); + let distance: f32 = euclidean_distance_with_precomputed_norm( + query_norm_sq, + vector_norm_sq, + ab_dot_product, + ); + + if distance <= max_distance { + closest_index = Some(idx); + break; // Early exit + } else if distance < closest_distance { + closest_index = Some(idx); + closest_distance = distance; + } + } + + if let Some(index) = closest_index { + indexes.push(index); + } else { + return Ok(None); + } + } + + Ok(Some(Solution { indexes })) +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/innovator_outbound.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/innovator_outbound.rs new file mode 100644 index 0000000..a0996b6 --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/innovator_outbound.rs @@ -0,0 +1,113 @@ +/*! +Copyright 2024 Louis Silva + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. + */ + +use anyhow::Result; + +use tig_challenges::vector_search::*; + +#[inline] +fn l2_norm(x: &[f32]) -> f32 { + x.iter().map(|&val| val * val).sum::().sqrt() +} + +#[inline] +fn euclidean_distance_with_precomputed_norm( + a_norm_sq: f32, + b_norm_sq: f32, + ab_dot_product: f32 +) -> f32 { + (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() +} + +pub fn solve_challenge(challenge: &Challenge) -> Result> { + let vector_database: &Vec> = &challenge.vector_database; + let query_vectors: &Vec> = &challenge.query_vectors; + let max_distance: f32 = challenge.max_distance; + + let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); + let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); + + let mut sum_norms_sq: f32 = 0.0; + let mut sum_squares: f32 = 0.0; + + for vector in vector_database { + let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); + sum_norms_sq += norm_sq.sqrt(); + sum_squares += norm_sq; + vector_norms_sq.push(norm_sq); + } + + let vector_norms_len: f32 = vector_norms_sq.len() as f32; + let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); + let norm_threshold: f32 = 2.0 * std_dev; + + for query in query_vectors { + let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); + + let mut closest_index: Option = None; + let mut closest_distance: f32 = f32::MAX; + + for (idx, vector) in vector_database.iter().enumerate() { + let vector_norm_sq = vector_norms_sq[idx]; + if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { + continue; + } + + let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); + let distance: f32 = euclidean_distance_with_precomputed_norm( + query_norm_sq, + vector_norm_sq, + ab_dot_product, + ); + + if distance <= max_distance { + closest_index = Some(idx); + break; // Early exit + } else if distance < closest_distance { + closest_index = Some(idx); + closest_distance = distance; + } + } + + if let Some(index) = closest_index { + indexes.push(index); + } else { + return Ok(None); + } + } + + Ok(Some(Solution { indexes })) +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/mod.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs b/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs new file mode 100644 index 0000000..c55800e --- /dev/null +++ b/tig-algorithms/src/vector_search/brute_force_bacalhau/open_data.rs @@ -0,0 +1,113 @@ +/*! +Copyright 2024 Louis Silva + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. + */ + +use anyhow::Result; + +use tig_challenges::vector_search::*; + +#[inline] +fn l2_norm(x: &[f32]) -> f32 { + x.iter().map(|&val| val * val).sum::().sqrt() +} + +#[inline] +fn euclidean_distance_with_precomputed_norm( + a_norm_sq: f32, + b_norm_sq: f32, + ab_dot_product: f32 +) -> f32 { + (a_norm_sq + b_norm_sq - 2.0 * ab_dot_product).sqrt() +} + +pub fn solve_challenge(challenge: &Challenge) -> Result> { + let vector_database: &Vec> = &challenge.vector_database; + let query_vectors: &Vec> = &challenge.query_vectors; + let max_distance: f32 = challenge.max_distance; + + let mut indexes: Vec = Vec::with_capacity(query_vectors.len()); + let mut vector_norms_sq: Vec = Vec::with_capacity(vector_database.len()); + + let mut sum_norms_sq: f32 = 0.0; + let mut sum_squares: f32 = 0.0; + + for vector in vector_database { + let norm_sq: f32 = vector.iter().map(|&val| val * val).sum(); + sum_norms_sq += norm_sq.sqrt(); + sum_squares += norm_sq; + vector_norms_sq.push(norm_sq); + } + + let vector_norms_len: f32 = vector_norms_sq.len() as f32; + let std_dev: f32 = ((sum_squares / vector_norms_len) - (sum_norms_sq / vector_norms_len).powi(2)).sqrt(); + let norm_threshold: f32 = 2.0 * std_dev; + + for query in query_vectors { + let query_norm_sq: f32 = query.iter().map(|&val| val * val).sum(); + + let mut closest_index: Option = None; + let mut closest_distance: f32 = f32::MAX; + + for (idx, vector) in vector_database.iter().enumerate() { + let vector_norm_sq = vector_norms_sq[idx]; + if ((vector_norm_sq.sqrt() - query_norm_sq.sqrt()).abs()) > norm_threshold { + continue; + } + + let ab_dot_product: f32 = query.iter().zip(vector).map(|(&x1, &x2)| x1 * x2).sum(); + let distance: f32 = euclidean_distance_with_precomputed_norm( + query_norm_sq, + vector_norm_sq, + ab_dot_product, + ); + + if distance <= max_distance { + closest_index = Some(idx); + break; // Early exit + } else if distance < closest_distance { + closest_index = Some(idx); + closest_distance = distance; + } + } + + if let Some(index) = closest_index { + indexes.push(index); + } else { + return Ok(None); + } + } + + Ok(Some(Solution { indexes })) +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/invector/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/invector/benchmarker_outbound.rs new file mode 100644 index 0000000..c7d3dac --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/benchmarker_outbound.rs @@ -0,0 +1,367 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} + +#[inline(always)] +fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { + let mut sum = 0.0; + for i in 0..180 { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + if sum > c { + sum; + } + for i in 180..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + let len = a.len(); + + if a.len() != b.len() || a.len() < 8 { + return f32::MAX; + } + + while i + 7 < len { + unsafe { + let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); + let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); + let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); + let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); + let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); + let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); + let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); + let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + + diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; + } + + if sum > current_min { + return f32::MAX; + } + + i += 8; + } + + while i < len { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + i += 1; + } + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + let (nearer, farther) = if diff < 0.0 { + (&node.left, &node.right) + } else { + (&node.right, &node.left) + }; + + if let Some(nearer_node) = nearer { + stack.push((nearer_node.as_ref(), depth + 1)); + } + + if sqr_diff < best.0 { + if let Some(farther_node) = farther { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + if heap.len() < k + { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() + { + let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); + let ord_dist = FloatOrd(dist); + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let max_fuel = 2000000000.0; + let base_fuel = 760000000.0; + let alpha = 1700.0 * challenge.difficulty.num_queries as f64; + + let subset_size = ((max_fuel - base_fuel) / alpha) as usize; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/invector/commercial.rs b/tig-algorithms/src/vector_search/invector/commercial.rs new file mode 100644 index 0000000..5ab2af2 --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/commercial.rs @@ -0,0 +1,367 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} + +#[inline(always)] +fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { + let mut sum = 0.0; + for i in 0..180 { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + if sum > c { + sum; + } + for i in 180..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + let len = a.len(); + + if a.len() != b.len() || a.len() < 8 { + return f32::MAX; + } + + while i + 7 < len { + unsafe { + let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); + let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); + let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); + let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); + let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); + let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); + let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); + let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + + diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; + } + + if sum > current_min { + return f32::MAX; + } + + i += 8; + } + + while i < len { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + i += 1; + } + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + let (nearer, farther) = if diff < 0.0 { + (&node.left, &node.right) + } else { + (&node.right, &node.left) + }; + + if let Some(nearer_node) = nearer { + stack.push((nearer_node.as_ref(), depth + 1)); + } + + if sqr_diff < best.0 { + if let Some(farther_node) = farther { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + if heap.len() < k + { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() + { + let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); + let ord_dist = FloatOrd(dist); + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let max_fuel = 2000000000.0; + let base_fuel = 760000000.0; + let alpha = 1700.0 * challenge.difficulty.num_queries as f64; + + let subset_size = ((max_fuel - base_fuel) / alpha) as usize; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/invector/inbound.rs b/tig-algorithms/src/vector_search/invector/inbound.rs new file mode 100644 index 0000000..bbc5f58 --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/inbound.rs @@ -0,0 +1,367 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} + +#[inline(always)] +fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { + let mut sum = 0.0; + for i in 0..180 { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + if sum > c { + sum; + } + for i in 180..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + let len = a.len(); + + if a.len() != b.len() || a.len() < 8 { + return f32::MAX; + } + + while i + 7 < len { + unsafe { + let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); + let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); + let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); + let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); + let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); + let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); + let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); + let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + + diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; + } + + if sum > current_min { + return f32::MAX; + } + + i += 8; + } + + while i < len { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + i += 1; + } + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + let (nearer, farther) = if diff < 0.0 { + (&node.left, &node.right) + } else { + (&node.right, &node.left) + }; + + if let Some(nearer_node) = nearer { + stack.push((nearer_node.as_ref(), depth + 1)); + } + + if sqr_diff < best.0 { + if let Some(farther_node) = farther { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + if heap.len() < k + { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() + { + let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); + let ord_dist = FloatOrd(dist); + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let max_fuel = 2000000000.0; + let base_fuel = 760000000.0; + let alpha = 1700.0 * challenge.difficulty.num_queries as f64; + + let subset_size = ((max_fuel - base_fuel) / alpha) as usize; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/invector/innovator_outbound.rs b/tig-algorithms/src/vector_search/invector/innovator_outbound.rs new file mode 100644 index 0000000..3496425 --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/innovator_outbound.rs @@ -0,0 +1,367 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} + +#[inline(always)] +fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { + let mut sum = 0.0; + for i in 0..180 { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + if sum > c { + sum; + } + for i in 180..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + let len = a.len(); + + if a.len() != b.len() || a.len() < 8 { + return f32::MAX; + } + + while i + 7 < len { + unsafe { + let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); + let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); + let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); + let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); + let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); + let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); + let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); + let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + + diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; + } + + if sum > current_min { + return f32::MAX; + } + + i += 8; + } + + while i < len { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + i += 1; + } + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + let (nearer, farther) = if diff < 0.0 { + (&node.left, &node.right) + } else { + (&node.right, &node.left) + }; + + if let Some(nearer_node) = nearer { + stack.push((nearer_node.as_ref(), depth + 1)); + } + + if sqr_diff < best.0 { + if let Some(farther_node) = farther { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + if heap.len() < k + { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() + { + let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); + let ord_dist = FloatOrd(dist); + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let max_fuel = 2000000000.0; + let base_fuel = 760000000.0; + let alpha = 1700.0 * challenge.difficulty.num_queries as f64; + + let subset_size = ((max_fuel - base_fuel) / alpha) as usize; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/invector/mod.rs b/tig-algorithms/src/vector_search/invector/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/invector/open_data.rs b/tig-algorithms/src/vector_search/invector/open_data.rs new file mode 100644 index 0000000..cada64f --- /dev/null +++ b/tig-algorithms/src/vector_search/invector/open_data.rs @@ -0,0 +1,367 @@ +/*! +Copyright 2024 syebastian + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} + +#[inline(always)] +fn squared_euclidean_distance_limited(a: &[f32], b: &[f32], c : f32) -> f32 { + let mut sum = 0.0; + for i in 0..180 { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + if sum > c { + sum; + } + for i in 180..a.len() { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + } + sum +} +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + let len = a.len(); + + if a.len() != b.len() || a.len() < 8 { + return f32::MAX; + } + + while i + 7 < len { + unsafe { + let diff0 = *a.get_unchecked(i) - *b.get_unchecked(i); + let diff1 = *a.get_unchecked(i + 1) - *b.get_unchecked(i + 1); + let diff2 = *a.get_unchecked(i + 2) - *b.get_unchecked(i + 2); + let diff3 = *a.get_unchecked(i + 3) - *b.get_unchecked(i + 3); + let diff4 = *a.get_unchecked(i + 4) - *b.get_unchecked(i + 4); + let diff5 = *a.get_unchecked(i + 5) - *b.get_unchecked(i + 5); + let diff6 = *a.get_unchecked(i + 6) - *b.get_unchecked(i + 6); + let diff7 = *a.get_unchecked(i + 7) - *b.get_unchecked(i + 7); + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3 + + diff4 * diff4 + diff5 * diff5 + diff6 * diff6 + diff7 * diff7; + } + + if sum > current_min { + return f32::MAX; + } + + i += 8; + } + + while i < len { + unsafe { + let diff = *a.get_unchecked(i) - *b.get_unchecked(i); + sum += diff * diff; + } + i += 1; + } + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + let (nearer, farther) = if diff < 0.0 { + (&node.left, &node.right) + } else { + (&node.right, &node.left) + }; + + if let Some(nearer_node) = nearer { + stack.push((nearer_node.as_ref(), depth + 1)); + } + + if sqr_diff < best.0 { + if let Some(farther_node) = farther { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + if heap.len() < k + { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() + { + let dist = squared_euclidean_distance_limited(&mean_query_vector, vector, top_dist); + let ord_dist = FloatOrd(dist); + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let max_fuel = 2000000000.0; + let base_fuel = 760000000.0; + let alpha = 1700.0 * challenge.difficulty.num_queries as f64; + + let subset_size = ((max_fuel - base_fuel) / alpha) as usize; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/mod.rs b/tig-algorithms/src/vector_search/mod.rs index d35e228..bf245d3 100644 --- a/tig-algorithms/src/vector_search/mod.rs +++ b/tig-algorithms/src/vector_search/mod.rs @@ -1,5 +1,4 @@ -pub mod basic; -pub use basic as c004_a001; +// c004_a001 // c004_a002 @@ -25,7 +24,8 @@ pub use basic as c004_a001; // c004_a013 -// c004_a014 +pub mod brute_force_bacalhau; +pub use brute_force_bacalhau as c004_a014; // c004_a015 @@ -49,7 +49,8 @@ pub use basic as c004_a001; // c004_a025 -// c004_a026 +pub mod optimax_gpu; +pub use optimax_gpu as c004_a026; // c004_a027 @@ -65,7 +66,8 @@ pub use basic as c004_a001; // c004_a033 -// c004_a034 +pub mod invector; +pub use invector as c004_a034; // c004_a035 diff --git a/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs new file mode 100644 index 0000000..dd9b614 --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/benchmarker_outbound.rs @@ -0,0 +1,468 @@ +/*! +Copyright 2024 bw-dev36 + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + } + sum +} + +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + while i + 3 < a.len() { + let diff0 = a[i] - b[i]; + let diff1 = a[i + 1] - b[i + 1]; + let diff2 = a[i + 2] - b[i + 2]; + let diff3 = a[i + 3] - b[i + 3]; + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + + if sum > current_min { + return f32::MAX; + } + + i += 4; + } + + while i < a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + + if sum > current_min { + return f32::MAX; + } + + i += 1; + } + + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + if sqr_diff < best.0 { + if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + + if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { + stack.push((nearer_node.as_ref(), depth + 1)); + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + pub const KERNEL: Option = Some(CudaKernel { + src: r#" + + extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_vectors) { + float dist = 0.0; + for (int d = 0; d < num_dimensions; ++d) { + float diff = query_mean[d] - vectors[idx * num_dimensions + d]; + dist += diff * diff; + } + distances[idx] = dist; + } + } + + "#, + + funcs: &["filter_vectors"], + }); + + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = cuda_filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + dev, + funcs, + )?; + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + + + + Ok(Some(Solution { + indexes: best_indexes, + })) + } + + #[cfg(feature = "cuda")] + fn cuda_filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let num_vectors = database.len(); + let num_dimensions = 250; + let flattened_database: Vec = database.iter().flatten().cloned().collect(); + let database_dev = dev.htod_sync_copy(&flattened_database)?; + let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; + let mut distances_dev = dev.alloc_zeros::(num_vectors)?; + let cfg = LaunchConfig { + block_dim: (256, 1, 1), + grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs.remove("filter_vectors").unwrap().launch( + cfg, + ( + &mean_query_dev, + &database_dev, + &mut distances_dev, + num_vectors as i32, + num_dimensions as i32, + ), + ) + }?; + let mut distances_host = vec![0.0f32; num_vectors]; + dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, &distance) in distances_host.iter().enumerate() { + let ord_dist = FloatOrd(distance); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if distance < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&[f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + Ok(result) + } + + #[cfg(feature = "cuda")] + fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> Option>> { + None + } + + #[cfg(feature = "cuda")] + fn cuda_nearest_neighbor_search( + kd_tree: &Option>>, + query: &[f32], + best: &mut (f32, Option), + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result<()> { + Ok(()) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs b/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs new file mode 100644 index 0000000..4bdaec5 --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/commercial.rs @@ -0,0 +1,468 @@ +/*! +Copyright 2024 bw-dev36 + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + } + sum +} + +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + while i + 3 < a.len() { + let diff0 = a[i] - b[i]; + let diff1 = a[i + 1] - b[i + 1]; + let diff2 = a[i + 2] - b[i + 2]; + let diff3 = a[i + 3] - b[i + 3]; + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + + if sum > current_min { + return f32::MAX; + } + + i += 4; + } + + while i < a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + + if sum > current_min { + return f32::MAX; + } + + i += 1; + } + + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + if sqr_diff < best.0 { + if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + + if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { + stack.push((nearer_node.as_ref(), depth + 1)); + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + pub const KERNEL: Option = Some(CudaKernel { + src: r#" + + extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_vectors) { + float dist = 0.0; + for (int d = 0; d < num_dimensions; ++d) { + float diff = query_mean[d] - vectors[idx * num_dimensions + d]; + dist += diff * diff; + } + distances[idx] = dist; + } + } + + "#, + + funcs: &["filter_vectors"], + }); + + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = cuda_filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + dev, + funcs, + )?; + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + + + + Ok(Some(Solution { + indexes: best_indexes, + })) + } + + #[cfg(feature = "cuda")] + fn cuda_filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let num_vectors = database.len(); + let num_dimensions = 250; + let flattened_database: Vec = database.iter().flatten().cloned().collect(); + let database_dev = dev.htod_sync_copy(&flattened_database)?; + let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; + let mut distances_dev = dev.alloc_zeros::(num_vectors)?; + let cfg = LaunchConfig { + block_dim: (256, 1, 1), + grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs.remove("filter_vectors").unwrap().launch( + cfg, + ( + &mean_query_dev, + &database_dev, + &mut distances_dev, + num_vectors as i32, + num_dimensions as i32, + ), + ) + }?; + let mut distances_host = vec![0.0f32; num_vectors]; + dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, &distance) in distances_host.iter().enumerate() { + let ord_dist = FloatOrd(distance); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if distance < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&[f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + Ok(result) + } + + #[cfg(feature = "cuda")] + fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> Option>> { + None + } + + #[cfg(feature = "cuda")] + fn cuda_nearest_neighbor_search( + kd_tree: &Option>>, + query: &[f32], + best: &mut (f32, Option), + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result<()> { + Ok(()) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs new file mode 100644 index 0000000..23ae821 --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/inbound.rs @@ -0,0 +1,468 @@ +/*! +Copyright 2024 bw-dev36 + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + } + sum +} + +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + while i + 3 < a.len() { + let diff0 = a[i] - b[i]; + let diff1 = a[i + 1] - b[i + 1]; + let diff2 = a[i + 2] - b[i + 2]; + let diff3 = a[i + 3] - b[i + 3]; + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + + if sum > current_min { + return f32::MAX; + } + + i += 4; + } + + while i < a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + + if sum > current_min { + return f32::MAX; + } + + i += 1; + } + + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + if sqr_diff < best.0 { + if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + + if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { + stack.push((nearer_node.as_ref(), depth + 1)); + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + pub const KERNEL: Option = Some(CudaKernel { + src: r#" + + extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_vectors) { + float dist = 0.0; + for (int d = 0; d < num_dimensions; ++d) { + float diff = query_mean[d] - vectors[idx * num_dimensions + d]; + dist += diff * diff; + } + distances[idx] = dist; + } + } + + "#, + + funcs: &["filter_vectors"], + }); + + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = cuda_filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + dev, + funcs, + )?; + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + + + + Ok(Some(Solution { + indexes: best_indexes, + })) + } + + #[cfg(feature = "cuda")] + fn cuda_filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let num_vectors = database.len(); + let num_dimensions = 250; + let flattened_database: Vec = database.iter().flatten().cloned().collect(); + let database_dev = dev.htod_sync_copy(&flattened_database)?; + let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; + let mut distances_dev = dev.alloc_zeros::(num_vectors)?; + let cfg = LaunchConfig { + block_dim: (256, 1, 1), + grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs.remove("filter_vectors").unwrap().launch( + cfg, + ( + &mean_query_dev, + &database_dev, + &mut distances_dev, + num_vectors as i32, + num_dimensions as i32, + ), + ) + }?; + let mut distances_host = vec![0.0f32; num_vectors]; + dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, &distance) in distances_host.iter().enumerate() { + let ord_dist = FloatOrd(distance); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if distance < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&[f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + Ok(result) + } + + #[cfg(feature = "cuda")] + fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> Option>> { + None + } + + #[cfg(feature = "cuda")] + fn cuda_nearest_neighbor_search( + kd_tree: &Option>>, + query: &[f32], + best: &mut (f32, Option), + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result<()> { + Ok(()) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs b/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs new file mode 100644 index 0000000..dc1c7e1 --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/innovator_outbound.rs @@ -0,0 +1,468 @@ +/*! +Copyright 2024 bw-dev36 + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + } + sum +} + +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + while i + 3 < a.len() { + let diff0 = a[i] - b[i]; + let diff1 = a[i + 1] - b[i + 1]; + let diff2 = a[i + 2] - b[i + 2]; + let diff3 = a[i + 3] - b[i + 3]; + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + + if sum > current_min { + return f32::MAX; + } + + i += 4; + } + + while i < a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + + if sum > current_min { + return f32::MAX; + } + + i += 1; + } + + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + if sqr_diff < best.0 { + if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + + if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { + stack.push((nearer_node.as_ref(), depth + 1)); + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + pub const KERNEL: Option = Some(CudaKernel { + src: r#" + + extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_vectors) { + float dist = 0.0; + for (int d = 0; d < num_dimensions; ++d) { + float diff = query_mean[d] - vectors[idx * num_dimensions + d]; + dist += diff * diff; + } + distances[idx] = dist; + } + } + + "#, + + funcs: &["filter_vectors"], + }); + + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = cuda_filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + dev, + funcs, + )?; + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + + + + Ok(Some(Solution { + indexes: best_indexes, + })) + } + + #[cfg(feature = "cuda")] + fn cuda_filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let num_vectors = database.len(); + let num_dimensions = 250; + let flattened_database: Vec = database.iter().flatten().cloned().collect(); + let database_dev = dev.htod_sync_copy(&flattened_database)?; + let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; + let mut distances_dev = dev.alloc_zeros::(num_vectors)?; + let cfg = LaunchConfig { + block_dim: (256, 1, 1), + grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs.remove("filter_vectors").unwrap().launch( + cfg, + ( + &mean_query_dev, + &database_dev, + &mut distances_dev, + num_vectors as i32, + num_dimensions as i32, + ), + ) + }?; + let mut distances_host = vec![0.0f32; num_vectors]; + dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, &distance) in distances_host.iter().enumerate() { + let ord_dist = FloatOrd(distance); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if distance < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&[f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + Ok(result) + } + + #[cfg(feature = "cuda")] + fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> Option>> { + None + } + + #[cfg(feature = "cuda")] + fn cuda_nearest_neighbor_search( + kd_tree: &Option>>, + query: &[f32], + best: &mut (f32, Option), + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result<()> { + Ok(()) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/optimax_gpu/mod.rs b/tig-algorithms/src/vector_search/optimax_gpu/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs b/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs new file mode 100644 index 0000000..37097ab --- /dev/null +++ b/tig-algorithms/src/vector_search/optimax_gpu/open_data.rs @@ -0,0 +1,468 @@ +/*! +Copyright 2024 bw-dev36 + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use anyhow::Ok; +use tig_challenges::vector_search::*; +use std::cmp::Ordering; +use std::collections::BinaryHeap; + +struct KDNode<'a> { + point: &'a [f32], + left: Option>>, + right: Option>>, + index: usize, +} + +impl<'a> KDNode<'a> { + fn new(point: &'a [f32], index: usize) -> Self { + KDNode { + point, + left: None, + right: None, + index, + } + } +} +fn quickselect_by(arr: &mut [(&[f32], usize)], k: usize, compare: &F) +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + if arr.len() <= 1 { + return; + } + + let pivot_index = partition(arr, compare); + if k < pivot_index { + quickselect_by(&mut arr[..pivot_index], k, compare); + } else if k > pivot_index { + quickselect_by(&mut arr[pivot_index + 1..], k - pivot_index - 1, compare); + } +} + +fn partition(arr: &mut [(&[f32], usize)], compare: &F) -> usize +where + F: Fn(&(&[f32], usize), &(&[f32], usize)) -> Ordering, +{ + let pivot_index = arr.len() >> 1; + arr.swap(pivot_index, arr.len() - 1); + + let mut store_index = 0; + for i in 0..arr.len() - 1 { + if compare(&arr[i], &arr[arr.len() - 1]) == Ordering::Less { + arr.swap(i, store_index); + store_index += 1; + } + } + arr.swap(store_index, arr.len() - 1); + store_index +} + +fn build_kd_tree<'a>(points: &mut [(&'a [f32], usize)]) -> Option>> { + if points.is_empty() { + return None; + } + + const NUM_DIMENSIONS: usize = 250; + let mut stack: Vec<(usize, usize, usize, Option<*mut KDNode<'a>>, bool)> = Vec::new(); + let mut root: Option>> = None; + + stack.push((0, points.len(), 0, None, false)); + + while let Some((start, end, depth, parent_ptr, is_left)) = stack.pop() { + if start >= end { + continue; + } + + let axis = depth % NUM_DIMENSIONS; + let median = (start + end) / 2; + quickselect_by(&mut points[start..end], median - start, &|a, b| { + a.0[axis].partial_cmp(&b.0[axis]).unwrap() + }); + + let (median_point, median_index) = points[median]; + let mut new_node = Box::new(KDNode::new(median_point, median_index)); + let new_node_ptr: *mut KDNode = &mut *new_node; + + if let Some(parent_ptr) = parent_ptr { + unsafe { + if is_left { + (*parent_ptr).left = Some(new_node); + } else { + (*parent_ptr).right = Some(new_node); + } + } + } else { + root = Some(new_node); + } + + stack.push((median + 1, end, depth + 1, Some(new_node_ptr), false)); + stack.push((start, median, depth + 1, Some(new_node_ptr), true)); + } + + root +} + +#[inline(always)] +fn squared_euclidean_distance(a: &[f32], b: &[f32]) -> f32 { + let mut sum = 0.0; + for i in 0..a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + } + sum +} + +#[inline(always)] +fn early_stopping_distance(a: &[f32], b: &[f32], current_min: f32) -> f32 { + let mut sum = 0.0; + let mut i = 0; + while i + 3 < a.len() { + let diff0 = a[i] - b[i]; + let diff1 = a[i + 1] - b[i + 1]; + let diff2 = a[i + 2] - b[i + 2]; + let diff3 = a[i + 3] - b[i + 3]; + + sum += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; + + if sum > current_min { + return f32::MAX; + } + + i += 4; + } + + while i < a.len() { + let diff = a[i] - b[i]; + sum += diff * diff; + + if sum > current_min { + return f32::MAX; + } + + i += 1; + } + + sum +} + +fn nearest_neighbor_search<'a>( + root: &Option>>, + target: &[f32], + best: &mut (f32, Option), +) { + let num_dimensions = target.len(); + let mut stack = Vec::with_capacity(64); + + if let Some(node) = root { + stack.push((node.as_ref(), 0)); + } + + while let Some((node, depth)) = stack.pop() { + let axis = depth % num_dimensions; + let dist = early_stopping_distance(&node.point, target, best.0); + + if dist < best.0 { + best.0 = dist; + best.1 = Some(node.index); + } + + let diff = target[axis] - node.point[axis]; + let sqr_diff = diff * diff; + + if sqr_diff < best.0 { + if let Some(farther_node) = if diff < 0.0 { &node.right } else { &node.left } { + stack.push((farther_node.as_ref(), depth + 1)); + } + } + + if let Some(nearer_node) = if diff < 0.0 { &node.left } else { &node.right } { + stack.push((nearer_node.as_ref(), depth + 1)); + } + } +} + +fn calculate_mean_vector(vectors: &[&[f32]]) -> Vec { + let num_vectors = vectors.len(); + let num_dimensions = 250; + + let mut mean_vector = vec![0.0; num_dimensions]; + + for vector in vectors { + for i in 0..num_dimensions { + mean_vector[i] += vector[i]; + } + } + + for i in 0..num_dimensions { + mean_vector[i] /= num_vectors as f32; + } + + mean_vector +} + +#[derive(Debug)] +struct FloatOrd(f32); + +impl PartialEq for FloatOrd { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for FloatOrd {} + +impl PartialOrd for FloatOrd { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for FloatOrd { + fn cmp(&self, other: &Self) -> Ordering { + + self.partial_cmp(other).unwrap_or(Ordering::Equal) + } +} + +fn filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, +) -> Vec<(&'a [f32], usize)> { + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, vector) in database.iter().enumerate() { + let dist = squared_euclidean_distance(&mean_query_vector, vector); + let ord_dist = FloatOrd(dist); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if dist < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&'a [f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + result +} + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + ); + + + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + Ok(Some(Solution { + indexes: best_indexes, + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + pub const KERNEL: Option = Some(CudaKernel { + src: r#" + + extern "C" __global__ void filter_vectors(float* query_mean, float* vectors, float* distances, int num_vectors, int num_dimensions) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < num_vectors) { + float dist = 0.0; + for (int d = 0; d < num_dimensions; ++d) { + float diff = query_mean[d] - vectors[idx * num_dimensions + d]; + dist += diff * diff; + } + distances[idx] = dist; + } + } + + "#, + + funcs: &["filter_vectors"], + }); + + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + let query_count = challenge.query_vectors.len(); + + let subset_size = match query_count { + 10..=19 if challenge.difficulty.better_than_baseline <= 470 => 4200, + 10..=19 if challenge.difficulty.better_than_baseline > 470 => 4200, + 20..=28 if challenge.difficulty.better_than_baseline <= 465 => 3000, + 20..=28 if challenge.difficulty.better_than_baseline > 465 => 6000, // need more fuel + 29..=50 if challenge.difficulty.better_than_baseline <= 480 => 2000, + 29..=45 if challenge.difficulty.better_than_baseline > 480 => 6000, + 46..=50 if challenge.difficulty.better_than_baseline > 480 => 5000, // need more fuel + 51..=70 if challenge.difficulty.better_than_baseline <= 480 => 3000, + 51..=70 if challenge.difficulty.better_than_baseline > 480 => 3000, // need more fuel + 71..=100 if challenge.difficulty.better_than_baseline <= 480 => 1500, + 71..=100 if challenge.difficulty.better_than_baseline > 480 => 2500, // need more fuel + _ => 1000, // need more fuel + }; + let subset = cuda_filter_relevant_vectors( + &challenge.vector_database, + &challenge.query_vectors, + subset_size, + dev, + funcs, + )?; + let kd_tree = build_kd_tree(&mut subset.clone()); + + + let mut best_indexes = Vec::with_capacity(challenge.query_vectors.len()); + + for query in challenge.query_vectors.iter() { + let mut best = (std::f32::MAX, None); + nearest_neighbor_search(&kd_tree, query, &mut best); + + if let Some(best_index) = best.1 { + best_indexes.push(best_index); + } + } + + + + + + Ok(Some(Solution { + indexes: best_indexes, + })) + } + + #[cfg(feature = "cuda")] + fn cuda_filter_relevant_vectors<'a>( + database: &'a [Vec], + query_vectors: &[Vec], + k: usize, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + + let query_refs: Vec<&[f32]> = query_vectors.iter().map(|v| &v[..]).collect(); + let mean_query_vector = calculate_mean_vector(&query_refs); + + let num_vectors = database.len(); + let num_dimensions = 250; + let flattened_database: Vec = database.iter().flatten().cloned().collect(); + let database_dev = dev.htod_sync_copy(&flattened_database)?; + let mean_query_dev = dev.htod_sync_copy(&mean_query_vector)?; + let mut distances_dev = dev.alloc_zeros::(num_vectors)?; + let cfg = LaunchConfig { + block_dim: (256, 1, 1), + grid_dim: ((num_vectors as u32 + 255) / 256, 1, 1), + shared_mem_bytes: 0, + }; + unsafe { + funcs.remove("filter_vectors").unwrap().launch( + cfg, + ( + &mean_query_dev, + &database_dev, + &mut distances_dev, + num_vectors as i32, + num_dimensions as i32, + ), + ) + }?; + let mut distances_host = vec![0.0f32; num_vectors]; + dev.dtoh_sync_copy_into(&distances_dev, &mut distances_host)?; + let mut heap: BinaryHeap<(FloatOrd, usize)> = BinaryHeap::with_capacity(k); + + for (index, &distance) in distances_host.iter().enumerate() { + let ord_dist = FloatOrd(distance); + if heap.len() < k { + heap.push((ord_dist, index)); + } else if let Some(&(FloatOrd(top_dist), _)) = heap.peek() { + if distance < top_dist { + heap.pop(); + heap.push((ord_dist, index)); + } + } + } + let result: Vec<(&[f32], usize)> = heap + .into_iter() + .map(|(_, index)| (&database[index][..], index)) + .collect(); + + Ok(result) + } + + #[cfg(feature = "cuda")] + fn cuda_build_kd_tree<'a>(subset: &mut [(&'a [f32], usize)], + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> Option>> { + None + } + + #[cfg(feature = "cuda")] + fn cuda_nearest_neighbor_search( + kd_tree: &Option>>, + query: &[f32], + best: &mut (f32, Option), + dev: &Arc, + funcs: &mut HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result<()> { + Ok(()) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vector_search/template.rs b/tig-algorithms/src/vector_search/template.rs index 0f5fa1e..eddf8a0 100644 --- a/tig-algorithms/src/vector_search/template.rs +++ b/tig-algorithms/src/vector_search/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::vector_search::{Challenge, Solution}; diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs new file mode 100644 index 0000000..cb49161 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/benchmarker_outbound.rs @@ -0,0 +1,130 @@ +/*! +Copyright 2024 OvErLoDe + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); +for i in 1..n { + let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups + for j in (i + 1)..n { + let score = d_i0 + d[0][j] - d[i][j]; + scores.push((score, i, j)); + } +} + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + // Directly get the routes + let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); + + // Cache indices and demands + let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); + let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + // Check constraints + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + // Merge routes + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // Reverse if needed + if left_startnode == i { + left_route.reverse(); + } + if right_endnode == j { + right_route.reverse(); + } + + // Create new route + let mut new_route = left_route; + new_route.extend(right_route); + + // Update routes and demands + let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); + routes[start] = Some(new_route.clone()); + routes[end] = Some(new_route); + route_demands[start] = merged_demand; + route_demands[end] = merged_demand; + } + + let mut final_routes = Vec::new(); + + for (i, opt_route) in routes.into_iter().enumerate() { + if let Some(mut route) = opt_route { + if route[0] == i { + let mut full_route = Vec::with_capacity(route.len() + 2); + full_route.push(0); + full_route.append(&mut route); + full_route.push(0); + final_routes.push(full_route); + } + } + } + + Ok(Some(Solution { routes: final_routes })) + +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/commercial.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/commercial.rs new file mode 100644 index 0000000..257991d --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/commercial.rs @@ -0,0 +1,130 @@ +/*! +Copyright 2024 OvErLoDe + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); +for i in 1..n { + let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups + for j in (i + 1)..n { + let score = d_i0 + d[0][j] - d[i][j]; + scores.push((score, i, j)); + } +} + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + // Directly get the routes + let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); + + // Cache indices and demands + let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); + let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + // Check constraints + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + // Merge routes + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // Reverse if needed + if left_startnode == i { + left_route.reverse(); + } + if right_endnode == j { + right_route.reverse(); + } + + // Create new route + let mut new_route = left_route; + new_route.extend(right_route); + + // Update routes and demands + let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); + routes[start] = Some(new_route.clone()); + routes[end] = Some(new_route); + route_demands[start] = merged_demand; + route_demands[end] = merged_demand; + } + + let mut final_routes = Vec::new(); + + for (i, opt_route) in routes.into_iter().enumerate() { + if let Some(mut route) = opt_route { + if route[0] == i { + let mut full_route = Vec::with_capacity(route.len() + 2); + full_route.push(0); + full_route.append(&mut route); + full_route.push(0); + final_routes.push(full_route); + } + } + } + + Ok(Some(Solution { routes: final_routes })) + +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/inbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/inbound.rs new file mode 100644 index 0000000..0523dd0 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/inbound.rs @@ -0,0 +1,130 @@ +/*! +Copyright 2024 OvErLoDe + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); +for i in 1..n { + let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups + for j in (i + 1)..n { + let score = d_i0 + d[0][j] - d[i][j]; + scores.push((score, i, j)); + } +} + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + // Directly get the routes + let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); + + // Cache indices and demands + let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); + let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + // Check constraints + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + // Merge routes + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // Reverse if needed + if left_startnode == i { + left_route.reverse(); + } + if right_endnode == j { + right_route.reverse(); + } + + // Create new route + let mut new_route = left_route; + new_route.extend(right_route); + + // Update routes and demands + let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); + routes[start] = Some(new_route.clone()); + routes[end] = Some(new_route); + route_demands[start] = merged_demand; + route_demands[end] = merged_demand; + } + + let mut final_routes = Vec::new(); + + for (i, opt_route) in routes.into_iter().enumerate() { + if let Some(mut route) = opt_route { + if route[0] == i { + let mut full_route = Vec::with_capacity(route.len() + 2); + full_route.push(0); + full_route.append(&mut route); + full_route.push(0); + final_routes.push(full_route); + } + } + } + + Ok(Some(Solution { routes: final_routes })) + +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/innovator_outbound.rs new file mode 100644 index 0000000..c87bd18 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/innovator_outbound.rs @@ -0,0 +1,130 @@ +/*! +Copyright 2024 OvErLoDe + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); +for i in 1..n { + let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups + for j in (i + 1)..n { + let score = d_i0 + d[0][j] - d[i][j]; + scores.push((score, i, j)); + } +} + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + // Directly get the routes + let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); + + // Cache indices and demands + let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); + let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + // Check constraints + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + // Merge routes + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // Reverse if needed + if left_startnode == i { + left_route.reverse(); + } + if right_endnode == j { + right_route.reverse(); + } + + // Create new route + let mut new_route = left_route; + new_route.extend(right_route); + + // Update routes and demands + let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); + routes[start] = Some(new_route.clone()); + routes[end] = Some(new_route); + route_demands[start] = merged_demand; + route_demands[end] = merged_demand; + } + + let mut final_routes = Vec::new(); + + for (i, opt_route) in routes.into_iter().enumerate() { + if let Some(mut route) = opt_route { + if route[0] == i { + let mut full_route = Vec::with_capacity(route.len() + 2); + full_route.push(0); + full_route.append(&mut route); + full_route.push(0); + final_routes.push(full_route); + } + } + } + + Ok(Some(Solution { routes: final_routes })) + +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/mod.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs b/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs new file mode 100644 index 0000000..5d71dbb --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/clarke_wright_super/open_data.rs @@ -0,0 +1,130 @@ +/*! +Copyright 2024 OvErLoDe + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n * (n - 1)) / 2); +for i in 1..n { + let d_i0 = d[i][0]; // Cache this value to avoid repeated lookups + for j in (i + 1)..n { + let score = d_i0 + d[0][j] - d[i][j]; + scores.push((score, i, j)); + } +} + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); // Sort in descending order by score + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + // Directly get the routes + let (left_route, right_route) = (routes[i].as_ref().unwrap(), routes[j].as_ref().unwrap()); + + // Cache indices and demands + let (left_startnode, left_endnode) = (left_route[0], *left_route.last().unwrap()); + let (right_startnode, right_endnode) = (right_route[0], *right_route.last().unwrap()); + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + // Check constraints + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + // Merge routes + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // Reverse if needed + if left_startnode == i { + left_route.reverse(); + } + if right_endnode == j { + right_route.reverse(); + } + + // Create new route + let mut new_route = left_route; + new_route.extend(right_route); + + // Update routes and demands + let (start, end) = (*new_route.first().unwrap(), *new_route.last().unwrap()); + routes[start] = Some(new_route.clone()); + routes[end] = Some(new_route); + route_demands[start] = merged_demand; + route_demands[end] = merged_demand; + } + + let mut final_routes = Vec::new(); + + for (i, opt_route) in routes.into_iter().enumerate() { + if let Some(mut route) = opt_route { + if route[0] == i { + let mut full_route = Vec::with_capacity(route.len() + 2); + full_route.push(0); + full_route.append(&mut route); + full_route.push(0); + final_routes.push(full_route); + } + } + } + + Ok(Some(Solution { routes: final_routes })) + +} +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/benchmarker_outbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/benchmarker_outbound.rs new file mode 100644 index 0000000..5ddaecf --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/benchmarker_outbound.rs @@ -0,0 +1,133 @@ +/*! +Copyright 2024 Just + +Licensed under the TIG Benchmarker Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.max_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(Solution { + routes + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/commercial.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/commercial.rs new file mode 100644 index 0000000..334851d --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/commercial.rs @@ -0,0 +1,133 @@ +/*! +Copyright 2024 Just + +Licensed under the TIG Commercial License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.max_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(Solution { + routes + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/inbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/inbound.rs new file mode 100644 index 0000000..7dc1630 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/inbound.rs @@ -0,0 +1,133 @@ +/*! +Copyright 2024 Just + +Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +version (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.max_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(Solution { + routes + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/innovator_outbound.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/innovator_outbound.rs new file mode 100644 index 0000000..3087299 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/innovator_outbound.rs @@ -0,0 +1,133 @@ +/*! +Copyright 2024 Just + +Licensed under the TIG Innovator Outbound Game License v1.0 (the "License"); you +may not use this file except in compliance with the License. You may obtain a copy +of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.max_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(Solution { + routes + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs new file mode 100644 index 0000000..fcec967 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs @@ -0,0 +1,4 @@ +mod benchmarker_outbound; +pub use benchmarker_outbound::solve_challenge; +#[cfg(feature = "cuda")] +pub use benchmarker_outbound::{cuda_solve_challenge, KERNEL}; \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs new file mode 100644 index 0000000..d2545b8 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/open_data.rs @@ -0,0 +1,133 @@ +/*! +Copyright 2024 Just + +Licensed under the TIG Open Data License v1.0 or (at your option) any later version +(the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the specific +language governing permissions and limitations under the License. +*/ + +use tig_challenges::vehicle_routing::*; + +pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.difficulty.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.max_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(Solution { + routes + })) +} + +#[cfg(feature = "cuda")] +mod gpu_optimisation { + use super::*; + use cudarc::driver::*; + use std::{collections::HashMap, sync::Arc}; + use tig_challenges::CudaKernel; + + // set KERNEL to None if algorithm only has a CPU implementation + pub const KERNEL: Option = None; + + // Important! your GPU and CPU version of the algorithm should return the same result + pub fn cuda_solve_challenge( + challenge: &Challenge, + dev: &Arc, + mut funcs: HashMap<&'static str, CudaFunction>, + ) -> anyhow::Result> { + solve_challenge(challenge) + } +} +#[cfg(feature = "cuda")] +pub use gpu_optimisation::{cuda_solve_challenge, KERNEL}; diff --git a/tig-algorithms/src/vehicle_routing/mod.rs b/tig-algorithms/src/vehicle_routing/mod.rs index 8976e9b..4f9879e 100644 --- a/tig-algorithms/src/vehicle_routing/mod.rs +++ b/tig-algorithms/src/vehicle_routing/mod.rs @@ -67,9 +67,11 @@ pub use clarke_wright as c002_a001; // c002_a034 -// c002_a035 +pub mod cw_heuristic; +pub use cw_heuristic as c002_a035; -// c002_a036 +pub mod clarke_wright_super; +pub use clarke_wright_super as c002_a036; // c002_a037 diff --git a/tig-algorithms/src/vehicle_routing/template.rs b/tig-algorithms/src/vehicle_routing/template.rs index b5c872b..02a7cab 100644 --- a/tig-algorithms/src/vehicle_routing/template.rs +++ b/tig-algorithms/src/vehicle_routing/template.rs @@ -1,7 +1,11 @@ /*! -Copyright [yyyy] [name of copyright owner] +Copyright [year copyright work created] [name of copyright owner] -Licensed under the TIG Inbound Game License v1.0 or (at your option) any later +Identity of Submitter [name of person or entity that submits the Work to TIG] + +UAI [UAI (if applicable)] + +Licensed under the TIG Inbound Game License v2.0 or (at your option) any later version (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -13,6 +17,24 @@ CONDITIONS OF ANY KIND, either express or implied. See the License for the speci language governing permissions and limitations under the License. */ +// REMOVE BELOW SECTION IF UNUSED +/* +REFERENCES AND ACKNOWLEDGMENTS + +This implementation is based on or inspired by existing work. Citations and +acknowledgments below: + +1. Academic Papers: + - [Author(s), "Paper Title", DOI (if available)] + +2. Code References: + - [Author(s), URL] + +3. Other: + - [Author(s), Details] + +*/ + // TIG's UI uses the pattern `tig_challenges::` to automatically detect your algorithm's challenge use anyhow::{anyhow, Result}; use tig_challenges::vehicle_routing::{Challenge, Solution}; diff --git a/tig-algorithms/wasm/knapsack/knapmaxxing.wasm b/tig-algorithms/wasm/knapsack/knapmaxxing.wasm new file mode 100644 index 0000000..b638fd0 Binary files /dev/null and b/tig-algorithms/wasm/knapsack/knapmaxxing.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm b/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm new file mode 100644 index 0000000..55b1a25 Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/fast_walk_sat.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/inbound.wasm b/tig-algorithms/wasm/satisfiability/inbound.wasm new file mode 100644 index 0000000..ca5eb20 Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/inbound.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/sat_allocd.wasm b/tig-algorithms/wasm/satisfiability/sat_allocd.wasm new file mode 100644 index 0000000..38f9984 Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/sat_allocd.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/sat_global.wasm b/tig-algorithms/wasm/satisfiability/sat_global.wasm new file mode 100644 index 0000000..a36133b Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/sat_global.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/schnoing.wasm b/tig-algorithms/wasm/satisfiability/schnoing.wasm index 7350d82..4d57267 100644 Binary files a/tig-algorithms/wasm/satisfiability/schnoing.wasm and b/tig-algorithms/wasm/satisfiability/schnoing.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/sprint_sat.wasm b/tig-algorithms/wasm/satisfiability/sprint_sat.wasm new file mode 100644 index 0000000..38fb4ad Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/sprint_sat.wasm differ diff --git a/tig-algorithms/wasm/satisfiability/walk_sat.wasm b/tig-algorithms/wasm/satisfiability/walk_sat.wasm new file mode 100644 index 0000000..e0a7d8d Binary files /dev/null and b/tig-algorithms/wasm/satisfiability/walk_sat.wasm differ diff --git a/tig-algorithms/wasm/vector_search/basic.wasm b/tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm similarity index 77% rename from tig-algorithms/wasm/vector_search/basic.wasm rename to tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm index b31e559..9c34e15 100644 Binary files a/tig-algorithms/wasm/vector_search/basic.wasm and b/tig-algorithms/wasm/vector_search/brute_force_bacalhau.wasm differ diff --git a/tig-algorithms/wasm/vector_search/invector.wasm b/tig-algorithms/wasm/vector_search/invector.wasm new file mode 100644 index 0000000..87fc64c Binary files /dev/null and b/tig-algorithms/wasm/vector_search/invector.wasm differ diff --git a/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm b/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm new file mode 100644 index 0000000..929b53d Binary files /dev/null and b/tig-algorithms/wasm/vehicle_routing/clarke_wright_super.wasm differ diff --git a/tig-benchmarker/README.md b/tig-benchmarker/README.md index b8b4f9b..4285f64 100644 --- a/tig-benchmarker/README.md +++ b/tig-benchmarker/README.md @@ -1,133 +1,92 @@ # tig-benchmarker -test -Python scripts that implements a master/slave Benchmarker for TIG. +Benchmarker for TIG. Expected setup is a single master and multiple slaves on different servers. -# Getting Started +# Starting Your Master -1. Navigate to https://play.tig.foundation/benchmarker -2. Connect your wallet -3. Use the UI to Top-Up your Available Fee Balance by burning some TIG -4. Find your API key: - * Run the following command in the console: `JSON.parse(Cookies.get("account"))` - * `address` is your Mainnet `player_id` - * `api_key` is your Mainnet API key -5. Clone this repo +Simply run: + +``` +POSTGRES_USER=postgres \ +POSTGRES_PASSWORD=mysecretpassword \ +POSTGRES_DB=postgres \ +UI_PORT=80 \ +DB_PORT=5432 \ +MASTER_PORT=5115 \ +# set VERBOSE=1 for debug master logs +VERBOSE= \ +docker-compose up --build +``` + +See last section on how to find your player_id & api_key. + +**Notes:** +* Interaction with the master is via UI: `http://localhost` + * If your UI port is not 80, then your UI is accessed via `http://localhost:` + * If you are running on a server, then your UI is access via: `http://` + * Alternatively, you can [ssh port forward](https://www.ssh.com/academy/ssh/tunneling-example) +* The config of the master can be updated via the UI +* Recommend to run dockers in detached mode: `docker-compose up --detach` +* You can view the logs of each service individually: `docker-compose logs -f ` + * There are 4 services: `db`, `master`, `ui`, `nginx` +* To query the database, recommend to use [pgAdmin](https://www.pgadmin.org/) + +## Hard Resetting Your Master + +1. Kill the services: `docker-compose down` +2. Delete the database: `rm -rf db_data` +3. Start your master + +# Connecting Slaves + +1. Compile `tig-worker`: ``` - git clone https://github.com/tig-foundation/tig-monorepo.git - ``` -6. Compile `tig-worker` - ``` - cd tig-monorepo cargo build -p tig-worker --release ``` -7. Install python requirements +2. Run `slave.py`: ``` - # in tig-benchmarker folder - pip install -r requirements.txt + # assume you are in tig-benchmarker folder + python3 slave.py ../target/release/tig-worker ``` -8. Edit config.json with your `player_id` and `api_key` -9. Run a master - ``` - # in tig-benchmarker folder - python3 master.py - ``` -10. Connect at least 1 slave to your master - ``` - python3 slave.py - ``` - * If running locally, your master_ip should be 0.0.0.0 or localhost - * tig-worker binary should be located at `tig-monorepo/target/release/tig-worker` -# Benchmarking Process +**Notes:** +* If your master is on a different server to your slave, you need to add the option `--master ` +* To set the number of workers (threads), use the option `--workers ` +* To use a different port, use the option `--port ` +* To see all options, use `--help` -1. Master submits a precommit (benchmark settings + num_nonces) -2. Once precommit is confirmed, master creates a job and generates "batches" -3. Slaves poll for batches to work on, using `tig-worker` to output the batch merkle_root and nonces for which there are solutions -4. Slaves submit batch results back to master -5. Once all batches are complete, the master calculates the top merkle root, collates solution_nonces, and submits it as a Benchmark -6. When a benchmark is confirmed, master generates priority batches corresponding to the sampled nonces - * The master stores batch merkle_roots to minimise re-computation -7. Slaves working on a priority batch generate the merkle proof for sampled nonces -8. Slaves submit priority batch results back to master -9. Once all priority batches are complete, the master calculates the merkle proofs and submits it as a proof +## Hard Resetting Your Slave + +1. Stop your slave +2. Remove the output folder (defaults to results): `rm -rf results` +3. Start your slave # Optimising your Config -1. `difficulty_sampler_config` allows you to set the `difficulty_range` for each challenge. - * Every block, each challenge recalculates its `base_frontier` and `scaled_frontier` - * The difficulties within these 2 frontiers are "sorted" into easiest to hardest (0.0 is easiest, 1.0 is hardest) - * Benchmarkers can set the `difficulty_range` from which to sample a difficulty. Examples: +1. `difficulty_sampler_config` allows you to configure the difficulty your benchmarker selects for each challenge: + * `selected_difficulties` expects a list of difficulties. If any of those difficulties are within the valid range, they will be picked + * `difficulty_ranges` expects a range defined by 2 numbers: `[lower, higher]`. This is used to sample a random difficulty in the valid range. Examples: * `[0.0, 1.0]` samples the full range of valid difficulties * `[0.0, 0.1]` samples the easiest 10% of valid difficulties - * Key consideration: easier difficulties may result in more solutions given the same compute, but might not be a qualifier for long if the frontiers get harder + 2. `job_manager_config` allows you to set the `batch_size` for each challenge. * `batch_size` is the number of nonces that are part of a batch. Must be a power of 2 * Recommend to pick a `batch_size` for your slave with lowest `num_workers` such that it takes a few seconds to compute (e.g. 5 seconds) * `batch_size` shouldn't be too small, or else network latency between master and slave will affect performance - * To support slaves with different `num_workers`, see `slave_manager_config` below + * Use `slave_manager_config` to support slaves with different `num_workers` 3. `precommit_manager_config` allows you to control your benchmarks: - * `max_pending_benchmarks` is the maximum number of pending benchmarks - * Key consideration: you want batches to always be available for your slaves, but at the same time if you submit benchmarks too slowly, it will have large delays before it will be active - * `num_nonces` is the number of nonces to compute per benchmark. Recommend to adjust based on the logs which tells you the average number of nonces to find a solution. Example log: - * `global qualifier difficulty stats for vehicle_routing: (#nonces: 43739782840, #solutions: 22376, avg_nonces_per_solution: 1954763)` - * `weight` affects how likely the challenge will be picked (weight of 0 will never be picked). Recommend to adjust if the logs warns you to benchmark a specific challenge to increase your cutoff. Example log: - * `recommend finding more solutions for challenge knapsack to avoid being cut off` + * `max_pending_benchmarks` is the maximum number of pending benchmarks. You dont want benchmarks to take too long, nor do you want your slaves to be idle too much + + * `num_nonces` is the number of nonces to compute per benchmark. See Discord #community-tools channel for stats to help pick + + * `weight` affects how likely the challenge will be picked (weight of 0 will never be picked). Observe your cutoff and imbalance on the [benchmarker page](https://play.tig.foundation/benchmarker) 4. `slave_manager_config` allows you to control your slaves: - * When a slave makes a request, the manager iterates through each slave config one at a time until it finds a regex match. The most specific regexes should be earlier in the list, and the more general regexes should be latter in the list. - * `max_concurrent_batches` determines how many batches of that challenge a slave can fetch & process concurrently - * `max_concurrent_batches` also serves as a whitelist of challenges for that slave. If you don't want a slave to benchmark a specific challenge, remove its entry from the list. Example: - * `{"vector_search": 1}` means the slave will only be given `vector_search` batches - -# Master - -The master node is responsible for submitting precommits/benchmarks/proofs, generating batches for slaves to work on, and managing the overall benchmarking process. - -The current implementation expects only a single instance of `master.py` per `player_id`. - -The master does no benchmarking! You need to connect slaves - -## Usage - -``` -usage: master.py [-h] [--verbose] config_path - -TIG Benchmarker - -positional arguments: - config_path Path to the configuration JSON file - -options: - -h, --help show this help message and exit - --verbose Print debug logs -``` - -# Slave - -Slave nodes poll the master for batches to work on and process them using `tig-worker`. - -## Usage - -``` -usage: slave.py [-h] [--download DOWNLOAD] [--workers WORKERS] [--name NAME] [--port PORT] [--verbose] master_ip tig_worker_path - -TIG Slave Benchmarker - -positional arguments: - master_ip IP address of the master - tig_worker_path Path to tig-worker executable - -options: - -h, --help show this help message and exit - --download DOWNLOAD Folder to download WASMs to - --workers WORKERS Number of workers (default: 8) - --name NAME Name for the slave (default: randomly generated) - --port PORT Port for master (default: 5115) - --verbose Print debug logs -``` + * Slave names are matched to regexes (or else rejected). + * `max_concurrent_batches` is the max number of concurrent batches a slave can work on + * `selected_challenges` is a whitelist of challenges for that slave. If you don't want a slave to benchmark a specific challenge, remove its entry from the list # Finding your API Key diff --git a/tig-benchmarker/tig_benchmarker/__init__.py b/tig-benchmarker/common/__init__.py similarity index 100% rename from tig-benchmarker/tig_benchmarker/__init__.py rename to tig-benchmarker/common/__init__.py diff --git a/tig-benchmarker/common/common b/tig-benchmarker/common/common new file mode 120000 index 0000000..d66298e --- /dev/null +++ b/tig-benchmarker/common/common @@ -0,0 +1 @@ +common \ No newline at end of file diff --git a/tig-benchmarker/tig_benchmarker/merkle_tree.py b/tig-benchmarker/common/merkle_tree.py similarity index 98% rename from tig-benchmarker/tig_benchmarker/merkle_tree.py rename to tig-benchmarker/common/merkle_tree.py index 0d2a27a..202a4de 100644 --- a/tig-benchmarker/tig_benchmarker/merkle_tree.py +++ b/tig-benchmarker/common/merkle_tree.py @@ -1,6 +1,6 @@ from blake3 import blake3 from typing import List, Tuple -from tig_benchmarker.utils import FromStr, u8s_from_str +from .utils import FromStr, u8s_from_str class MerkleHash(FromStr): def __init__(self, value: bytes): diff --git a/tig-benchmarker/tig_benchmarker/structs.py b/tig-benchmarker/common/structs.py similarity index 96% rename from tig-benchmarker/tig_benchmarker/structs.py rename to tig-benchmarker/common/structs.py index ea0fbb7..c746f3d 100644 --- a/tig-benchmarker/tig_benchmarker/structs.py +++ b/tig-benchmarker/common/structs.py @@ -1,5 +1,5 @@ -from tig_benchmarker.merkle_tree import MerkleHash, MerkleBranch -from tig_benchmarker.utils import FromDict, u64s_from_str, u8s_from_str, jsonify, PreciseNumber +from .merkle_tree import MerkleHash, MerkleBranch +from .utils import FromDict, u64s_from_str, u8s_from_str, jsonify, PreciseNumber from dataclasses import dataclass, field from typing import Dict, List, Optional, Set, Any, Tuple @@ -19,8 +19,8 @@ class AlgorithmDetails(FromDict): class AlgorithmState(FromDict): block_confirmed: int round_submitted: int - round_pushed: int - round_active: int + round_pushed: Optional[int] + round_active: Optional[int] round_merged: Optional[int] banned: bool @@ -44,7 +44,7 @@ class BenchmarkSettings(FromDict): block_id: str challenge_id: str algorithm_id: str - difficulty: List[int] + difficulty: Point def calc_seed(self, rand_hash: str, nonce: int) -> bytes: return u8s_from_str(f"{jsonify(self)}_{rand_hash}_{nonce}") diff --git a/tig-benchmarker/tig_benchmarker/utils.py b/tig-benchmarker/common/utils.py similarity index 99% rename from tig-benchmarker/tig_benchmarker/utils.py rename to tig-benchmarker/common/utils.py index e95d156..becb696 100644 --- a/tig-benchmarker/tig_benchmarker/utils.py +++ b/tig-benchmarker/common/utils.py @@ -5,7 +5,7 @@ from dataclasses import dataclass, fields, is_dataclass, asdict from typing import TypeVar, Type, Dict, Any, List, Union, Optional, get_origin, get_args import json import time - + T = TypeVar('T', bound='DataclassBase') class FromStr(ABC): @@ -213,4 +213,5 @@ def u64s_from_str(input: str) -> List[int]: ] def now(): - return int(time.time() * 1000) \ No newline at end of file + return int(time.time() * 1000) + diff --git a/tig-benchmarker/config.json b/tig-benchmarker/config.json deleted file mode 100644 index a3097da..0000000 --- a/tig-benchmarker/config.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "player_id": "0x0000000000000000000000000000000000000000", - "api_key": "00000000000000000000000000000000", - "api_url": "https://mainnet-api.tig.foundation", - "difficulty_sampler_config": { - "difficulty_ranges": { - "satisfiability": [ - 0.0, - 0.5 - ], - "vehicle_routing": [ - 0.0, - 0.5 - ], - "knapsack": [ - 0.0, - 0.5 - ], - "vector_search": [ - 0.0, - 0.5 - ] - } - }, - "job_manager_config": { - "backup_folder": "jobs", - "batch_sizes": { - "satisfiability": 1024, - "vehicle_routing": 1024, - "knapsack": 1024, - "vector_search": 1024 - } - }, - "submissions_manager_config": { - "time_between_retries": 60000 - }, - "precommit_manager_config": { - "max_pending_benchmarks": 4, - "algo_selection": { - "satisfiability": { - "algorithm": "sat_global_opt", - "num_nonces": 1000, - "weight": 1.0, - "base_fee_limit": "10000000000000000" - }, - "vehicle_routing": { - "algorithm": "advanced_routing", - "num_nonces": 1000, - "weight": 1.0, - "base_fee_limit": "10000000000000000" - }, - "knapsack": { - "algorithm": "classic_quadkp", - "num_nonces": 1000, - "weight": 1.0, - "base_fee_limit": "10000000000000000" - }, - "vector_search": { - "algorithm": "invector_hybrid", - "num_nonces": 1000, - "weight": 1.0, - "base_fee_limit": "10000000000000000" - } - } - }, - "slave_manager_config": { - "port": 5115, - "time_before_batch_retry": 60000, - "slaves": [ - { - "name_regex": ".*", - "max_concurrent_batches": { - "satisfiability": 1, - "vehicle_routing": 1, - "knapsack": 1, - "vector_search": 1 - } - } - ] - } -} \ No newline at end of file diff --git a/tig-benchmarker/docker-compose.yml b/tig-benchmarker/docker-compose.yml new file mode 100644 index 0000000..74451ad --- /dev/null +++ b/tig-benchmarker/docker-compose.yml @@ -0,0 +1,72 @@ +services: + db: + build: + context: ./postgres + dockerfile: Dockerfile + container_name: db + restart: unless-stopped + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + volumes: + - ./db_data:/var/lib/postgresql/data + networks: + - tig-benchmarker + ports: + - ${DB_PORT}:5432 + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 5s + timeout: 5s + retries: 5 + + master: + build: + context: ./ + dockerfile: ./master/Dockerfile + environment: + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} + - POSTGRES_HOST=db + - VERBOSE=${VERBOSE} + networks: + - tig-benchmarker + ports: + - ${MASTER_PORT}:5115 + expose: + - 3336 + depends_on: + db: + condition: service_healthy + + ui: + build: + context: ./ui + dockerfile: Dockerfile + restart: unless-stopped + networks: + - tig-benchmarker + expose: + - 80 + depends_on: + - master + + nginx: + build: + context: ./nginx + dockerfile: Dockerfile + ports: + - "${UI_PORT}:80" + networks: + - tig-benchmarker + depends_on: + - ui + - master + +networks: + tig-benchmarker: + +volumes: + pgadmin-data: diff --git a/tig-benchmarker/master.py b/tig-benchmarker/master.py deleted file mode 100644 index 584a1dd..0000000 --- a/tig-benchmarker/master.py +++ /dev/null @@ -1,76 +0,0 @@ -import argparse -import asyncio -import json -import logging -import os -from tig_benchmarker.extensions.data_fetcher import * -from tig_benchmarker.extensions.difficulty_sampler import * -from tig_benchmarker.extensions.job_manager import * -from tig_benchmarker.extensions.precommit_manager import * -from tig_benchmarker.extensions.slave_manager import * -from tig_benchmarker.extensions.submissions_manager import * -from tig_benchmarker.utils import FromDict - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -@dataclass -class Config(FromDict): - player_id: str - api_key: str - api_url: str - difficulty_sampler_config: DifficultySamplerConfig - job_manager_config: JobManagerConfig - precommit_manager_config: PrecommitManagerConfig - slave_manager_config: SlaveManagerConfig - submissions_manager_config: SubmissionsManagerConfig - -async def main(config: Config): - last_block_id = None - jobs = [] - - data_fetcher = DataFetcher(config.api_url, config.player_id) - difficulty_sampler = DifficultySampler(config.difficulty_sampler_config) - job_manager = JobManager(config.job_manager_config, jobs) - precommit_manager = PrecommitManager(config.precommit_manager_config, config.player_id, jobs) - submissions_manager = SubmissionsManager(config.submissions_manager_config, config.api_url, config.api_key, jobs) - slave_manager = SlaveManager(config.slave_manager_config, jobs) - slave_manager.start() - - while True: - try: - data = await data_fetcher.run() - if data["block"].id != last_block_id: - last_block_id = data["block"].id - difficulty_sampler.on_new_block(**data) - job_manager.on_new_block(**data) - precommit_manager.on_new_block(**data) - job_manager.run() - samples = difficulty_sampler.run() - submit_precommit_req = precommit_manager.run(samples) - submissions_manager.run(submit_precommit_req) - except Exception as e: - import traceback - traceback.print_exc() - logger.error(f"{e}") - finally: - await asyncio.sleep(5) - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="TIG Benchmarker") - parser.add_argument("config_path", help="Path to the configuration JSON file") - parser.add_argument("--verbose", action='store_true', help="Print debug logs") - - args = parser.parse_args() - - logging.basicConfig( - format='%(levelname)s - [%(name)s] - %(message)s', - level=logging.DEBUG if args.verbose else logging.INFO - ) - - if not os.path.exists(args.config_path): - logger.error(f"config file not found at path: {args.config_path}") - sys.exit(1) - with open(args.config_path, "r") as f: - config = json.load(f) - config = Config.from_dict(config) - asyncio.run(main(config)) \ No newline at end of file diff --git a/tig-benchmarker/master/Dockerfile b/tig-benchmarker/master/Dockerfile new file mode 100644 index 0000000..7ba7bc6 --- /dev/null +++ b/tig-benchmarker/master/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.13-slim + +WORKDIR /app + +COPY master/requirements.txt requirements.txt + +RUN pip install --no-cache-dir -r requirements.txt + +COPY common common +COPY master/master master +COPY master/main.py main.py + +CMD ["python", "main.py"] diff --git a/tig-benchmarker/master/common b/tig-benchmarker/master/common new file mode 120000 index 0000000..60d3b0a --- /dev/null +++ b/tig-benchmarker/master/common @@ -0,0 +1 @@ +../common \ No newline at end of file diff --git a/tig-benchmarker/master/main.py b/tig-benchmarker/master/main.py new file mode 100644 index 0000000..f4189c9 --- /dev/null +++ b/tig-benchmarker/master/main.py @@ -0,0 +1,65 @@ +import argparse +import json +import logging +import os +import threading +import time +from master.data_fetcher import * +from master.difficulty_sampler import * +from master.job_manager import * +from master.precommit_manager import * +from master.slave_manager import * +from master.submissions_manager import * +from master.client_manager import * +from common.utils import FromDict + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +def main(): + last_block_id = None + + client_manager = ClientManager() + client_manager.start() + + data_fetcher = DataFetcher() + difficulty_sampler = DifficultySampler() + job_manager = JobManager() + precommit_manager = PrecommitManager() + submissions_manager = SubmissionsManager() + + slave_manager = SlaveManager() + slave_manager.start() + + while True: + try: + data = data_fetcher.run() + if data["block"].id != last_block_id: + last_block_id = data["block"].id + client_manager.on_new_block(**data) + difficulty_sampler.on_new_block(**data) + job_manager.on_new_block(**data) + submissions_manager.on_new_block(**data) + precommit_manager.on_new_block(**data) + job_manager.run() + samples = difficulty_sampler.run() + submit_precommit_req = precommit_manager.run(samples) + submissions_manager.run(submit_precommit_req) + slave_manager.run() + except Exception as e: + import traceback + traceback.print_exc() + logger.error(f"{e}") + finally: + time.sleep(5) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="TIG Benchmarker") + + args = parser.parse_args() + + logging.basicConfig( + format='%(levelname)s - [%(name)s] - %(message)s', + level=logging.DEBUG if os.environ.get("VERBOSE") else logging.INFO + ) + + main() \ No newline at end of file diff --git a/tig-benchmarker/tig_benchmarker/extensions/__init__.py b/tig-benchmarker/master/master/__init__.py similarity index 100% rename from tig-benchmarker/tig_benchmarker/extensions/__init__.py rename to tig-benchmarker/master/master/__init__.py diff --git a/tig-benchmarker/master/master/client_manager.py b/tig-benchmarker/master/master/client_manager.py new file mode 100644 index 0000000..f46667e --- /dev/null +++ b/tig-benchmarker/master/master/client_manager.py @@ -0,0 +1,224 @@ +import os +import logging +import signal +import threading +import uvicorn +import json +from datetime import datetime +from master.sql import db_conn +from fastapi import FastAPI, Query, Request, HTTPException, Depends, Header +from fastapi.responses import JSONResponse +from fastapi.middleware.cors import CORSMiddleware +from fastapi.middleware.gzip import GZipMiddleware +from fastapi import Request + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +CONFIG = {} + +class ClientManager: + def __init__(self): + logger.info("ClientManager initialized and connected to the database.") + + # Fetch initial config from database + result = db_conn.fetch_one( + """ + SELECT config FROM config + LIMIT 1 + """ + ) + CONFIG.update(result["config"]) + self.latest_data = {} + + def on_new_block(self, **kwargs): + def convert(d): + if isinstance(d, dict): + return {k: convert(v) for k, v in d.items()} + elif isinstance(d, list): + return [convert(v) for v in d] + elif hasattr(d, "to_dict"): + return d.to_dict() + else: + return d + self.latest_data = convert(kwargs) + + def setup_routes(self): + @self.app.get('/get-latest-data') + async def get_latest_data(): + return JSONResponse(content=self.latest_data) + + @self.app.get('/get-config') + async def get_config_endpoint(): + return JSONResponse(content=CONFIG) + + @self.app.get('/stop/{benchmark_id}') + async def stop_benchmark(benchmark_id: str): + try: + db_conn.execute( + """ + UPDATE job + SET stopped = true + WHERE benchmark_id = %s + """, + (benchmark_id,) + ) + return JSONResponse(content={"message": "Benchmark stopped successfully."}) + except Exception as e: + logger.error(f"Unexpected error on /stop/{benchmark_id}: {e}") + raise HTTPException(status_code=500, detail="Internal Server Error") + + @self.app.post("/update-config") + async def update_config(request: Request): + logger.debug("Received config update") + try: + new_config = await request.json() + new_config["player_id"] = new_config["player_id"].lower() + new_config["api_url"] = new_config["api_url"].rstrip('/') + + # Update config in database + db_conn.execute( + """ + DELETE FROM config; + INSERT INTO config (config) + VALUES (%s) + """, + (json.dumps(new_config),) + ) + logger.info(f"Config updated in database: {new_config}") + + CONFIG.update(new_config) + + return JSONResponse(content={"message": "Config updated successfully."}) + except Exception as e: + logger.error(f"Unexpected error on /update-config: {e}") + raise HTTPException(status_code=400, detail="Invalid configuration data") + + @self.app.get("/get-jobs") + async def get_jobs(): + result = db_conn.fetch_all( + """ + WITH recent_jobs AS ( + SELECT benchmark_id + FROM job + ORDER BY block_started DESC + LIMIT 100 + ), + recent_batches AS ( + SELECT + C.benchmark_id, + C.batch_idx, + JSONB_BUILD_OBJECT( + 'batch_idx', C.batch_idx, + 'slave', CASE + WHEN E.batch_idx IS NOT NULL THEN E.slave + ELSE C.slave + END, + 'num_nonces', LEAST( + B.batch_size, + B.num_nonces - B.batch_size * C.batch_idx + ), + 'num_solutions', JSONB_ARRAY_LENGTH(D.solution_nonces), + 'status', CASE + WHEN B.stopped IS NOT NULL THEN 'STOPPED' + WHEN E.batch_idx IS NOT NULL THEN ( + CASE + WHEN E.ready = true THEN 'PROOF READY' + WHEN E.start_time IS NOT NULL THEN 'COMPUTING PROOF' + ELSE 'PROOF NOT ASSIGNED' + END + ) + ELSE ( + CASE + WHEN C.ready = true THEN 'ROOT READY' + WHEN C.start_time IS NOT NULL THEN 'COMPUTING ROOT' + ELSE 'ROOT NOT ASSIGNED' + END + ) + END, + 'end_time', CASE + WHEN E.batch_idx IS NOT NULL THEN E.end_time + ELSE C.end_time + END, + 'start_time', CASE + WHEN E.batch_idx IS NOT NULL THEN E.start_time + ELSE C.start_time + END + ) AS batch_data + FROM recent_jobs A + INNER JOIN job B + ON A.benchmark_id = B.benchmark_id + INNER JOIN root_batch C + ON B.benchmark_id = C.benchmark_id + INNER JOIN batch_data D + ON C.benchmark_id = D.benchmark_id + AND C.batch_idx = D.batch_idx + LEFT JOIN proofs_batch E + ON C.benchmark_id = E.benchmark_id + AND C.batch_idx = E.batch_idx + ), + grouped_batches AS ( + SELECT + benchmark_id, + JSONB_AGG(batch_data ORDER BY batch_idx) AS batches, + SUM((batch_data->>'num_solutions')::INTEGER) AS num_solutions + FROM recent_batches + GROUP BY benchmark_id + ) + SELECT + B.benchmark_id, + B.challenge, + B.algorithm, + B.settings->'difficulty' AS difficulty, + B.batch_size, + B.num_nonces, + A.num_solutions, + CASE + WHEN B.end_time IS NOT NULL THEN 'COMPLETED' + WHEN B.stopped IS NOT NULL THEN 'STOPPED' + WHEN B.merkle_proofs_ready = true THEN 'SUBMITTING PROOF' + WHEN B.sampled_nonces IS NOT NULL AND B.merkle_root_ready THEN 'COMPUTING PROOF' + WHEN B.sampled_nonces IS NULL AND B.merkle_root_ready THEN 'SUBMITTING ROOT' + ELSE 'COMPUTING ROOT' + END AS status, + B.end_time, + B.start_time, + A.batches, + B.end_time IS NULL AND B.stopped IS NULL AS can_stop + FROM grouped_batches A + INNER JOIN job B + ON A.benchmark_id = B.benchmark_id + ORDER BY + B.block_started DESC, + B.benchmark_id DESC + """ + ) + + return JSONResponse( + content=[dict(row) for row in result], + status_code=200, + headers = {"Accept-Encoding": "gzip"} + ) + + def start(self): + def run(): + self.app = FastAPI() + self.app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + self.app.add_middleware( + GZipMiddleware, + minimum_size=1000, + compresslevel=5, + ) + + self.setup_routes() + + uvicorn.run(self.app, host="0.0.0.0", port=3336) + + server_thread = threading.Thread(target=run, daemon=True) + server_thread.start() + logger.info(f"ClientManager started on 0.0.0.0:3336") \ No newline at end of file diff --git a/tig-benchmarker/master/master/data_fetcher.py b/tig-benchmarker/master/master/data_fetcher.py new file mode 100644 index 0000000..82a6642 --- /dev/null +++ b/tig-benchmarker/master/master/data_fetcher.py @@ -0,0 +1,85 @@ +import requests +import json +import logging +import os +from common.structs import * +from common.utils import * +from typing import Dict, Any +from concurrent.futures import ThreadPoolExecutor +from master.client_manager import CONFIG + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +def _get(url: str) -> Dict[str, Any]: + logger.debug(f"Fetching from {url}") + resp = requests.get(url, timeout=10) # Added timeout for robustness + if resp.status_code == 200: + return json.loads(resp.text) + else: + if resp.headers.get("Content-Type") == "text/plain": + err_msg = f"status code {resp.status_code} from {url}: {resp.text}" + else: + err_msg = f"status code {resp.status_code} from {url}" + logger.error(err_msg) + raise Exception(err_msg) + +class DataFetcher: + def __init__(self): + self.last_fetch = 0 + self._cache = None + + def run(self) -> dict: + config = CONFIG + logger.debug("fetching latest block") + block_data = _get(f"{config['api_url']}/get-block") + block = Block.from_dict(block_data["block"]) + + if self._cache is not None and block.id == self._cache["block"].id: + logger.debug("no new block data") + return self._cache + + logger.info(f"new block @ height {block.details.height}, fetching data") + tasks = [ + f"{config['api_url']}/get-algorithms?block_id={block.id}", + f"{config['api_url']}/get-benchmarks?player_id={config['player_id']}&block_id={block.id}", + f"{config['api_url']}/get-challenges?block_id={block.id}" + ] + + with ThreadPoolExecutor(max_workers=4) as executor: # Defined max workers as there are 4 process to be executed in parallel. + algorithms_data, benchmarks_data, challenges_data = list(executor.map(_get, tasks)) + + algorithms = {a["id"]: Algorithm.from_dict(a) for a in algorithms_data["algorithms"]} + wasms = {w["algorithm_id"]: Binary.from_dict(w) for w in algorithms_data["binarys"]} + + precommits = {b["benchmark_id"]: Precommit.from_dict(b) for b in benchmarks_data["precommits"]} + benchmarks = {b["id"]: Benchmark.from_dict(b) for b in benchmarks_data["benchmarks"]} + proofs = {p["benchmark_id"]: Proof.from_dict(p) for p in benchmarks_data["proofs"]} + frauds = {f["benchmark_id"]: Fraud.from_dict(f) for f in benchmarks_data["frauds"]} + challenges = {c["id"]: Challenge.from_dict(c) for c in challenges_data["challenges"]} + + # Fetch difficulty data for each challenge + difficulty_urls = [ + f"{config['api_url']}/get-difficulty-data?block_id={block.id}&challenge_id={c_id}" + for c_id in challenges + ] + + with ThreadPoolExecutor(max_workers=4) as executor: + difficulty_responses = list(executor.map(_get, difficulty_urls)) + + difficulty_data = { + c_id: [DifficultyData.from_dict(d) for d in resp.get("data", [])] + for c_id, resp in zip(challenges, difficulty_responses) + } + + self._cache = { + "block": block, + "algorithms": algorithms, + "wasms": wasms, + "precommits": precommits, + "benchmarks": benchmarks, + "proofs": proofs, + "frauds": frauds, + "challenges": challenges, + "difficulty_data": difficulty_data + } + return self._cache diff --git a/tig-benchmarker/tig_benchmarker/extensions/difficulty_sampler.py b/tig-benchmarker/master/master/difficulty_sampler.py similarity index 75% rename from tig-benchmarker/tig_benchmarker/extensions/difficulty_sampler.py rename to tig-benchmarker/master/master/difficulty_sampler.py index 0cd7382..784c4bd 100644 --- a/tig-benchmarker/tig_benchmarker/extensions/difficulty_sampler.py +++ b/tig-benchmarker/master/master/difficulty_sampler.py @@ -1,11 +1,11 @@ -import asyncio import logging import math import numpy as np import os import random -from tig_benchmarker.structs import * +from common.structs import * from typing import List, Tuple, Dict +from master.client_manager import CONFIG logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) @@ -110,20 +110,11 @@ def calc_all_frontiers(points: List[Point]) -> List[Frontier]: frontiers.append(frontier) return frontiers -@dataclass -class DifficultySamplerConfig(FromDict): - difficulty_ranges: Dict[str, Tuple[float, float]] - - def __post_init__(self): - for c_name, (start, end) in self.difficulty_ranges.items(): - if start < 0 or start > 1 or end < 0 or end > 1 or start > end: - raise ValueError(f"Invalid difficulty range for challenge {c_name}. Must be (start, end) where '0 <= start <= end <= 1'") - class DifficultySampler: - def __init__(self, config: DifficultySamplerConfig): - self.config = config + def __init__(self): self.valid_difficulties = {} self.frontiers = {} + self.challenges = {} def on_new_block(self, challenges: Dict[str, Challenge], **kwargs): for c in challenges.values(): @@ -137,14 +128,38 @@ class DifficultySampler: self.valid_difficulties[c.details.name] = calc_valid_difficulties(list(upper_frontier), list(lower_frontier)) self.frontiers[c.details.name] = calc_all_frontiers(self.valid_difficulties[c.details.name]) + self.challenges = [c.details.name for c in challenges.values()] + def run(self) -> Dict[str, Point]: samples = {} - for c_name, frontiers in self.frontiers.items(): - difficulty_range = self.config.difficulty_ranges[c_name] # FIXME - idx1 = math.floor(difficulty_range[0] * (len(frontiers) - 1)) - idx2 = math.ceil(difficulty_range[1] * (len(frontiers) - 1)) - difficulties = [p for frontier in frontiers[idx1:idx2 + 1] for p in frontier] - difficulty = random.choice(difficulties) - samples[c_name] = difficulty - logger.debug(f"Sampled difficulty {difficulty} for challenge {c_name}") + config = CONFIG["difficulty_sampler_config"] + + for c_name in self.challenges: + found_valid = False + + if len(selected_difficulties := config["selected_difficulties"].get(c_name, [])) > 0: + valid_difficulties = set(tuple(d) for d in self.valid_difficulties[c_name]) + selected_difficulties = [tuple(d) for d in selected_difficulties] + selected_difficulties = [ + d for d in selected_difficulties if d in valid_difficulties + ] + + if len(selected_difficulties) > 0: + random.shuffle(selected_difficulties) + samples[c_name] = selected_difficulties[0] + logger.debug(f"Selected difficulty {samples[c_name]} for challenge {c_name}") + found_valid = True + else: + logger.debug(f"No valid difficulties found for {c_name} - skipping selected difficulties") + + if not found_valid: + frontiers = self.frontiers[c_name] + difficulty_range = config["difficulty_ranges"][c_name] + idx1 = math.floor(difficulty_range[0] * (len(frontiers) - 1)) + idx2 = math.ceil(difficulty_range[1] * (len(frontiers) - 1)) + difficulties = [p for frontier in frontiers[idx1:idx2 + 1] for p in frontier] + difficulty = random.choice(difficulties) + samples[c_name] = difficulty + logger.debug(f"Sampled difficulty {difficulty} for challenge {c_name}") + return samples \ No newline at end of file diff --git a/tig-benchmarker/master/master/job_manager.py b/tig-benchmarker/master/master/job_manager.py new file mode 100644 index 0000000..82bbc0a --- /dev/null +++ b/tig-benchmarker/master/master/job_manager.py @@ -0,0 +1,364 @@ +import os +import json +import logging +from common.merkle_tree import MerkleHash, MerkleBranch, MerkleTree +from common.structs import * +from common.utils import * +from typing import Dict, List, Optional, Set +from master.sql import db_conn +from master.client_manager import CONFIG +import math + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +class JobManager: + def __init__(self): + pass + + def on_new_block( + self, + block: Block, + precommits: Dict[str, Precommit], + benchmarks: Dict[str, Benchmark], + proofs: Dict[str, Proof], + challenges: Dict[str, Challenge], + algorithms: Dict[str, Algorithm], + wasms: Dict[str, Binary], + **kwargs + ): + config = CONFIG["job_manager_config"] + # create jobs from confirmed precommits + challenge_id_2_name = { + c.id: c.details.name + for c in challenges.values() + } + algorithm_id_2_name = { + a.id: a.details.name + for a in algorithms.values() + } + for benchmark_id, x in precommits.items(): + if ( + benchmark_id in proofs or + db_conn.fetch_one( # check if job is already created + """ + SELECT 1 + FROM job + WHERE benchmark_id = %s + """, + (benchmark_id,) + ) + ): + continue + + logger.info(f"creating job from confirmed precommit {benchmark_id}") + c_name = challenge_id_2_name[x.settings.challenge_id] + a_name = algorithm_id_2_name[x.settings.algorithm_id] + + wasm = wasms.get(x.settings.algorithm_id, None) + if wasm is None: + logger.error(f"no wasm found for algorithm_id {x.settings.algorithm_id}") + continue + if wasm.details.download_url is None: + logger.error(f"no download_url found for wasm {wasm.algorithm_id}") + continue + num_batches = math.ceil(x.details.num_nonces / config["batch_sizes"][c_name]) + atomic_inserts = [ + ( + """ + INSERT INTO job + ( + benchmark_id, + settings, + num_nonces, + num_batches, + rand_hash, + runtime_config, + batch_size, + challenge, + algorithm, + download_url, + block_started, + start_time + ) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT) + ON CONFLICT (benchmark_id) DO NOTHING; + """, + ( + benchmark_id, + json.dumps(asdict(x.settings)), + x.details.num_nonces, + num_batches, + x.details.rand_hash, + json.dumps(block.config["benchmarks"]["runtime_configs"]["wasm"]), + config["batch_sizes"][c_name], + c_name, + a_name, + wasm.details.download_url, + x.details.block_started + ) + ), + ( + """ + INSERT INTO job_data (benchmark_id) VALUES (%s) + ON CONFLICT (benchmark_id) DO NOTHING; + """, + (benchmark_id,) + ) + ] + + for batch_idx in range(num_batches): + atomic_inserts += [ + ( + """ + INSERT INTO root_batch (benchmark_id, batch_idx) VALUES (%s, %s) + """, + (benchmark_id, batch_idx) + ), + ( + """ + INSERT INTO batch_data (benchmark_id, batch_idx) VALUES (%s, %s) + """, + (benchmark_id, batch_idx) + ) + ] + + db_conn.execute_many(*atomic_inserts) + + + # update jobs from confirmed benchmarks + for benchmark_id, x in benchmarks.items(): + if ( + benchmark_id in proofs or + (result := db_conn.fetch_one( + """ + SELECT num_batches, batch_size + FROM job + WHERE benchmark_id = %s + AND sampled_nonces IS NULL + """, + (benchmark_id,) + )) is None + ): + continue + + logger.info(f"updating job from confirmed benchmark {benchmark_id}") + atomic_update = [ + ( + """ + UPDATE job + SET sampled_nonces = %s + WHERE benchmark_id = %s + """, + (json.dumps(x.details.sampled_nonces), benchmark_id) + ) + ] + + batch_sampled_nonces = {} + for nonce in x.details.sampled_nonces: + batch_idx = nonce // result["batch_size"] + batch_sampled_nonces.setdefault(batch_idx, []).append(nonce) + + for batch_idx, sampled_nonces in batch_sampled_nonces.items(): + atomic_update += [ + ( + """ + INSERT INTO proofs_batch (sampled_nonces, benchmark_id, batch_idx, slave, start_time) + SELECT %s, A.benchmark_id, A.batch_idx, A.slave, (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + FROM root_batch A + WHERE A.benchmark_id = %s AND A.batch_idx = %s + """, + (json.dumps(sampled_nonces), benchmark_id, batch_idx) + ) + ] + + db_conn.execute_many(*atomic_update) + + # update jobs from confirmed proofs + if len(proofs) > 0: + db_conn.execute( + """ + UPDATE job + SET end_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + WHERE end_time IS NULL + AND benchmark_id IN %s + """, + (tuple(proofs),) + ) + + # stop any expired jobs + db_conn.execute( + """ + UPDATE job + SET stopped = true + WHERE end_time IS NULL + AND stopped IS NULL + AND %s >= block_started + 120 + """, + (block.details.height,) + ) + + + def run(self): + now = int(time.time() * 1000) + + # Find jobs where all root_batchs are ready + rows = db_conn.fetch_all( + """ + WITH ready AS ( + SELECT A.benchmark_id + FROM root_batch A + INNER JOIN job B + ON B.merkle_root_ready IS NULL + AND A.benchmark_id = B.benchmark_id + GROUP BY A.benchmark_id + HAVING COUNT(*) = COUNT(A.ready) + ) + SELECT + B.benchmark_id, + JSONB_AGG(B.merkle_root ORDER BY B.batch_idx) AS batch_merkle_roots, + JSONB_AGG(B.solution_nonces) AS solution_nonces + FROM ready A + INNER JOIN batch_data B + ON A.benchmark_id = B.benchmark_id + GROUP BY B.benchmark_id + """ + ) + + # Calculate merkle roots for completed jobs + for row in rows: + solution_nonces = [x for y in row['solution_nonces'] for x in y] + + benchmark_id = row['benchmark_id'] + batch_merkle_roots = [MerkleHash.from_str(root) for root in row['batch_merkle_roots']] + num_batches = len(batch_merkle_roots) + + logger.info(f"job {benchmark_id}: (benchmark ready)") + + tree = MerkleTree( + batch_merkle_roots, + 1 << (num_batches - 1).bit_length() + ) + merkle_root = tree.calc_merkle_root() + + # Update the database with calculated merkle root + db_conn.execute_many(*[ + ( + """ + UPDATE job_data + SET merkle_root = %s, + solution_nonces = %s + WHERE benchmark_id = %s + """, + ( + merkle_root.to_str(), + json.dumps(solution_nonces), + benchmark_id + ) + ), + ( + """ + UPDATE job + SET merkle_root_ready = true + WHERE benchmark_id = %s + """, + (benchmark_id,) + ) + ]) + + # Find jobs where all proofs_batchs are ready + rows = db_conn.fetch_all( + """ + WITH ready AS ( + SELECT A.benchmark_id + FROM proofs_batch A + INNER JOIN job B + ON B.merkle_root_ready + AND B.merkle_proofs_ready IS NULL + AND A.benchmark_id = B.benchmark_id + GROUP BY A.benchmark_id + HAVING COUNT(*) = COUNT(A.ready) + ) + SELECT + A.benchmark_id, + JSONB_AGG(D.merkle_proofs ORDER BY D.batch_idx) AS batch_merkle_proofs, + B.batch_size, + B.num_batches + FROM ready A + INNER JOIN job B + ON A.benchmark_id = B.benchmark_id + INNER JOIN proofs_batch C + ON A.benchmark_id = C.benchmark_id + INNER JOIN batch_data D + ON C.benchmark_id = D.benchmark_id + AND C.batch_idx = D.batch_idx + GROUP BY + A.benchmark_id, + B.batch_size, + B.num_batches + """ + ) + + for row in rows: + benchmark_id = row["benchmark_id"] + batch_merkle_proofs = [ + MerkleProof.from_dict(x) + for y in row["batch_merkle_proofs"] + for x in y + ] + + batch_merkle_roots = db_conn.fetch_one( + """ + SELECT JSONB_AGG(merkle_root ORDER BY batch_idx) as batch_merkle_roots + FROM batch_data + WHERE benchmark_id = %s + """, + (benchmark_id,) + ) + + batch_merkle_roots = batch_merkle_roots["batch_merkle_roots"] + + logger.info(f"job {benchmark_id}: (proof ready)") + + depth_offset = (row["batch_size"] - 1).bit_length() + tree = MerkleTree( + [MerkleHash.from_str(root) for root in batch_merkle_roots], + 1 << (row["num_batches"] - 1).bit_length() + ) + + merkle_proofs = [] + for proof in batch_merkle_proofs: + batch_idx = proof.leaf.nonce // row["batch_size"] + upper_stems = [ + (d + depth_offset, h) + for d, h in tree.calc_merkle_branch(batch_idx).stems + ] + + merkle_proofs.append( + MerkleProof( + leaf=proof.leaf, + branch=MerkleBranch(proof.branch.stems + upper_stems) + ) + ) + + # Update database with calculated merkle proofs + db_conn.execute_many(*[ + ( + """ + UPDATE job_data + SET merkle_proofs = %s + WHERE benchmark_id = %s + """, + ( + json.dumps([x.to_dict() for x in merkle_proofs]), + benchmark_id + ) + ), + ( + """ + UPDATE job + SET merkle_proofs_ready = true + WHERE benchmark_id = %s + """, + (benchmark_id,) + ) + ]) diff --git a/tig-benchmarker/tig_benchmarker/extensions/precommit_manager.py b/tig-benchmarker/master/master/precommit_manager.py similarity index 77% rename from tig-benchmarker/tig_benchmarker/extensions/precommit_manager.py rename to tig-benchmarker/master/master/precommit_manager.py index c72d86f..1e3a58e 100644 --- a/tig-benchmarker/tig_benchmarker/extensions/precommit_manager.py +++ b/tig-benchmarker/master/master/precommit_manager.py @@ -1,13 +1,13 @@ -import asyncio import os import logging import random from dataclasses import dataclass -from tig_benchmarker.extensions.job_manager import Job -from tig_benchmarker.extensions.submissions_manager import SubmitPrecommitRequest -from tig_benchmarker.structs import * -from tig_benchmarker.utils import FromDict +from master.submissions_manager import SubmitPrecommitRequest +from common.structs import * +from common.utils import FromDict from typing import Dict, List, Optional, Set +from master.sql import db_conn +from master.client_manager import CONFIG logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) @@ -24,10 +24,7 @@ class PrecommitManagerConfig(FromDict): algo_selection: Dict[str, AlgorithmSelectionConfig] class PrecommitManager: - def __init__(self, config: PrecommitManagerConfig, player_id: str, jobs: List[Job]): - self.config = config - self.player_id = player_id - self.jobs = jobs + def __init__(self): self.last_block_id = None self.num_precommits_submitted = 0 self.algorithm_name_2_id = {} @@ -96,31 +93,42 @@ class PrecommitManager: logger.info(f"global qualifier difficulty stats for {challenges[c_id].details.name}: (#nonces: {x['nonces']}, #solutions: {x['solutions']}, avg_nonces_per_solution: {avg_nonces_per_solution})") def run(self, difficulty_samples: Dict[str, List[int]]) -> SubmitPrecommitRequest: - num_pending_benchmarks = sum(1 for job in self.jobs if job.merkle_root is None) + self.num_precommits_submitted - if num_pending_benchmarks >= self.config.max_pending_benchmarks: - logger.debug(f"number of pending benchmarks has reached max of {self.config.max_pending_benchmarks}") + num_pending_jobs = db_conn.fetch_one( + """ + SELECT COUNT(*) + FROM job + WHERE merkle_proofs_ready IS NULL + AND stopped IS NULL + """ + )["count"] + + config = CONFIG["precommit_manager_config"] + + num_pending_benchmarks = num_pending_jobs + self.num_precommits_submitted + if num_pending_benchmarks >= config["max_pending_benchmarks"]: + logger.debug(f"number of pending benchmarks has reached max of {config['max_pending_benchmarks']}") return selections = [ - (c_name, x) for c_name, x in self.config.algo_selection.items() - if self.curr_base_fees[c_name] <= x.base_fee_limit + (c_name, x) for c_name, x in config["algo_selection"].items() + #if self.curr_base_fees[c_name] <= x.base_fee_limit ] if len(selections) == 0: logger.warning("No challenges under base fee limit") return None - logger.debug(f"Selecting challenge from: {[(c_name, x.weight) for c_name, x in selections]}") - selection = random.choices(selections, weights=[x.weight for _, x in selections])[0] + logger.debug(f"Selecting challenge from: {[(c_name, x['weight']) for c_name, x in selections]}") + selection = random.choices(selections, weights=[x["weight"] for _, x in selections])[0] c_id = self.challenge_name_2_id[selection[0]] - a_id = self.algorithm_name_2_id[f"{selection[0]}_{selection[1].algorithm}"] + a_id = self.algorithm_name_2_id[f"{selection[0]}_{selection[1]['algorithm']}"] self.num_precommits_submitted += 1 req = SubmitPrecommitRequest( settings=BenchmarkSettings( challenge_id=c_id, algorithm_id=a_id, - player_id=self.player_id, + player_id=CONFIG["player_id"], block_id=self.last_block_id, difficulty=difficulty_samples[selection[0]] ), - num_nonces=selection[1].num_nonces + num_nonces=selection[1]["num_nonces"] ) - logger.info(f"Created precommit (challenge: {selection[0]}, algorithm: {selection[1].algorithm}, difficulty: {req.settings.difficulty}, num_nonces: {req.num_nonces})") + logger.info(f"Created precommit (challenge: {selection[0]}, algorithm: {selection[1]['algorithm']}, difficulty: {req.settings.difficulty}, num_nonces: {req.num_nonces})") return req \ No newline at end of file diff --git a/tig-benchmarker/master/master/slave_manager.py b/tig-benchmarker/master/master/slave_manager.py new file mode 100644 index 0000000..100f2ff --- /dev/null +++ b/tig-benchmarker/master/master/slave_manager.py @@ -0,0 +1,283 @@ +import os +import json +import logging +import re +import time +import random +from threading import Thread, Lock +from dataclasses import dataclass +from fastapi import FastAPI, Request, HTTPException +from fastapi.encoders import jsonable_encoder +from fastapi.responses import JSONResponse +import uvicorn +from common.structs import * +from common.utils import * +from typing import Dict, List, Optional, Set +from master.sql import db_conn +from master.client_manager import CONFIG + + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + + +class SlaveManager: + def __init__(self): + self.batches = [] + self.lock = Lock() + + def run(self): + with self.lock: + self.batches = db_conn.fetch_all( + """ + SELECT * FROM ( + SELECT + A.slave, + A.start_time, + A.end_time, + B.challenge, + JSONB_BUILD_OBJECT( + 'id', A.benchmark_id || '_' || A.batch_idx, + 'benchmark_id', A.benchmark_id, + 'start_nonce', A.batch_idx * B.batch_size, + 'num_nonces', LEAST(B.batch_size, B.num_nonces - A.batch_idx * B.batch_size), + 'settings', B.settings, + 'sampled_nonces', A.sampled_nonces, + 'runtime_config', B.runtime_config, + 'download_url', B.download_url, + 'rand_hash', B.rand_hash, + 'batch_size', B.batch_size, + 'batch_idx', A.batch_idx + ) AS batch + FROM proofs_batch A + INNER JOIN job B + ON A.ready IS NULL + AND B.merkle_root_ready + AND B.stopped IS NULL + AND A.benchmark_id = B.benchmark_id + ORDER BY B.block_started, A.benchmark_id, A.batch_idx + ) + + UNION ALL + + SELECT * FROM ( + SELECT + A.slave, + A.start_time, + A.end_time, + B.challenge, + JSONB_BUILD_OBJECT( + 'id', A.benchmark_id || '_' || A.batch_idx, + 'benchmark_id', A.benchmark_id, + 'start_nonce', A.batch_idx * B.batch_size, + 'num_nonces', LEAST(B.batch_size, B.num_nonces - A.batch_idx * B.batch_size), + 'settings', B.settings, + 'sampled_nonces', NULL, + 'runtime_config', B.runtime_config, + 'download_url', B.download_url, + 'rand_hash', B.rand_hash, + 'batch_size', B.batch_size, + 'batch_idx', A.batch_idx + ) AS batch + FROM root_batch A + INNER JOIN job B + ON A.ready IS NULL + AND B.stopped IS NULL + AND A.benchmark_id = B.benchmark_id + ORDER BY B.block_started, A.benchmark_id, A.batch_idx + ) + """ + ) + logger.debug(f"Refreshed pending batches. Got {len(self.batches)}") + + def start(self): + app = FastAPI() + + @app.route('/get-batches', methods=['GET']) + def get_batch(request: Request): + config = CONFIG["slave_manager_config"] + + if (slave_name := request.headers.get('User-Agent', None)) is None: + return "User-Agent header is required", 403 + if not any(re.match(slave["name_regex"], slave_name) for slave in config["slaves"]): + logger.warning(f"slave {slave_name} does not match any regex. rejecting get-batch request") + raise HTTPException(status_code=403, detail="Unregistered slave") + + slave = next((slave for slave in config["slaves"] if re.match(slave["name_regex"], slave_name)), None) + + concurrent = [] + updates = [] + now = time.time() * 1000 + selected_challenges = set(slave["selected_challenges"]) + with self.lock: + concurrent = [ + b["batch"] for b in self.batches + if b["slave"] == slave_name + ] + for b in self.batches: + batch = b["batch"] + if len(concurrent) >= slave["max_concurrent_batches"]: + break + if ( + b["slave"] == slave_name or + b["challenge"] not in selected_challenges or + b["end_time"] is not None + ): + continue + if ( + b["slave"] is None or + b["start_time"] is None or + (now - b["start_time"]) > config["time_before_batch_retry"] + ): + b["slave"] = slave_name + b["start_time"] = now + table = "root_batch" if batch["sampled_nonces"] is None else "proofs_batch" + updates.append(( + f""" + UPDATE {table} + SET slave = %s, + start_time = %s + WHERE benchmark_id = %s + AND batch_idx = %s + """, + (slave_name, now, batch["benchmark_id"], batch["batch_idx"]) + )) + concurrent.append(batch) + if len(concurrent) == 0: + logger.debug(f"no batches available for {slave_name}") + if len(updates) > 0: + db_conn.execute_many(*updates) + return JSONResponse(content=jsonable_encoder(concurrent)) + + @app.post('/submit-batch-root/{batch_id}') + async def submit_batch_root(batch_id: str, request: Request): + if (slave_name := request.headers.get('User-Agent', None)) is None: + raise HTTPException(status_code=403, detail="User-Agent header is required") + + with self.lock: + b = next(( + b for b in self.batches + if ( + b["batch"]["id"] == batch_id and + b["batch"]["sampled_nonces"] is None and + b["slave"] == slave_name + ) + ), None) + if b is None: + raise HTTPException( + status_code=408, + detail=f"Slave submitted roots for {batch_id}, but either took too long, or was not assigned this batch." + ) + b["end_time"] = time.time() * 1000 + + try: + result = await request.json() + merkle_root = MerkleHash.from_str(result["merkle_root"]) + solution_nonces = result["solution_nonces"] + assert isinstance(solution_nonces, list) and all(isinstance(x, int) for x in solution_nonces) + logger.debug(f"slave {slave_name} submitted root for {batch_id}") + except Exception as e: + logger.error(f"slave {slave_name} submitted INVALID root for {batch_id}: {e}") + raise HTTPException(status_code=400, detail="INVALID root") + + # Update roots table with merkle root and solution nonces + benchmark_id, batch_idx = batch_id.split("_") + batch_idx = int(batch_idx) + db_conn.execute_many(*[ + ( + """ + UPDATE root_batch + SET ready = true, + end_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + WHERE benchmark_id = %s + AND batch_idx = %s + """, + ( + benchmark_id, + batch_idx + ) + ), + ( + """ + UPDATE batch_data + SET merkle_root = %s, + solution_nonces = %s + WHERE benchmark_id = %s + AND batch_idx = %s + """, + ( + merkle_root.to_str(), + json.dumps(solution_nonces), + benchmark_id, + batch_idx + ) + ) + ]) + + return {"status": "OK"} + + @app.post('/submit-batch-proofs/{batch_id}') + async def submit_batch_proofs(batch_id: str, request: Request): + if (slave_name := request.headers.get('User-Agent', None)) is None: + raise HTTPException(status_code=403, detail="User-Agent header is required") + + with self.lock: + b = next(( + b for b in self.batches + if ( + b["batch"]["id"] == batch_id and + b["batch"]["sampled_nonces"] is not None and + b["slave"] == slave_name + ) + ), None) + if b is None: + raise HTTPException( + status_code=408, + detail=f"Slave submitted proofs for {batch_id}, but either took too long, or was not assigned this batch." + ) + b["end_time"] = time.time() * 1000 + + try: + result = await request.json() + merkle_proofs = [MerkleProof.from_dict(x) for x in result["merkle_proofs"]] + logger.debug(f"slave {slave_name} submitted proofs for {batch_id}") + except Exception as e: + logger.error(f"slave {slave_name} submitted INVALID proofs for {batch_id}: {e}") + raise HTTPException(status_code=400, detail="INVALID proofs") + + # Update proofs table with merkle proofs + benchmark_id, batch_idx = batch_id.split("_") + batch_idx = int(batch_idx) + db_conn.execute_many(*[ + ( + """ + UPDATE proofs_batch + SET ready = true, + end_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + WHERE benchmark_id = %s + AND batch_idx = %s + """, + (benchmark_id, batch_idx) + ), + ( + """ + UPDATE batch_data + SET merkle_proofs = %s + WHERE benchmark_id = %s + AND batch_idx = %s + """, + ( + json.dumps([x.to_dict() for x in merkle_proofs]), + benchmark_id, + batch_idx + ) + ) + ]) + + return {"status": "OK"} + + config = CONFIG["slave_manager_config"] + thread = Thread(target=lambda: uvicorn.run(app, host="0.0.0.0", port=config["port"])) + thread.daemon = True + thread.start() + + logger.info(f"webserver started on 0.0.0.0:{config['port']}") diff --git a/tig-benchmarker/master/master/sql.py b/tig-benchmarker/master/master/sql.py new file mode 100644 index 0000000..0c4d46f --- /dev/null +++ b/tig-benchmarker/master/master/sql.py @@ -0,0 +1,110 @@ +import os +import logging +import psycopg2 +from psycopg2.extras import RealDictCursor +from typing import Optional, Dict, Any, List + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +class PostgresDB: + def __init__(self, host: str, port: int, dbname: str, user: str, password: str): + self.conn_params = { + 'host': host, + 'port': port, + 'dbname': dbname, + 'user': user, + 'password': password + } + self._conn = None + + def connect(self) -> None: + """Establish connection to PostgreSQL database""" + try: + self._conn = psycopg2.connect(**self.conn_params) + logger.info(f"Connected to PostgreSQL database at {self.conn_params['host']}:{self.conn_params['port']}") + except Exception as e: + logger.error(f"Error connecting to PostgreSQL: {str(e)}") + raise + + def disconnect(self) -> None: + """Close database connection""" + if self._conn: + self._conn.close() + self._conn = None + logger.info("Disconnected from PostgreSQL database") + + def execute_many(self, *args) -> None: + """Execute multiple queries in a single transaction""" + if not self._conn: + self.connect() + + try: + with self._conn.cursor() as cur: + cur.execute("BEGIN") + for query in args: + cur.execute(*query) + self._conn.commit() + except Exception as e: + self._conn.rollback() + logger.error(f"Error executing queries: {str(e)}") + raise + + def execute(self, query: str, params: Optional[tuple] = None) -> None: + """Execute a query without returning results""" + if not self._conn: + self.connect() + + try: + with self._conn.cursor() as cur: + cur.execute(query, params) + self._conn.commit() + except Exception as e: + self._conn.rollback() + logger.error(f"Error executing query: {str(e)}") + raise + + def fetch_one(self, query: str, params: Optional[tuple] = None) -> Optional[Dict[str, Any]]: + """Execute query and return single row as dictionary""" + if not self._conn: + self.connect() + + try: + with self._conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(query, params) + return cur.fetchone() + except Exception as e: + self._conn.rollback() + logger.error(f"Error fetching row: {str(e)}") + raise + + def fetch_all(self, query: str, params: Optional[tuple] = None) -> List[Dict[str, Any]]: + """Execute query and return all rows as list of dictionaries""" + if not self._conn: + self.connect() + + try: + with self._conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute(query, params) + return cur.fetchall() + except Exception as e: + self._conn.rollback() + logger.error(f"Error fetching rows: {str(e)}") + raise + + def __enter__(self): + self.connect() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.disconnect() + + +db_conn = None +if db_conn is None: + db_conn = PostgresDB( + host=os.environ["POSTGRES_HOST"], + port=5432, + dbname=os.environ["POSTGRES_DB"], + user=os.environ["POSTGRES_USER"], + password=os.environ["POSTGRES_PASSWORD"] + ) \ No newline at end of file diff --git a/tig-benchmarker/master/master/submissions_manager.py b/tig-benchmarker/master/master/submissions_manager.py new file mode 100644 index 0000000..1833cba --- /dev/null +++ b/tig-benchmarker/master/master/submissions_manager.py @@ -0,0 +1,178 @@ +import requests +import threading +import logging +import json +import os +from common.structs import * +from common.utils import * +from typing import Union, Set, List, Dict +from master.sql import db_conn +from master.client_manager import CONFIG + +logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) + +@dataclass +class SubmitPrecommitRequest(FromDict): + settings: BenchmarkSettings + num_nonces: int + +@dataclass +class SubmitBenchmarkRequest(FromDict): + benchmark_id: str + merkle_root: MerkleHash + solution_nonces: Set[int] + +@dataclass +class SubmitProofRequest(FromDict): + benchmark_id: str + merkle_proofs: List[MerkleProof] + +class SubmissionsManager: + def __init__(self): + pass + + def _post(self, submission_type: str, req: Union[SubmitPrecommitRequest, SubmitBenchmarkRequest, SubmitProofRequest]): + api_key = CONFIG["api_key"] + api_url = CONFIG["api_url"] + + headers = { + "X-Api-Key": api_key, + "Content-Type": "application/json", + "User-Agent": "tig-benchmarker-py/v0.2" + } + if submission_type == "precommit": + logger.info(f"submitting {submission_type}") + else: + logger.info(f"submitting {submission_type} '{req.benchmark_id}'") + logger.debug(f"{req}") + + resp = requests.post(f"{api_url}/submit-{submission_type}", json=req.to_dict(), headers=headers) + if resp.status_code == 200: + logger.info(f"submitted {submission_type} successfully") + elif resp.headers.get("Content-Type") == "text/plain": + logger.error(f"status {resp.status_code} when submitting {submission_type}: {resp.text}") + else: + logger.error(f"status {resp.status_code} when submitting {submission_type}") + + def _post_thread(self, submission_type: str, req: Union[SubmitPrecommitRequest, SubmitBenchmarkRequest, SubmitProofRequest]): + thread = threading.Thread(target=self._post, args=(submission_type, req)) + thread.start() + + def on_new_block(self, + benchmarks: Dict[str, Benchmark], + proofs: Dict[str, Proof], + **kwargs + ): + if len(benchmarks) > 0: + db_conn.execute( + """ + UPDATE job + SET benchmark_submitted = true + WHERE benchmark_id IN %s + """, + (tuple(benchmarks),) + ) + + if len(proofs) > 0: + db_conn.execute( + """ + UPDATE job + SET proof_submitted = true + WHERE benchmark_id IN %s + """, + (tuple(proofs),) + ) + + def run(self, submit_precommit_req: Optional[SubmitPrecommitRequest]): + config = CONFIG["submissions_manager_config"] + + now = int(time.time() * 1000) + if submit_precommit_req is None: + logger.debug("no precommit to submit") + else: + self._post_thread("precommit", submit_precommit_req) + + benchmark_to_submit = db_conn.fetch_one( + """ + WITH updated AS ( + UPDATE job + SET benchmark_submit_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + WHERE benchmark_id IN ( + SELECT benchmark_id + FROM job + WHERE merkle_root_ready + AND stopped IS NULL + AND benchmark_submitted IS NULL + AND ( + benchmark_submit_time IS NULL + OR ((EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT - benchmark_submit_time) > %s + ) + ORDER BY block_started + LIMIT 1 + ) + RETURNING benchmark_id + ) + SELECT + B.benchmark_id, + B.merkle_root, + B.solution_nonces + FROM updated A + INNER JOIN job_data B + ON A.benchmark_id = B.benchmark_id + """, + (config["time_between_retries"],) + ) + + if benchmark_to_submit: + benchmark_id = benchmark_to_submit["benchmark_id"] + merkle_root = benchmark_to_submit["merkle_root"] + solution_nonces = benchmark_to_submit["solution_nonces"] + + self._post_thread("benchmark", SubmitBenchmarkRequest( + benchmark_id=benchmark_id, + merkle_root=merkle_root, + solution_nonces=solution_nonces + )) + else: + logger.debug("no benchmark to submit") + + proof_to_submit = db_conn.fetch_one( + """ + WITH updated AS ( + UPDATE job + SET proof_submit_time = (EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT + WHERE benchmark_id IN ( + SELECT benchmark_id + FROM job + WHERE merkle_proofs_ready + AND stopped IS NULL + AND proof_submitted IS NULL + AND ( + proof_submit_time IS NULL + OR ((EXTRACT(EPOCH FROM NOW()) * 1000)::BIGINT - proof_submit_time) > %s + ) + ORDER BY block_started + LIMIT 1 + ) + RETURNING benchmark_id + ) + SELECT + B.benchmark_id, + B.merkle_proofs + FROM updated A + INNER JOIN job_data B + ON A.benchmark_id = B.benchmark_id + """, + (config["time_between_retries"],) + ) + + if proof_to_submit: + benchmark_id = proof_to_submit["benchmark_id"] + merkle_proofs = proof_to_submit["merkle_proofs"] + + self._post_thread("proof", SubmitProofRequest( + benchmark_id=benchmark_id, + merkle_proofs=merkle_proofs + )) + else: + logger.debug("no proof to submit") diff --git a/tig-benchmarker/master/requirements.txt b/tig-benchmarker/master/requirements.txt new file mode 100644 index 0000000..d01ed26 --- /dev/null +++ b/tig-benchmarker/master/requirements.txt @@ -0,0 +1,11 @@ +blake3 +dataclasses +hypercorn +numpy +quart +randomname +uvicorn +fastapi +sqlalchemy +psycopg2-binary +requests \ No newline at end of file diff --git a/tig-benchmarker/nginx/Dockerfile b/tig-benchmarker/nginx/Dockerfile new file mode 100644 index 0000000..9ded20d --- /dev/null +++ b/tig-benchmarker/nginx/Dockerfile @@ -0,0 +1,3 @@ +FROM nginx:alpine + +COPY nginx.conf /etc/nginx/nginx.conf \ No newline at end of file diff --git a/tig-benchmarker/nginx/nginx.conf b/tig-benchmarker/nginx/nginx.conf new file mode 100644 index 0000000..26ceb36 --- /dev/null +++ b/tig-benchmarker/nginx/nginx.conf @@ -0,0 +1,40 @@ +events { + worker_connections 1024; +} + +http { + # Additional common settings + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server { + listen 80; + server_name localhost; + + # Master Service - specific endpoints + location ~ ^/(get-config|stop|update-config|get-jobs|get-latest-data) { + proxy_pass http://master:3336; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + + # UI Service - everything else + location / { + proxy_pass http://ui:80; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } + } +} \ No newline at end of file diff --git a/tig-benchmarker/postgres/Dockerfile b/tig-benchmarker/postgres/Dockerfile new file mode 100644 index 0000000..69be141 --- /dev/null +++ b/tig-benchmarker/postgres/Dockerfile @@ -0,0 +1,3 @@ +FROM postgres:17 + +COPY init.sql /docker-entrypoint-initdb.d/ \ No newline at end of file diff --git a/tig-benchmarker/postgres/init.sql b/tig-benchmarker/postgres/init.sql new file mode 100644 index 0000000..a822447 --- /dev/null +++ b/tig-benchmarker/postgres/init.sql @@ -0,0 +1,179 @@ +CREATE TABLE IF NOT EXISTS config ( + config JSONB +); + +CREATE TABLE IF NOT EXISTS job ( + benchmark_id TEXT PRIMARY KEY, + settings JSONB NOT NULL, + num_nonces INTEGER NOT NULL, + rand_hash TEXT NOT NULL, + runtime_config JSONB NOT NULL, + batch_size INTEGER NOT NULL, + num_batches INTEGER NOT NULL, + challenge TEXT NOT NULL, + algorithm TEXT NOT NULL, + download_url TEXT NOT NULL, + block_started INTEGER NOT NULL, + sampled_nonces JSONB, + benchmark_submit_time BIGINT, + proof_submit_time BIGINT, + start_time BIGINT, + end_time BIGINT, + merkle_root_ready BOOLEAN, + merkle_proofs_ready BOOLEAN, + benchmark_submitted BOOLEAN, + proof_submitted BOOLEAN, + stopped BOOLEAN +); + +CREATE INDEX idx_job_batch_size ON job(batch_size); +CREATE INDEX idx_job_block_started ON job(block_started); +CREATE INDEX idx_job_challenge ON job(challenge); +CREATE INDEX idx_job_benchmark_submit_time ON job(benchmark_submit_time); +CREATE INDEX idx_job_proof_submit_time ON job(proof_submit_time); +CREATE INDEX idx_job_merkle_root_ready ON job(merkle_root_ready); +CREATE INDEX idx_job_merkle_proofs_ready ON job(merkle_proofs_ready); +CREATE INDEX idx_job_benchmark_submitted ON job(benchmark_submitted); +CREATE INDEX idx_job_proof_submitted ON job(proof_submitted); +CREATE INDEX idx_job_stopped ON job(stopped); + +CREATE TABLE IF NOT EXISTS job_data ( + benchmark_id TEXT PRIMARY KEY, + merkle_root TEXT, + solution_nonces JSONB, + merkle_proofs JSONB, + + FOREIGN KEY (benchmark_id) REFERENCES job(benchmark_id) +); + +CREATE TABLE IF NOT EXISTS root_batch ( + benchmark_id TEXT, + batch_idx INTEGER, + slave TEXT, + start_time BIGINT, + end_time BIGINT, + ready BOOLEAN, + + PRIMARY KEY (benchmark_id, batch_idx), + FOREIGN KEY (benchmark_id) REFERENCES job(benchmark_id) +); + +CREATE INDEX idx_root_batch_benchmark_id ON root_batch(benchmark_id); +CREATE INDEX idx_root_batch_batch_idx ON root_batch(batch_idx); +CREATE INDEX idx_root_batch_slave ON root_batch(slave); +CREATE INDEX idx_root_batch_start_time ON root_batch(start_time); +CREATE INDEX idx_root_batch_end_time ON root_batch(end_time); +CREATE INDEX idx_root_batch_ready ON root_batch(ready); + +CREATE TABLE IF NOT EXISTS proofs_batch ( + benchmark_id TEXT REFERENCES job(benchmark_id), + batch_idx INTEGER, + slave TEXT, + start_time BIGINT, + end_time BIGINT, + sampled_nonces JSONB, + ready BOOLEAN, + + PRIMARY KEY (benchmark_id, batch_idx), + FOREIGN KEY (benchmark_id, batch_idx) REFERENCES root_batch(benchmark_id, batch_idx) +); + +CREATE INDEX idx_proofs_batch_benchmark_id ON proofs_batch(benchmark_id); +CREATE INDEX idx_proofs_batch_batch_idx ON proofs_batch(batch_idx); +CREATE INDEX idx_proofs_batch_slave ON proofs_batch(slave); +CREATE INDEX idx_proofs_batch_start_time ON proofs_batch(start_time); +CREATE INDEX idx_proofs_batch_end_time ON proofs_batch(end_time); +CREATE INDEX idx_proofs_batch_ready ON proofs_batch(ready); + +CREATE TABLE IF NOT EXISTS batch_data ( + benchmark_id TEXT, + batch_idx INTEGER, + merkle_root TEXT, + solution_nonces JSONB, + merkle_proofs JSONB, + + PRIMARY KEY (benchmark_id, batch_idx), + FOREIGN KEY (benchmark_id, batch_idx) REFERENCES root_batch(benchmark_id, batch_idx) +); + +CREATE INDEX idx_proofs_batch_data_benchmark_id ON batch_data(benchmark_id); +CREATE INDEX idx_proofs_batch_data_batch_idx ON batch_data(batch_idx); + +INSERT INTO config +SELECT ' +{ + "player_id": "0x0000000000000000000000000000000000000000", + "api_key": "00000000000000000000000000000000", + "api_url": "https://mainnet-api.tig.foundation", + "difficulty_sampler_config": { + "difficulty_ranges": { + "satisfiability": [0, 0.5], + "vehicle_routing": [0, 0.5], + "knapsack": [0, 0.5], + "vector_search": [0, 0.5] + }, + "selected_difficulties": { + "satisfiability": [], + "vehicle_routing": [], + "knapsack": [], + "vector_search": [] + } + }, + "job_manager_config": { + "batch_sizes": { + "satisfiability": 8, + "vehicle_routing": 8, + "knapsack": 8, + "vector_search": 8 + } + }, + "submissions_manager_config": { + "time_between_retries": 60000 + }, + "precommit_manager_config": { + "max_pending_benchmarks": 4, + "algo_selection": { + "satisfiability": { + "algorithm": "schnoing", + "num_nonces": 40, + "weight": 1, + "base_fee_limit": "10000000000000000" + }, + "vehicle_routing": { + "algorithm": "clarke_wright", + "num_nonces": 40, + "weight": 1, + "base_fee_limit": "10000000000000000" + }, + "knapsack": { + "algorithm": "dynamic", + "num_nonces": 40, + "weight": 1, + "base_fee_limit": "10000000000000000" + }, + "vector_search": { + "algorithm": "optimal_ann", + "num_nonces": 40, + "weight": 1, + "base_fee_limit": "10000000000000000" + } + } + }, + "slave_manager_config": { + "port": 5115, + "time_before_batch_retry": 60000, + "slaves": [ + { + "name_regex": ".*", + "max_concurrent_batches": 1, + "selected_challenges": [ + "satisfiability", + "vehicle_routing", + "knapsack", + "vector_search" + ] + } + ] + } +}' +WHERE NOT EXISTS (SELECT 1 FROM config); \ No newline at end of file diff --git a/tig-benchmarker/requirements.txt b/tig-benchmarker/requirements.txt deleted file mode 100644 index 9fe1c94..0000000 --- a/tig-benchmarker/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -aiohttp -asyncio -blake3 -dataclasses -hypercorn -numpy -quart -randomname \ No newline at end of file diff --git a/tig-benchmarker/slave copy.py b/tig-benchmarker/slave copy.py deleted file mode 100644 index fa4da11..0000000 --- a/tig-benchmarker/slave copy.py +++ /dev/null @@ -1,137 +0,0 @@ -import argparse -import json -import os -import logging -import randomname -import aiohttp -import asyncio -import subprocess -import time - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -def now(): - return int(time.time() * 1000) - -async def download_wasm(session, download_url, wasm_path): - if not os.path.exists(wasm_path): - start = now() - logger.info(f"downloading WASM from {download_url}") - async with session.get(download_url) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when downloading WASM: {await resp.text()}") - with open(wasm_path, 'wb') as f: - f.write(await resp.read()) - logger.debug(f"downloading WASM: took {now() - start}ms") - logger.debug(f"WASM Path: {wasm_path}") - -async def run_tig_worker(tig_worker_path, batch, wasm_path, num_workers): - start = now() - cmd = [ - tig_worker_path, "compute_batch", - json.dumps(batch["settings"]), - batch["rand_hash"], - str(batch["start_nonce"]), - str(batch["num_nonces"]), - str(batch["batch_size"]), - wasm_path, - "--mem", str(batch["runtime_config"]["max_memory"]), - "--fuel", str(batch["runtime_config"]["max_fuel"]), - "--workers", str(num_workers), - ] - if batch["sampled_nonces"]: - cmd += ["--sampled", *map(str, batch["sampled_nonces"])] - logger.info(f"computing batch: {' '.join(cmd)}") - process = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - if process.returncode != 0: - raise Exception(f"tig-worker failed: {stderr.decode()}") - result = json.loads(stdout.decode()) - logger.info(f"computing batch took {now() - start}ms") - logger.debug(f"batch result: {result}") - return result - -async def process_batch(session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, batch, headers): - try: - batch_id = f"{batch['benchmark_id']}_{batch['start_nonce']}" - logger.info(f"Processing batch {batch_id}: {batch}") - - # Step 2: Download WASM - wasm_path = os.path.join(download_wasms_folder, f"{batch['settings']['algorithm_id']}.wasm") - await download_wasm(session, batch['download_url'], wasm_path) - - # Step 3: Run tig-worker - result = await run_tig_worker(tig_worker_path, batch, wasm_path, num_workers) - - # Step 4: Submit results - start = now() - submit_url = f"http://{master_ip}:{master_port}/submit-batch-result/{batch_id}" - logger.info(f"posting results to {submit_url}") - async with session.post(submit_url, json=result, headers=headers) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when posting results to master: {await resp.text()}") - logger.debug(f"posting results took {now() - start} ms") - - except Exception as e: - logger.error(f"Error processing batch {batch_id}: {e}") - -async def main( - master_ip: str, - tig_worker_path: str, - download_wasms_folder: str, - num_workers: int, - slave_name: str, - master_port: int -): - if not os.path.exists(tig_worker_path): - raise FileNotFoundError(f"tig-worker not found at path: {tig_worker_path}") - os.makedirs(download_wasms_folder, exist_ok=True) - - headers = { - "User-Agent": slave_name - } - - async with aiohttp.ClientSession() as session: - while True: - try: - # Step 1: Query for job - start = now() - get_batch_url = f"http://{master_ip}:{master_port}/get-batches" - logger.info(f"fetching job from {get_batch_url}") - async with session.get(get_batch_url, headers=headers) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when fetching job: {await resp.text()}") - batches = await resp.json(content_type=None) - logger.debug(f"fetching job: took {now() - start}ms") - - # Process batches concurrently - tasks = [ - process_batch(session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, batch, headers) - for batch in batches - ] - await asyncio.gather(*tasks) - - except Exception as e: - logger.error(e) - await asyncio.sleep(5) - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="TIG Slave Benchmarker") - parser.add_argument("master_ip", help="IP address of the master") - parser.add_argument("tig_worker_path", help="Path to tig-worker executable") - parser.add_argument("--download", type=str, default="wasms", help="Folder to download WASMs to (default: wasms)") - parser.add_argument("--workers", type=int, default=8, help="Number of workers (default: 8)") - parser.add_argument("--name", type=str, default=randomname.get_name(), help="Name for the slave (default: randomly generated)") - parser.add_argument("--port", type=int, default=5115, help="Port for master (default: 5115)") - parser.add_argument("--verbose", action='store_true', help="Print debug logs") - - args = parser.parse_args() - - logging.basicConfig( - format='%(levelname)s - [%(name)s] - %(message)s', - level=logging.DEBUG if args.verbose else logging.INFO - ) - - asyncio.run(main(args.master_ip, args.tig_worker_path, args.download, args.workers, args.name, args.port)) \ No newline at end of file diff --git a/tig-benchmarker/slave.py b/tig-benchmarker/slave.py index cde0bdd..4d8a51e 100644 --- a/tig-benchmarker/slave.py +++ b/tig-benchmarker/slave.py @@ -3,154 +3,286 @@ import json import os import logging import randomname -import aiohttp -import asyncio +import requests +import shutil import subprocess import time +import zlib +from threading import Thread +from common.structs import OutputData, MerkleProof +from common.merkle_tree import MerkleTree logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) +PENDING_BATCH_IDS = set() +PROCESSING_BATCH_IDS = set() +READY_BATCH_IDS = set() +FINISHED_BATCH_IDS = {} def now(): return int(time.time() * 1000) -async def download_wasm(session, download_url, wasm_path): +def download_wasm(session, download_url, wasm_path): if not os.path.exists(wasm_path): start = now() logger.info(f"downloading WASM from {download_url}") - async with session.get(download_url) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when downloading WASM: {await resp.text()}") - with open(wasm_path, 'wb') as f: - f.write(await resp.read()) + resp = session.get(download_url) + if resp.status_code != 200: + raise Exception(f"status {resp.status_code} when downloading WASM: {resp.text}") + with open(wasm_path, 'wb') as f: + f.write(resp.content) logger.debug(f"downloading WASM: took {now() - start}ms") logger.debug(f"WASM Path: {wasm_path}") -async def run_tig_worker(tig_worker_path, batch, wasm_path, num_workers): + +def run_tig_worker(tig_worker_path, batch, wasm_path, num_workers, output_path): start = now() cmd = [ tig_worker_path, "compute_batch", - json.dumps(batch["settings"]), - batch["rand_hash"], - str(batch["start_nonce"]), + json.dumps(batch["settings"]), + batch["rand_hash"], + str(batch["start_nonce"]), str(batch["num_nonces"]), - str(batch["batch_size"]), + str(batch["batch_size"]), wasm_path, "--mem", str(batch["runtime_config"]["max_memory"]), "--fuel", str(batch["runtime_config"]["max_fuel"]), "--workers", str(num_workers), + "--output", f"{output_path}/{batch['id']}", ] - if batch["sampled_nonces"]: - cmd += ["--sampled", *map(str, batch["sampled_nonces"])] logger.info(f"computing batch: {' '.join(cmd)}") - process = await asyncio.create_subprocess_exec( - *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE + process = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) - stdout, stderr = await process.communicate() + stdout, stderr = process.communicate() if process.returncode != 0: + PROCESSING_BATCH_IDS.remove(batch["id"]) raise Exception(f"tig-worker failed: {stderr.decode()}") result = json.loads(stdout.decode()) logger.info(f"computing batch took {now() - start}ms") logger.debug(f"batch result: {result}") - return result + with open(f"{output_path}/{batch['id']}/result.json", "w") as f: + json.dump(result, f) + + PROCESSING_BATCH_IDS.remove(batch["id"]) + READY_BATCH_IDS.add(batch["id"]) + -async def process_batch(session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, batch, headers): +def purge_folders(output_path, ttl): + n = now() + purge_batch_ids = [ + batch_id + for batch_id, finish_time in FINISHED_BATCH_IDS.items() + if n >= finish_time + (ttl * 1000) + ] + if len(purge_batch_ids) == 0: + time.sleep(5) + return + + for batch_id in purge_batch_ids: + if os.path.exists(f"{output_path}/{batch_id}"): + logger.info(f"purging batch {batch_id}") + shutil.rmtree(f"{output_path}/{batch_id}") + FINISHED_BATCH_IDS.pop(batch_id) + + +def send_results(session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, output_path): try: - batch_id = f"{batch['benchmark_id']}_{batch['start_nonce']}" - logger.info(f"Processing batch {batch_id}: {batch}") + batch_id = READY_BATCH_IDS.pop() + except KeyError: + logger.debug("No pending batches") + time.sleep(1) + return + + output_folder = f"{output_path}/{batch_id}" + with open(f"{output_folder}/batch.json") as f: + batch = json.load(f) - # Step 2: Download WASM - wasm_path = os.path.join(download_wasms_folder, f"{batch['settings']['algorithm_id']}.wasm") - await download_wasm(session, batch['download_url'], wasm_path) + if ( + not os.path.exists(f"{output_folder}/result.json") + or not os.path.exists(f"{output_folder}/data.zlib") + ): + if os.path.exists(f"{output_folder}/result.json"): + os.remove(f"{output_folder}/result.json") + logger.debug(f"Batch {batch_id} flagged as ready, but missing nonce files") + PENDING_BATCH_IDS.add(batch_id) + return - # Step 3: Run tig-worker - result = await run_tig_worker(tig_worker_path, batch, wasm_path, num_workers) + if batch["sampled_nonces"] is None: + with open(f"{output_folder}/result.json") as f: + result = json.load(f) - # Step 4: Submit results - start = now() - submit_url = f"http://{master_ip}:{master_port}/submit-batch-result/{batch_id}" - logger.info(f"posting results to {submit_url}") - async with session.post(submit_url, json=result, headers=headers) as resp: - if resp.status != 200: - raise Exception(f"status {resp.status} when posting results to master: {await resp.text()}") - logger.debug(f"posting results took {now() - start} ms") + submit_url = f"http://{master_ip}:{master_port}/submit-batch-root/{batch_id}" + logger.info(f"posting root to {submit_url}") + resp = session.post(submit_url, json=result) + if resp.status_code == 200: + FINISHED_BATCH_IDS[batch_id] = now() + logger.info(f"successfully posted root for batch {batch_id}") + elif resp.status_code == 408: # took too long + FINISHED_BATCH_IDS[batch_id] = now() + logger.error(f"status {resp.status_code} when posting root for batch {batch_id} to master: {resp.text}") + else: + logger.error(f"status {resp.status_code} when posting root for batch {batch_id} to master: {resp.text}") + READY_BATCH_IDS.add(batch_id) # requeue + time.sleep(2) - except Exception as e: - logger.error(f"Error processing batch {batch_id}: {e}") + else: + with open(f"{output_folder}/data.zlib", "rb") as f: + leafs = [ + OutputData.from_dict(x) + for x in json.loads(zlib.decompress(f.read()).decode()) + ] + + merkle_tree = MerkleTree( + [x.to_merkle_hash() for x in leafs], + batch["batch_size"] + ) -async def main( + proofs_to_submit = [ + MerkleProof( + leaf=leafs[n - batch["start_nonce"]], + branch=merkle_tree.calc_merkle_branch(branch_idx=n - batch["start_nonce"]) + ).to_dict() + for n in batch["sampled_nonces"] + ] + + submit_url = f"http://{master_ip}:{master_port}/submit-batch-proofs/{batch_id}" + logger.info(f"posting proofs to {submit_url}") + resp = session.post(submit_url, json={"merkle_proofs": proofs_to_submit}) + if resp.status_code == 200: + FINISHED_BATCH_IDS[batch_id] = now() + logger.info(f"successfully posted proofs for batch {batch_id}") + elif resp.status_code == 408: # took too long + FINISHED_BATCH_IDS[batch_id] = now() + logger.error(f"status {resp.status_code} when posting proofs for batch {batch_id} to master: {resp.text}") + else: + logger.error(f"status {resp.status_code} when posting proofs for batch {batch_id} to master: {resp.text}") + READY_BATCH_IDS.add(batch_id) # requeue + time.sleep(2) + + +def process_batch(session, tig_worker_path, download_wasms_folder, num_workers, output_path): + try: + batch_id = PENDING_BATCH_IDS.pop() + except KeyError: + logger.debug("No pending batches") + time.sleep(1) + return + + if ( + batch_id in PROCESSING_BATCH_IDS or + batch_id in READY_BATCH_IDS + ): + return + + if os.path.exists(f"{output_path}/{batch_id}/result.json"): + logger.info(f"Batch {batch_id} already processed") + READY_BATCH_IDS.add(batch_id) + return + PROCESSING_BATCH_IDS.add(batch_id) + + with open(f"{output_path}/{batch_id}/batch.json") as f: + batch = json.load(f) + + wasm_path = os.path.join(download_wasms_folder, f"{batch['settings']['algorithm_id']}.wasm") + download_wasm(session, batch['download_url'], wasm_path) + + Thread( + target=run_tig_worker, + args=(tig_worker_path, batch, wasm_path, num_workers, output_path) + ).start() + + +def poll_batches(session, master_ip, master_port, output_path): + get_batches_url = f"http://{master_ip}:{master_port}/get-batches" + logger.info(f"fetching batches from {get_batches_url}") + resp = session.get(get_batches_url) + + if resp.status_code == 200: + batches = resp.json() + root_batch_ids = [batch['id'] for batch in batches if batch['sampled_nonces'] is None] + proofs_batch_ids = [batch['id'] for batch in batches if batch['sampled_nonces'] is not None] + logger.info(f"root batches: {root_batch_ids}") + logger.info(f"proofs batches: {proofs_batch_ids}") + for batch in batches: + output_folder = f"{output_path}/{batch['id']}" + os.makedirs(output_folder, exist_ok=True) + with open(f"{output_folder}/batch.json", "w") as f: + json.dump(batch, f) + PENDING_BATCH_IDS.clear() + PENDING_BATCH_IDS.update(root_batch_ids + proofs_batch_ids) + time.sleep(5) + + else: + logger.error(f"status {resp.status_code} when fetching batch: {resp.text}") + time.sleep(5) + + +def wrap_thread(func, *args): + logger.info(f"Starting thread for {func.__name__}") + while True: + try: + func(*args) + except Exception as e: + logger.error(f"Error in {func.__name__}: {e}") + time.sleep(5) + + +def main( master_ip: str, tig_worker_path: str, download_wasms_folder: str, num_workers: int, slave_name: str, - master_port: int + master_port: int, + output_path: str, + ttl: int, ): + print(f"Starting slave {slave_name}") + if not os.path.exists(tig_worker_path): raise FileNotFoundError(f"tig-worker not found at path: {tig_worker_path}") os.makedirs(download_wasms_folder, exist_ok=True) - headers = { + session = requests.Session() + session.headers.update({ "User-Agent": slave_name - } + }) + Thread( + target=wrap_thread, + args=(process_batch, session, tig_worker_path, download_wasms_folder, num_workers, output_path) + ).start() - async with aiohttp.ClientSession() as session: - while True: - try: - # Step 1: Query for job test maj - start = now() - get_batch_url = f"http://{master_ip}:{master_port}/get-batches" - logger.info(f"fetching job from {get_batch_url}") - try: - resp = await asyncio.wait_for(session.get(get_batch_url, headers=headers), timeout=5) - if resp.status != 200: - text = await resp.text() - if resp.status == 404 and text.strip() == "No batches available": - # Retry with master_port - 1 - new_port = master_port - 1 - get_batch_url = f"http://{master_ip}:{new_port}/get-batches" - logger.info(f"No batches available on port {master_port}, trying port {new_port}") - resp_retry = await asyncio.wait_for(session.get(get_batch_url, headers=headers), timeout=10) - if resp_retry.status != 200: - raise Exception(f"status {resp_retry.status} when fetching job: {await resp_retry.text()}") - master_port_w = new_port - batches = await resp_retry.json(content_type=None) - else: - raise Exception(f"status {resp.status} when fetching job: {text}") - else: - master_port_w = master_port - batches = await resp.json(content_type=None) - except asyncio.TimeoutError: - logger.error(f"Timeout occurred when fetching job from {get_batch_url}") - continue - logger.debug(f"fetching job: took {now() - start}ms") + Thread( + target=wrap_thread, + args=(send_results, session, master_ip, master_port, tig_worker_path, download_wasms_folder, num_workers, output_path) + ).start() - # Process batches concurrently - tasks = [ - process_batch(session, master_ip, master_port_w, tig_worker_path, download_wasms_folder, num_workers, batch, headers) - for batch in batches - ] - await asyncio.gather(*tasks) + Thread( + target=wrap_thread, + args=(purge_folders, output_path, ttl) + ).start() + + wrap_thread(poll_batches, session, master_ip, master_port, output_path) - except Exception as e: - logger.error(e) - await asyncio.sleep(2) if __name__ == "__main__": parser = argparse.ArgumentParser(description="TIG Slave Benchmarker") - parser.add_argument("master_ip", help="IP address of the master") parser.add_argument("tig_worker_path", help="Path to tig-worker executable") + parser.add_argument("--master", type=str, default="0.0.0.0", help="IP address of the master (default: 0.0.0.0)") parser.add_argument("--download", type=str, default="wasms", help="Folder to download WASMs to (default: wasms)") parser.add_argument("--workers", type=int, default=8, help="Number of workers (default: 8)") parser.add_argument("--name", type=str, default=randomname.get_name(), help="Name for the slave (default: randomly generated)") parser.add_argument("--port", type=int, default=5115, help="Port for master (default: 5115)") parser.add_argument("--verbose", action='store_true', help="Print debug logs") - + parser.add_argument("--output", type=str, default="results", help="Folder to output results to (default: results)") + parser.add_argument("--ttl", type=int, default=300, help="(Time To Live) Seconds to retain results (default: 300)") + args = parser.parse_args() - + logging.basicConfig( format='%(levelname)s - [%(name)s] - %(message)s', level=logging.DEBUG if args.verbose else logging.INFO ) - asyncio.run(main(args.master_ip, args.tig_worker_path, args.download, args.workers, args.name, args.port)) + main(args.master, args.tig_worker_path, args.download, args.workers, args.name, args.port, args.output, args.ttl) diff --git a/tig-benchmarker/tests/data.py b/tig-benchmarker/tests/data.py index b0419cc..7752c22 100644 --- a/tig-benchmarker/tests/data.py +++ b/tig-benchmarker/tests/data.py @@ -4,9 +4,9 @@ import os sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from tig_benchmarker.utils import u64s_from_str, u8s_from_str, jsonify -from tig_benchmarker.merkle_tree import MerkleHash -from tig_benchmarker.data import BenchmarkSettings, OutputData +from common.utils import u64s_from_str, u8s_from_str, jsonify +from common.merkle_tree import MerkleHash +from common.structs import BenchmarkSettings, OutputData class TestData(unittest.TestCase): def test_calc_solution_signature(self): diff --git a/tig-benchmarker/tests/merkle_tree.py b/tig-benchmarker/tests/merkle_tree.py index f4ebb88..dc7f0df 100644 --- a/tig-benchmarker/tests/merkle_tree.py +++ b/tig-benchmarker/tests/merkle_tree.py @@ -6,7 +6,7 @@ from typing import List sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) -from tig_benchmarker.merkle_tree import MerkleHash, MerkleTree, MerkleBranch +from common.merkle_tree import MerkleHash, MerkleTree, MerkleBranch def create_test_hashes() -> List[MerkleHash]: return [MerkleHash(blake3(i.to_bytes(4, 'big')).digest()) for i in range(9)] diff --git a/tig-benchmarker/tig_benchmarker/extensions/data_fetcher.py b/tig-benchmarker/tig_benchmarker/extensions/data_fetcher.py deleted file mode 100644 index d0de43e..0000000 --- a/tig-benchmarker/tig_benchmarker/extensions/data_fetcher.py +++ /dev/null @@ -1,82 +0,0 @@ -import aiohttp -import asyncio -import json -import logging -import os -from tig_benchmarker.structs import * -from tig_benchmarker.utils import * -from typing import Dict, Any - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -async def _get(url: str) -> Dict[str, Any]: - async with aiohttp.ClientSession() as session: - logger.debug(f"fetching from {url}") - async with session.get(url) as resp: - text = await resp.text() - if resp.status == 200: - return json.loads(text) - else: - if resp.headers.get("Content-Type") == "text/plain": - err_msg = f"status code {resp.status} from {url}: {text}" - else: - err_msg = f"status code {resp.status} from {url}" - logger.error(err_msg) - raise Exception(err_msg) - -class DataFetcher: - def __init__(self, api_url: str, player_id: str): - self.api_url = api_url - self.player_id = player_id - self.last_fetch = 0 - self._cache = None - - async def run(self) -> dict: - logger.debug("fetching latest block") - block_data = await _get(f"{self.api_url}/get-block") - block = Block.from_dict(block_data["block"]) - - if self._cache is not None and block.id == self._cache["block"].id: - logger.debug("no new block data") - return self._cache - - logger.info(f"new block @ height {block.details.height}, fetching data") - tasks = [ - _get(f"{self.api_url}/get-algorithms?block_id={block.id}"), - _get(f"{self.api_url}/get-benchmarks?player_id={self.player_id}&block_id={block.id}"), - _get(f"{self.api_url}/get-challenges?block_id={block.id}") - ] - - algorithms_data, benchmarks_data, challenges_data = await asyncio.gather(*tasks) - - algorithms = {a["id"]: Algorithm.from_dict(a) for a in algorithms_data["algorithms"]} - wasms = {w["algorithm_id"]: Binary.from_dict(w) for w in algorithms_data["binarys"]} - - precommits = {b["benchmark_id"]: Precommit.from_dict(b) for b in benchmarks_data["precommits"]} - benchmarks = {b["id"]: Benchmark.from_dict(b) for b in benchmarks_data["benchmarks"]} - proofs = {p["benchmark_id"]: Proof.from_dict(p) for p in benchmarks_data["proofs"]} - frauds = {f["benchmark_id"]: Fraud.from_dict(f) for f in benchmarks_data["frauds"]} - challenges = {c["id"]: Challenge.from_dict(c) for c in challenges_data["challenges"]} - - tasks = [ - _get(f"{self.api_url}/get-difficulty-data?block_id={block.id}&challenge_id={c_id}") - for c_id in challenges - ] - difficulty_data = await asyncio.gather(*tasks) - difficulty_data = { - c_id: [DifficultyData.from_dict(x) for x in d["data"]] - for c_id, d in zip(challenges, difficulty_data) - } - - self._cache = { - "block": block, - "algorithms": algorithms, - "wasms": wasms, - "precommits": precommits, - "benchmarks": benchmarks, - "proofs": proofs, - "frauds": frauds, - "challenges": challenges, - "difficulty_data": difficulty_data - } - return self._cache diff --git a/tig-benchmarker/tig_benchmarker/extensions/job_manager.py b/tig-benchmarker/tig_benchmarker/extensions/job_manager.py deleted file mode 100644 index e1c2f3d..0000000 --- a/tig-benchmarker/tig_benchmarker/extensions/job_manager.py +++ /dev/null @@ -1,190 +0,0 @@ -import asyncio -import os -import json -import logging -from dataclasses import dataclass -from tig_benchmarker.merkle_tree import MerkleHash, MerkleBranch, MerkleTree -from tig_benchmarker.structs import * -from tig_benchmarker.utils import * -from typing import Dict, List, Optional, Set - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -@dataclass -class Job(FromDict): - benchmark_id: str - settings: BenchmarkSettings - num_nonces: int - rand_hash: str - runtime_config: Dict[str, int] - download_url: str - batch_size: int - challenge: str - sampled_nonces: Optional[List[int]] = field(default_factory=list) - merkle_root: Optional[MerkleHash] = None - solution_nonces: List[int] = field(default_factory=list) - merkle_proofs: Dict[int, MerkleProof] = field(default_factory=dict) - solution_nonces: List[int] = field(default_factory=list) - batch_merkle_proofs: Dict[int, MerkleProof] = field(default_factory=dict) - batch_merkle_roots: List[Optional[MerkleHash]] = None - last_benchmark_submit_time: int = 0 - last_proof_submit_time: int = 0 - last_batch_retry_time: List[int] = None - - def __post_init__(self): - self.batch_merkle_roots = [None] * self.num_batches - self.last_batch_retry_time = [0] * self.num_batches - - @property - def num_batches(self) -> int: - return (self.num_nonces + self.batch_size - 1) // self.batch_size - - @property - def sampled_nonces_by_batch_idx(self) -> Dict[int, List[int]]: - ret = {} - for nonce in self.sampled_nonces: - batch_idx = nonce // self.batch_size - ret.setdefault(batch_idx, []).append(nonce) - return ret - -@dataclass -class JobManagerConfig(FromDict): - backup_folder: str - batch_sizes: Dict[str, int] - -class JobManager: - def __init__(self, config: JobManagerConfig, jobs: List[Job]): - self.config = config - self.jobs = jobs - os.makedirs(self.config.backup_folder, exist_ok=True) - for file in os.listdir(self.config.backup_folder): - if not file.endswith(".json"): - continue - file_path = f"{self.config.backup_folder}/{file}" - logger.info(f"restoring job from {file_path}") - with open(file_path) as f: - job = Job.from_dict(json.load(f)) - self.jobs.append(job) - - def on_new_block( - self, - block: Block, - precommits: Dict[str, Precommit], - benchmarks: Dict[str, Benchmark], - proofs: Dict[str, Proof], - challenges: Dict[str, Challenge], - wasms: Dict[str, Binary], - **kwargs - ): - job_idxs = { - j.benchmark_id: idx - for idx, j in enumerate(self.jobs) - } - # create jobs from confirmed precommits - challenge_id_2_name = { - c.id: c.details.name - for c in challenges.values() - } - for benchmark_id, x in precommits.items(): - if ( - benchmark_id in job_idxs or - benchmark_id in proofs - ): - continue - logger.info(f"creating job from confirmed precommit {benchmark_id}") - c_name = challenge_id_2_name[x.settings.challenge_id] - job = Job( - benchmark_id=benchmark_id, - settings=x.settings, - num_nonces=x.details.num_nonces, - rand_hash=x.details.rand_hash, - runtime_config=block.config["benchmarks"]["runtime_configs"]["wasm"], - batch_size=self.config.batch_sizes[c_name], - challenge=c_name, - download_url=next((w.details.download_url for w in wasms.values() if w.algorithm_id == x.settings.algorithm_id), None) - ) - job_idxs[benchmark_id] = len(self.jobs) - self.jobs.append(job) - - # update jobs from confirmed benchmarks - for benchmark_id, x in benchmarks.items(): - if benchmark_id in proofs: - continue - job = self.jobs[job_idxs[benchmark_id]] - if len(job.sampled_nonces) > 0: - continue - logger.info(f"updating job from confirmed benchmark {benchmark_id}") - job.sampled_nonces = x.details.sampled_nonces - for batch_idx in job.sampled_nonces_by_batch_idx: - job.last_batch_retry_time[batch_idx] = 0 - - # prune jobs from confirmed proofs - prune_idxs = [ - job_idxs[benchmark_id] - for benchmark_id in proofs - if benchmark_id in job_idxs - ] + [ - job_idxs[benchmark_id] - for benchmark_id in job_idxs - if benchmark_id not in precommits - ] - for idx in sorted(set(prune_idxs), reverse=True): - job = self.jobs.pop(idx) - logger.info(f"pruning job {job.benchmark_id}") - if os.path.exists(f"{self.config.backup_folder}/{job.benchmark_id}.json"): - os.remove(f"{self.config.backup_folder}/{job.benchmark_id}.json") - - def run(self): - now = int(time.time() * 1000) - for job in self.jobs: - if job.merkle_root is not None: - continue - num_batches_ready = sum(x is not None for x in job.batch_merkle_roots) - logger.info(f"benchmark {job.benchmark_id}: (batches: {num_batches_ready} of {job.num_batches} ready, #solutions: {len(job.solution_nonces)})") - if num_batches_ready != job.num_batches: - continue - start_time = min(job.last_batch_retry_time) - logger.info(f"benchmark {job.benchmark_id}: ready, took {(now - start_time) / 1000} seconds") - tree = MerkleTree( - job.batch_merkle_roots, - 1 << (job.num_batches - 1).bit_length() - ) - job.merkle_root = tree.calc_merkle_root() - - for job in self.jobs: - if ( - len(job.sampled_nonces) == 0 or # benchmark not confirmed - len(job.merkle_proofs) == len(job.sampled_nonces) # already processed - ): - continue - logger.info(f"proof {job.benchmark_id}: (merkle_proof: {len(job.batch_merkle_proofs)} of {len(job.sampled_nonces)} ready)") - if ( - len(job.batch_merkle_proofs) != len(job.sampled_nonces) or # not finished - any(x is None for x in job.batch_merkle_roots) - ): - continue - logger.info(f"proof {job.benchmark_id}: ready") - depth_offset = (job.batch_size - 1).bit_length() - tree = MerkleTree( - job.batch_merkle_roots, - 1 << (job.num_batches - 1).bit_length() - ) - proofs = {} - sampled_nonces_by_batch_idx = job.sampled_nonces_by_batch_idx - for batch_idx in sampled_nonces_by_batch_idx: - upper_stems = [ - (d + depth_offset, h) - for d, h in tree.calc_merkle_branch(batch_idx).stems - ] - for nonce in set(sampled_nonces_by_batch_idx[batch_idx]): - proof = job.batch_merkle_proofs[nonce] - job.merkle_proofs[nonce] = MerkleProof( - leaf=proof.leaf, - branch=MerkleBranch(proof.branch.stems + upper_stems) - ) - - for job in self.jobs: - file_path = f"{self.config.backup_folder}/{job.benchmark_id}.json" - logger.debug(f"backing up job to {file_path}") - with open(file_path, "w") as f: - json.dump(job.to_dict(), f) \ No newline at end of file diff --git a/tig-benchmarker/tig_benchmarker/extensions/slave_manager.py b/tig-benchmarker/tig_benchmarker/extensions/slave_manager.py deleted file mode 100644 index 3151dd6..0000000 --- a/tig-benchmarker/tig_benchmarker/extensions/slave_manager.py +++ /dev/null @@ -1,142 +0,0 @@ -import asyncio -import os -import json -import logging -import re -import signal -from quart import Quart, request, jsonify -from hypercorn.config import Config -from hypercorn.asyncio import serve -from tig_benchmarker.extensions.job_manager import Job -from tig_benchmarker.structs import * -from tig_benchmarker.utils import * -from typing import Dict, List, Optional, Set - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -@dataclass -class Batch(FromDict): - benchmark_id: str - start_nonce: int - num_nonces: int - settings: BenchmarkSettings - sampled_nonces: List[int] - runtime_config: dict - download_url: str - rand_hash: str - batch_size: int - -@dataclass -class BatchResult(FromDict): - merkle_root: MerkleHash - solution_nonces: List[int] - merkle_proofs: List[MerkleProof] - -@dataclass -class SlaveConfig(FromDict): - name_regex: str - max_concurrent_batches: Dict[str, int] - -@dataclass -class SlaveManagerConfig(FromDict): - port: int - time_before_batch_retry: int - slaves: List[SlaveConfig] - -class SlaveManager: - def __init__(self, config: SlaveManagerConfig, jobs: List[Job]): - self.config = config - self.jobs = jobs - - def start(self): - app = Quart(__name__) - - @app.route('/get-batches', methods=['GET']) - async def get_batches(): - if (slave_name := request.headers.get('User-Agent', None)) is None: - return "User-Agent header is required", 403 - if not any(re.match(slave.name_regex, slave_name) for slave in self.config.slaves): - logger.warning(f"slave {slave_name} does not match any regex. rejecting get-batches request") - return "Unregistered slave", 403 - - slave = next((slave for slave in self.config.slaves if re.match(slave.name_regex, slave_name)), None) - - now = int(time.time() * 1000) - batches = [] - selected_challenge = None - max_concurrent_batches = None - for job in self.jobs: - if ( - job.challenge not in slave.max_concurrent_batches or - (selected_challenge is not None and job.challenge != selected_challenge) - ): - continue - sampled_nonces_by_batch_idx = job.sampled_nonces_by_batch_idx - for batch_idx in range(job.num_batches): - if not ( - now - job.last_batch_retry_time[batch_idx] > self.config.time_before_batch_retry and - ( - job.batch_merkle_roots[batch_idx] is None or - not set(sampled_nonces_by_batch_idx.get(batch_idx, [])).issubset(job.merkle_proofs) - ) - ): - continue - job.last_batch_retry_time[batch_idx] = now - selected_challenge = job.challenge - max_concurrent_batches = slave.max_concurrent_batches[job.challenge] - start_nonce = batch_idx * job.batch_size - batches.append(Batch( - benchmark_id=job.benchmark_id, - start_nonce=start_nonce, - num_nonces=min(job.batch_size, job.num_nonces - start_nonce), - settings=job.settings.to_dict(), - sampled_nonces=sampled_nonces_by_batch_idx.get(batch_idx, []), - runtime_config=job.runtime_config, - download_url=job.download_url, - rand_hash=job.rand_hash, - batch_size=job.batch_size - )) - if len(batches) >= max_concurrent_batches: - break - if max_concurrent_batches is not None and len(batches) >= max_concurrent_batches: - break - - if len(batches) == 0: - logger.debug(f"{slave_name} get-batches: None available") - return "No batches available", 503 - else: - logger.debug(f"{slave_name} get-batches: (challenge: {selected_challenge}, #batches: {len(batches)}, batch_ids: {[b.benchmark_id for b in batches]})") - return jsonify([b.to_dict() for b in batches]) - - @app.route('/submit-batch-result/', methods=['POST']) - async def submit_batch_result(batch_id): - if (slave_name := request.headers.get('User-Agent', None)) is None: - return "User-Agent header is required", 403 - if not any(re.match(slave.name_regex, slave_name) for slave in self.config.slaves): - logger.warning(f"slave {slave_name} does not match any regex. rejecting submit-batch-result request") - benchmark_id, start_nonce = batch_id.split("_") - start_nonce = int(start_nonce) - result = BatchResult.from_dict(await request.json) - job = next((job for job in self.jobs if job.benchmark_id == benchmark_id), None) - logger.debug(f"{slave_name} submit-batch-result: (benchmark_id: {benchmark_id}, start_nonce: {start_nonce}, #solutions: {len(result.solution_nonces)}, #proofs: {len(result.merkle_proofs)})") - if job is None: - logger.warning(f"{slave_name} submit-batch-result: no job found with benchmark_id {benchmark_id}") - return "Invalid benchmark_id", 400 - batch_idx = start_nonce // job.batch_size - job.batch_merkle_roots[batch_idx] = result.merkle_root - job.solution_nonces = list(set(job.solution_nonces + result.solution_nonces)) - job.batch_merkle_proofs.update({ - x.leaf.nonce: x - for x in result.merkle_proofs - }) - return "OK" - - config = Config() - config.bind = [f"0.0.0.0:{self.config.port}"] - - exit_event = asyncio.Event() - loop = asyncio.get_running_loop() - loop.add_signal_handler(signal.SIGINT, exit_event.set) - loop.add_signal_handler(signal.SIGTERM, exit_event.set) - asyncio.create_task(serve(app, config, shutdown_trigger=exit_event.wait)) - logger.info(f"webserver started on {config.bind[0]}") \ No newline at end of file diff --git a/tig-benchmarker/tig_benchmarker/extensions/submissions_manager.py b/tig-benchmarker/tig_benchmarker/extensions/submissions_manager.py deleted file mode 100644 index 97c138a..0000000 --- a/tig-benchmarker/tig_benchmarker/extensions/submissions_manager.py +++ /dev/null @@ -1,97 +0,0 @@ -import aiohttp -import asyncio -import logging -import json -import os -from tig_benchmarker.extensions.job_manager import Job -from tig_benchmarker.structs import * -from tig_benchmarker.utils import * -from typing import Union - -logger = logging.getLogger(os.path.splitext(os.path.basename(__file__))[0]) - -@dataclass -class SubmitPrecommitRequest(FromDict): - settings: BenchmarkSettings - num_nonces: int - -@dataclass -class SubmitBenchmarkRequest(FromDict): - benchmark_id: str - merkle_root: MerkleHash - solution_nonces: Set[int] - -@dataclass -class SubmitProofRequest(FromDict): - benchmark_id: str - merkle_proofs: List[MerkleProof] - -@dataclass -class SubmissionsManagerConfig(FromDict): - time_between_retries: int - -class SubmissionsManager: - def __init__(self, config: SubmissionsManagerConfig, api_url: str, api_key: str, jobs: List[Job]): - self.config = config - self.jobs = jobs - self.api_url = api_url - self.api_key = api_key - - async def _post(self, submission_type: str, req: Union[SubmitPrecommitRequest, SubmitBenchmarkRequest, SubmitProofRequest]): - headers = { - "X-Api-Key": self.api_key, - "Content-Type": "application/json", - "User-Agent": "tig-benchmarker-py/v0.2" - } - if submission_type == "precommit": - logger.info(f"submitting {submission_type}") - else: - logger.info(f"submitting {submission_type} '{req.benchmark_id}'") - logger.debug(f"{req}") - async with aiohttp.ClientSession() as session: - async with session.post(f"{self.api_url}/submit-{submission_type}", json=req.to_dict(), headers=headers) as resp: - text = await resp.text() - if resp.status == 200: - logger.info(f"submitted {submission_type} successfully") - elif resp.headers.get("Content-Type") == "text/plain": - logger.error(f"status {resp.status} when submitting {submission_type}: {text}") - else: - logger.error(f"status {resp.status} when submitting {submission_type}") - - def run(self, submit_precommit_req: Optional[SubmitPrecommitRequest]): - now = int(time.time() * 1000) - if submit_precommit_req is None: - logger.debug("no precommit to submit") - else: - asyncio.create_task(self._post("precommit", submit_precommit_req)) - - for job in self.jobs: - if ( - job.merkle_root is not None and - len(job.sampled_nonces) == 0 and - now - job.last_benchmark_submit_time > self.config.time_between_retries - ): - job.last_benchmark_submit_time = now - asyncio.create_task(self._post("benchmark", SubmitBenchmarkRequest( - benchmark_id=job.benchmark_id, - merkle_root=job.merkle_root.to_str(), - solution_nonces=job.solution_nonces - ))) - break - else: - logger.debug("no benchmark to submit") - - for job in self.jobs: - if ( - len(job.sampled_nonces) > 0 and - len(job.merkle_proofs) == len(job.sampled_nonces) and - now - job.last_proof_submit_time > self.config.time_between_retries - ): - job.last_proof_submit_time = now - asyncio.create_task(self._post("proof", SubmitProofRequest( - benchmark_id=job.benchmark_id, - merkle_proofs=list(job.merkle_proofs.values()) - ))) - break - else: - logger.debug("no proof to submit") \ No newline at end of file diff --git a/tig-benchmarker/ui/.editorconfig b/tig-benchmarker/ui/.editorconfig new file mode 100644 index 0000000..59d9a3a --- /dev/null +++ b/tig-benchmarker/ui/.editorconfig @@ -0,0 +1,16 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/tig-benchmarker/ui/.gitignore b/tig-benchmarker/ui/.gitignore new file mode 100644 index 0000000..cc7b141 --- /dev/null +++ b/tig-benchmarker/ui/.gitignore @@ -0,0 +1,42 @@ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. + +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out + +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors +.idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files +.DS_Store +Thumbs.db diff --git a/tig-benchmarker/ui/Dockerfile b/tig-benchmarker/ui/Dockerfile new file mode 100644 index 0000000..a3fbb68 --- /dev/null +++ b/tig-benchmarker/ui/Dockerfile @@ -0,0 +1,21 @@ +FROM node:latest AS build + +WORKDIR /app + +COPY . . + +RUN npm install -g @angular/cli +RUN npm install --legacy-peer-deps + +ARG CONFIG + +RUN ng build + +FROM nginx:latest + +COPY --from=build /app/dist/tig-brenchmarker-ui/browser /usr/share/nginx/html +COPY ./nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/tig-benchmarker/ui/README.md b/tig-benchmarker/ui/README.md new file mode 100644 index 0000000..e91fdd2 --- /dev/null +++ b/tig-benchmarker/ui/README.md @@ -0,0 +1,27 @@ +# TigBrenchmarkerUi + +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 18.2.6. + +## Development server + +Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. + +## Code scaffolding + +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. + +## Build + +Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Running unit tests + +Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Running end-to-end tests + +Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/tig-benchmarker/ui/angular.json b/tig-benchmarker/ui/angular.json new file mode 100644 index 0000000..3d805e6 --- /dev/null +++ b/tig-benchmarker/ui/angular.json @@ -0,0 +1,113 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "tig-brenchmarker-ui": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { + "outputPath": "dist/tig-brenchmarker-ui", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + }, + { + "glob": "**/*", + "input": "node_modules/monaco-editor/min/vs", + "output": "assets/monaco/vs" + } + ], + "styles": [ + "src/styles.scss", + "node_modules/primeflex/primeflex.css" + ], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "5MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kB", + "maximumError": "4kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": { + "scripts": true, + "styles": true, + "vendor": true + } + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "tig-brenchmarker-ui:build:production" + }, + "development": { + "buildTarget": "tig-brenchmarker-ui:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": ["zone.js", "zone.js/testing"], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", + "assets": [ + { + "glob": "**/*", + "input": "public" + }, + { + "glob": "**/*", + "input": "node_modules/monaco-editor/min/vs", + "output": "assets/monaco/vs" + } + ], + "styles": [ + "src/styles.scss", + "node_modules/primeflex/primeflex.css" + ], + "scripts": [] + } + } + } + } + } +} diff --git a/tig-benchmarker/ui/nginx.conf b/tig-benchmarker/ui/nginx.conf new file mode 100644 index 0000000..a38c2dd --- /dev/null +++ b/tig-benchmarker/ui/nginx.conf @@ -0,0 +1,20 @@ +server { + listen 80; + sendfile on; + default_type application/octet-stream; + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 256; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + + root /usr/share/nginx/html; + + location / { + try_files $uri $uri/ /index.html =404; + } +} \ No newline at end of file diff --git a/tig-benchmarker/ui/package-lock.json b/tig-benchmarker/ui/package-lock.json new file mode 100644 index 0000000..57c33de --- /dev/null +++ b/tig-benchmarker/ui/package-lock.json @@ -0,0 +1,16895 @@ +{ + "name": "tig-brenchmarker-ui", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tig-brenchmarker-ui", + "version": "0.0.0", + "dependencies": { + "@angular/animations": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@coinbase/wallet-sdk": "^4.1.0", + "@ngstack/code-editor": "^9.0.0", + "@walletconnect/client": "^1.8.0", + "@walletconnect/qrcode-modal": "^1.8.0", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "chart.js": "^4.4.6", + "ethers": "^6.13.4", + "i": "^0.3.7", + "install": "^0.13.0", + "monaco-editor": "^0.52.0", + "npm": "^10.9.0", + "primeflex": "^3.3.1", + "primeicons": "^7.0.0", + "primeng": "^17.18.11", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.10" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.2.6", + "@angular/cli": "^18.2.6", + "@angular/compiler-cli": "^18.2.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.2.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.5.2" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1802.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.10.tgz", + "integrity": "sha512-/xudcHK2s4J/GcL6qyobmGaWMHQcYLSMqCaWMT+nK6I6tu9VEAj/p3R83Tzx8B/eKi31Pz499uHw9pmqdtbafg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "18.2.10", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.10.tgz", + "integrity": "sha512-47XgJ5fdIqlZUFWAo/XtNsh3y597DtLZWvfsnwShw6/TgyiV0rbL1Z24Rn2TCV1D/b3VhLutAIIZ/i5O5BirxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.10", + "@angular-devkit/build-webpack": "0.1802.10", + "@angular-devkit/core": "18.2.10", + "@angular/build": "18.2.10", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.10", + "@vitejs/plugin-basic-ssl": "1.1.0", + "ansi-colors": "4.1.3", + "autoprefixer": "10.4.20", + "babel-loader": "9.1.3", + "browserslist": "^4.21.5", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", + "fast-glob": "3.3.2", + "http-proxy-middleware": "3.0.3", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "karma-source-map-support": "1.4.0", + "less": "4.2.0", + "less-loader": "12.2.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", + "mrmime": "2.0.0", + "open": "10.1.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", + "postcss-loader": "8.1.1", + "resolve-url-loader": "5.0.0", + "rxjs": "7.8.1", + "sass": "1.77.6", + "sass-loader": "16.0.0", + "semver": "7.6.3", + "source-map-loader": "5.0.0", + "source-map-support": "0.5.21", + "terser": "5.31.6", + "tree-kill": "1.2.2", + "tslib": "2.6.3", + "vite": "5.4.6", + "watchpack": "2.4.1", + "webpack": "5.94.0", + "webpack-dev-middleware": "7.4.2", + "webpack-dev-server": "5.0.4", + "webpack-merge": "6.0.1", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.23.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "@web/test-runner": "^0.18.0", + "browser-sync": "^3.0.2", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "karma": "^6.3.0", + "ng-packagr": "^18.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@web/test-runner": { + "optional": true + }, + "browser-sync": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1802.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.10.tgz", + "integrity": "sha512-WRftK/RJ9rBDDmkx5IAtIpyNo0DJiMfgGUTuZNpNUaJfSfGeaSZYgC7o1++axMchID8pncmI3Hr8L8gaP94WQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1802.10", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^5.0.2" + } + }, + "node_modules/@angular-devkit/core": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.10.tgz", + "integrity": "sha512-LFqiNdraBujg8e1lhuB0bkFVAoIbVbeXXwfoeROKH60OPbP8tHdgV6sFTqU7UGBKA+b+bYye70KFTG2Ys8QzKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.10.tgz", + "integrity": "sha512-EIm/yCYg3ZYPsPYJxXRX5F6PofJCbNQ5rZEuQEY09vy+ZRTqGezH0qoUP5WxlYeJrjiRLYqADI9WtVNzDyaD4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "18.2.10", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.9.tgz", + "integrity": "sha512-GAsTKENoTRVKgXX4ACBMMTp8SW4rW8u637uLag+ttJV2XBzC3YJlw5m6b/W4cdrmqZjztoEwUjR6CUTjBqMujQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.9" + } + }, + "node_modules/@angular/build": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.10.tgz", + "integrity": "sha512-YFBKvAyC5sH17yRYcx7VHCtJ4KUg7xCjCQ4Pe16kiTvW6vuYsgU6Btyti0Qgewd7XaWpTM8hk8N6hE4Z0hpflw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.10", + "@babel/core": "7.25.2", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.22.4", + "sass": "1.77.6", + "semver": "7.6.3", + "vite": "5.4.6", + "watchpack": "2.4.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular/cli": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.10.tgz", + "integrity": "sha512-qW/F3XVZMzzenFzbn+7FGpw8GOt9qW8UxBtYya7gUNdWlcsgGUk+ZaGC2OLbfI5gX6pchW4TOPMsDSMeaCEI2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1802.10", + "@angular-devkit/core": "18.2.10", + "@angular-devkit/schematics": "18.2.10", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.10", + "@yarnpkg/lockfile": "1.1.0", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", + "pacote": "18.0.6", + "resolve": "1.22.8", + "semver": "7.6.3", + "symbol-observable": "4.0.0", + "yargs": "17.7.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.9.tgz", + "integrity": "sha512-Opi6DVaU0aGyJqLk5jPmeYx559fp3afj4wuxM5aDzV4KEVGDVbNCpO0hMuwHZ6rtCjHhv1fQthgS48qoiQ6LKw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.9.tgz", + "integrity": "sha512-fchbcbsyTOd/qHGy+yPEmE1p10OTNEjGrWHQzUbf3xdlm23EvxHTitHh8i6EBdwYnM5zz0IIBhltP8tt89oeYw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "18.2.9" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.9.tgz", + "integrity": "sha512-4iMoRvyMmq/fdI/4Gob9HKjL/jvTlCjbS4kouAYHuGO9w9dmUhi1pY1z+mALtCEl9/Q8CzU2W8e5cU2xtV4nVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.25.2", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/compiler": "18.2.9", + "typescript": ">=5.4 <5.6" + } + }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/core": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.9.tgz", + "integrity": "sha512-h9/Bzo/7LTPzzh9I/1Gk8TWOXPGeHt3jLlnYrCh2KbrWbTErNtW0V3ad5I3Zv+K2Z7RSl9Z3D3Y6ILH796N4ZA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.14.10" + } + }, + "node_modules/@angular/forms": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.9.tgz", + "integrity": "sha512-yyN5dG60CXH6MRte8rv4aGUTeNOMz/pUV7rVxittpjN7tPHfGEL9Xz89Or90Aa1QiHuBmHFk+9A39s03aO1rDQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.9", + "@angular/core": "18.2.9", + "@angular/platform-browser": "18.2.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.9.tgz", + "integrity": "sha512-UNu6XjK0SV35FFe55yd1yefZI8tzflVKzev/RzC31XngrczhlH0+WCbae4rG1XJULzJwJ1R1p7gqq4+ktEczRQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "18.2.9", + "@angular/common": "18.2.9", + "@angular/core": "18.2.9" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.9.tgz", + "integrity": "sha512-cUTB8Jc3I/fu2UKv/PJmNGQGvKyyTo8ln4GUX3EJ4wUHzgkrU0s4x7DNok0Ql8FZKs5dLR8C0xVbG7Dv/ViPdw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.9", + "@angular/compiler": "18.2.9", + "@angular/core": "18.2.9", + "@angular/platform-browser": "18.2.9" + } + }, + "node_modules/@angular/router": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.9.tgz", + "integrity": "sha512-D0rSrMf/sbhr5yQgz+LNBxdv1BR3S4pYDj1Exq6yVRKX8HSbjc5hxe/44VaOEKBh8StJ6GRiNOMoIcDt73Jang==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/common": "18.2.9", + "@angular/core": "18.2.9", + "@angular/platform-browser": "18.2.9", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", + "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", + "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", + "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", + "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", + "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", + "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", + "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", + "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", + "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", + "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", + "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", + "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", + "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-simple-access": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", + "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", + "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", + "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", + "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.0", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@coinbase/wallet-sdk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@coinbase/wallet-sdk/-/wallet-sdk-4.1.0.tgz", + "integrity": "sha512-SkJJ72X/AA3+aS21sPs/4o4t6RVeDSA7HuBW4zauySX3eBiPU0zmVw95tXH/eNSX50agKz9WzeN8P5F+HcwLOw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.4.0", + "clsx": "^1.2.1", + "eventemitter3": "^5.0.1", + "preact": "^10.16.0" + } + }, + "node_modules/@coinbase/wallet-sdk/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", + "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", + "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.6", + "@inquirer/type": "^2.0.0", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.5.5", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@inquirer/type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", + "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", + "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/expand": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", + "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.7.tgz", + "integrity": "sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", + "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", + "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", + "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", + "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", + "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", + "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.1.0", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.3", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^1.5.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@ngstack/code-editor": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@ngstack/code-editor/-/code-editor-9.0.0.tgz", + "integrity": "sha512-sioi0qyeo9Q8PIdhGFmUeuEx6LETqSjC/4A1Fnl9HFVYzCU7mDVdyeUITubu+4THjraH1mIAVw2t0R7rDgdHdA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.5.0" + }, + "peerDependencies": { + "@angular/common": ">=17.1.1", + "@angular/core": ">=17.1.1" + } + }, + "node_modules/@ngtools/webpack": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.10.tgz", + "integrity": "sha512-CGYr8rdM5ntdb4kLUAhrLBPrhJQ4KBPo3KMT6qJE/S+jJJn5zHzedpuGFOCVhC1Siw+n1pOBSI8leTRJIW/eCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", + "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@schematics/angular": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.10.tgz", + "integrity": "sha512-2pDHT4aSzfs8Up4RQmHHuFd5FeuUebS1ZJwyt46MfXzRMFtzUZV/JKsIvDqyMwnkvFfLvgJyTCkl8JGw5jQObg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "18.2.10", + "@angular-devkit/schematics": "18.2.10", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", + "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.1.tgz", + "integrity": "sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/express/node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.4.tgz", + "integrity": "sha512-px7OMFO/ncXxixDe1zR13V1iycqWae0MxTaw62RpFlksUi5QuNWgQJFkTQjIOvrmutJbI7Fp2Y2N1F6D2R4G6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "22.8.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.1.tgz", + "integrity": "sha512-k6Gi8Yyo8EtrNtkHXutUu2corfDf9su95VYVP10aGYMMROM6SAItZi0w1XszA6RtWTHSVp5OeFof37w0IEqCQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@walletconnect/browser-utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/browser-utils/-/browser-utils-1.8.0.tgz", + "integrity": "sha512-Wcqqx+wjxIo9fv6eBUFHPsW1y/bGWWRboni5dfD8PtOmrihrEpOCmvRJe4rfl7xgJW8Ea9UqKEaq0bIRLHlK4A==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/safe-json": "1.0.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/window-getters": "1.0.0", + "@walletconnect/window-metadata": "1.0.0", + "detect-browser": "5.2.0" + } + }, + "node_modules/@walletconnect/client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/client/-/client-1.8.0.tgz", + "integrity": "sha512-svyBQ14NHx6Cs2j4TpkQaBI/2AF4+LXz64FojTjMtV4VMMhl81jSO1vNeg+yYhQzvjcGH/GpSwixjyCW0xFBOQ==", + "deprecated": "WalletConnect's v1 SDKs are now deprecated. Please upgrade to a v2 SDK. For details see: https://docs.walletconnect.com/", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "^1.8.0", + "@walletconnect/iso-crypto": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-1.8.0.tgz", + "integrity": "sha512-aFTHvEEbXcZ8XdWBw6rpQDte41Rxwnuk3SgTD8/iKGSRTni50gI9S3YEzMj05jozSiOBxQci4pJDMVhIUMtarw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/socket-transport": "^1.8.0", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/crypto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/crypto/-/crypto-1.0.3.tgz", + "integrity": "sha512-+2jdORD7XQs76I2Odgr3wwrtyuLUXD/kprNVsjWRhhhdO9Mt6WqVzOPu0/t7OHSmgal8k7SoBQzUc5hu/8zL/g==", + "license": "MIT", + "dependencies": { + "@walletconnect/encoding": "^1.0.2", + "@walletconnect/environment": "^1.0.1", + "@walletconnect/randombytes": "^1.0.3", + "aes-js": "^3.1.2", + "hash.js": "^1.1.7", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/crypto/node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==", + "license": "MIT" + }, + "node_modules/@walletconnect/crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/encoding": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/encoding/-/encoding-1.0.2.tgz", + "integrity": "sha512-CrwSBrjqJ7rpGQcTL3kU+Ief+Bcuu9PH6JLOb+wM6NITX1GTxR/MfNwnQfhLKK6xpRAyj2/nM04OOH6wS8Imag==", + "license": "MIT", + "dependencies": { + "is-typedarray": "1.0.0", + "tslib": "1.14.1", + "typedarray-to-buffer": "3.1.5" + } + }, + "node_modules/@walletconnect/encoding/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", + "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/environment/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/iso-crypto": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/iso-crypto/-/iso-crypto-1.8.0.tgz", + "integrity": "sha512-pWy19KCyitpfXb70hA73r9FcvklS+FvO9QUIttp3c2mfW8frxgYeRXfxLRCIQTkaYueRKvdqPjbyhPLam508XQ==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/crypto": "^1.0.2", + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", + "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", + "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", + "license": "MIT", + "dependencies": { + "@walletconnect/environment": "^1.0.1", + "@walletconnect/jsonrpc-types": "^1.0.3", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/jsonrpc-utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/mobile-registry": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@walletconnect/mobile-registry/-/mobile-registry-1.4.0.tgz", + "integrity": "sha512-ZtKRio4uCZ1JUF7LIdecmZt7FOLnX72RPSY7aUVu7mj7CSfxDwUn6gBuK6WGtH+NZCldBqDl5DenI5fFSvkKYw==", + "deprecated": "Deprecated in favor of dynamic registry available from: https://github.com/walletconnect/walletconnect-registry", + "license": "MIT" + }, + "node_modules/@walletconnect/qrcode-modal": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/qrcode-modal/-/qrcode-modal-1.8.0.tgz", + "integrity": "sha512-BueaFefaAi8mawE45eUtztg3ZFbsAH4DDXh1UNwdUlsvFMjqcYzLUG0xZvDd6z2eOpbgDg2N3bl6gF0KONj1dg==", + "deprecated": "WalletConnect's v1 SDKs are now deprecated. Please upgrade to a v2 SDK. For details see: https://docs.walletconnect.com/", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/mobile-registry": "^1.4.0", + "@walletconnect/types": "^1.8.0", + "copy-to-clipboard": "^3.3.1", + "preact": "10.4.1", + "qrcode": "1.4.4" + } + }, + "node_modules/@walletconnect/qrcode-modal/node_modules/preact": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.4.1.tgz", + "integrity": "sha512-WKrRpCSwL2t3tpOOGhf2WfTpcmbpxaWtDbdJdKdjd0aEiTkvOmS4NBkG6kzlaAHI9AkQ3iVqbFWM3Ei7mZ4o1Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@walletconnect/randombytes": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@walletconnect/randombytes/-/randombytes-1.0.3.tgz", + "integrity": "sha512-35lpzxcHFbTN3ABefC9W+uBpNZl1GC4Wpx0ed30gibfO/y9oLdy1NznbV96HARQKSBV9J9M/rrtIvf6a23jfYw==", + "license": "MIT", + "dependencies": { + "@walletconnect/encoding": "^1.0.2", + "@walletconnect/environment": "^1.0.1", + "randombytes": "^2.1.0", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/randombytes/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.0.tgz", + "integrity": "sha512-QJzp/S/86sUAgWY6eh5MKYmSfZaRpIlmCJdi5uG4DJlKkZrHEF7ye7gA+VtbVzvTtpM/gRwO2plQuiooIeXjfg==", + "license": "MIT" + }, + "node_modules/@walletconnect/socket-transport": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/socket-transport/-/socket-transport-1.8.0.tgz", + "integrity": "sha512-5DyIyWrzHXTcVp0Vd93zJ5XMW61iDM6bcWT4p8DTRfFsOtW46JquruMhxOLeCOieM4D73kcr3U7WtyR4JUsGuQ==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/types": "^1.8.0", + "@walletconnect/utils": "^1.8.0", + "ws": "7.5.3" + } + }, + "node_modules/@walletconnect/socket-transport/node_modules/ws": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.3.tgz", + "integrity": "sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@walletconnect/types": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-1.8.0.tgz", + "integrity": "sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg==", + "deprecated": "WalletConnect's v1 SDKs are now deprecated. Please upgrade to a v2 SDK. For details see: https://docs.walletconnect.com/", + "license": "Apache-2.0" + }, + "node_modules/@walletconnect/utils": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-1.8.0.tgz", + "integrity": "sha512-zExzp8Mj1YiAIBfKNm5u622oNw44WOESzo6hj+Q3apSMIb0Jph9X3GDIdbZmvVZsNPxWDL7uodKgZcCInZv2vA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/browser-utils": "^1.8.0", + "@walletconnect/encoding": "^1.0.1", + "@walletconnect/jsonrpc-utils": "^1.0.3", + "@walletconnect/types": "^1.8.0", + "bn.js": "4.11.8", + "js-sha3": "0.8.0", + "query-string": "6.13.5" + } + }, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.0.tgz", + "integrity": "sha512-xB0SQsLaleIYIkSsl43vm8EwETpBzJ2gnzk7e0wMF3ktqiTGS6TFHxcprMl5R44KKh4tCcHCJwolMCaDSwtAaA==", + "license": "MIT" + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.0.tgz", + "integrity": "sha512-9eFvmJxIKCC3YWOL97SgRkKhlyGXkrHwamfechmqszbypFspaSk+t2jQXAEU7YClHF6Qjw5eYOmy1//zFi9/GA==", + "license": "MIT", + "dependencies": { + "@walletconnect/window-getters": "^1.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "license": "MIT" + }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "license": "MIT" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001672", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001672.tgz", + "integrity": "sha512-XhW1vRo1ob6aeK2w3rTohwTPBLse/rvjq+s3RTSBwnlZqoFFjx9cHsShJjAIbLsLjyoacaTxpLZy9v3gg6zypw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chart.js": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz", + "integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-deep/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/critters": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.2.0.tgz", + "integrity": "sha512-tr7XntDAu50BVENgQfajMLzacmSe34D+qZc4zjnniz0ZVuw/TZcLcyxHQjYpJTM36sGEkZZlYLnIM1hH7alTMA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.47", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.47.tgz", + "integrity": "sha512-zS5Yer0MOYw4rtK2iq43cJagHZ8sXN0jDHDKzB+86gSBSAI4v07S97mcq+Gs2vclAxSh1j7vOAHxSVgduiiuVQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz", + "integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.1.tgz", + "integrity": "sha512-QHuXVeZx9d+tIQAz/XztU0ZwZf2Agg9CcXcgE1rurqvdBeDBrpSwjl8/6XUqMg7tw2Y7uAdKb2sRv+bSEFqQ5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", + "dev": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ethers": { + "version": "6.13.4", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.4.tgz", + "integrity": "sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.3.tgz", + "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.18" + } + }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine-core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz", + "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "license": "ISC", + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lmdb": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz", + "integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/monaco-editor": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.0.tgz", + "integrity": "sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==", + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.0.tgz", + "integrity": "sha512-ZanDioFylI9helNhl2LNd+ErmVD+H5I53ry41ixlLyCBgkuYb+58CvbAp99hW+zr5L9W4X7CchSoeqKdngOLSw==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "@npmcli/promise-spawn": "^8.0.1", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.0", + "ini": "^5.0.0", + "init-package-json": "^7.0.1", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.0", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^10.2.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.1", + "npm-user-validate": "^3.0.0", + "p-map": "^4.0.0", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/agent": { + "version": "2.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/@npmcli/fs": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/cacache": { + "version": "18.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "13.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/proc-log": { + "version": "4.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/ssri": { + "version": "10.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.6", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^2.2.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "10.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/agent": { + "version": "2.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/cacache": { + "version": "18.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "13.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/nopt": { + "version": "7.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { + "version": "4.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/ssri": { + "version": "10.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.18", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/agent": { + "version": "2.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@npmcli/fs": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/cacache": { + "version": "18.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "13.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/minipass-fetch": { + "version": "3.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/proc-log": { + "version": "4.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/ssri": { + "version": "10.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ordered-binary": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.2.tgz", + "integrity": "sha512-JTo+4+4Fw7FreyAvlSLjb1BBVaxEQAacmjD3jjuyPZclpbEghTvQZbXBb2qPd2LeIMxiHwXBZUcpmG2Gl/mDEA==", + "dev": true, + "license": "MIT" + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/piscina": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/primeflex": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz", + "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==", + "license": "MIT" + }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, + "node_modules/primeng": { + "version": "17.18.11", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.11.tgz", + "integrity": "sha512-LzV0fFZmb3GdnaRqi1+GP+RPtW0a+jztL5pH1zRWY7+7pyQ0n1YNyTXzmqVcdks/CmoyjNhutWEmexwi6vFVeA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "rxjs": "^6.0.0 || ^7.8.1", + "zone.js": "~0.14.0" + } + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qrcode": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", + "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", + "license": "MIT", + "dependencies": { + "buffer": "^5.4.3", + "buffer-alloc": "^1.2.0", + "buffer-from": "^1.1.1", + "dijkstrajs": "^1.0.1", + "isarray": "^2.0.1", + "pngjs": "^3.3.0", + "yargs": "^13.2.4" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/qrcode/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/qrcode/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/qrcode/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "license": "MIT" + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/qrcode/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.5.tgz", + "integrity": "sha512-svk3xg9qHR39P3JlHuD7g3nRnyay5mHbrPctEBDUxUkHRifPHXJDhBUycdCC0NBjXoDf44Gb+IsOZL1Uwn8M/Q==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.2.tgz", + "integrity": "sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true, + "license": "MIT" + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/terser": { + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" + } + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toggle-selection": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.39", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.39.tgz", + "integrity": "sha512-IZ6acm6RhQHNibSt7+c09hhvsKy9WUr4DVbeq9U8o71qxyYtJpQeDxQnMrVqnIFMLcQjHO0I9wgfO2vIahht4w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack": { + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", + "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^4.6.0", + "mime-types": "^2.1.31", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.4.0", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", + "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zone.js": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", + "license": "MIT" + } + } +} diff --git a/tig-benchmarker/ui/package.json b/tig-benchmarker/ui/package.json new file mode 100644 index 0000000..fa9c354 --- /dev/null +++ b/tig-benchmarker/ui/package.json @@ -0,0 +1,56 @@ +{ + "name": "tig-brenchmarker-ui", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@coinbase/wallet-sdk": "^4.1.0", + "@ngstack/code-editor": "^9.0.0", + "@walletconnect/client": "^1.8.0", + "@walletconnect/qrcode-modal": "^1.8.0", + "axios": "^1.7.7", + "bignumber.js": "^9.1.2", + "chart.js": "^4.4.6", + "ethers": "^6.13.4", + "i": "^0.3.7", + "install": "^0.13.0", + "monaco-editor": "^0.52.0", + "npm": "^10.9.0", + "primeflex": "^3.3.1", + "primeicons": "^7.0.0", + "primeng": "^17.18.11", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.14.10" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^18.2.6", + "@angular/cli": "^18.2.6", + "@angular/compiler-cli": "^18.2.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.2.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.5.2" + }, + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "*" + } +} diff --git a/tig-benchmarker/ui/public/favicon.ico b/tig-benchmarker/ui/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/tig-benchmarker/ui/public/favicon.ico differ diff --git a/tig-benchmarker/ui/src/app/app.component.html b/tig-benchmarker/ui/src/app/app.component.html new file mode 100644 index 0000000..559d81e --- /dev/null +++ b/tig-benchmarker/ui/src/app/app.component.html @@ -0,0 +1,5 @@ +
+ +
+
+
diff --git a/tig-benchmarker/ui/src/app/app.component.scss b/tig-benchmarker/ui/src/app/app.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/app.component.ts b/tig-benchmarker/ui/src/app/app.component.ts new file mode 100644 index 0000000..b8a81b6 --- /dev/null +++ b/tig-benchmarker/ui/src/app/app.component.ts @@ -0,0 +1,31 @@ +import { Component, inject } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { NavBarComponent } from './components/nav-bar/nav-bar.component'; +import { SidebarModule } from 'primeng/sidebar'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ButtonModule } from 'primeng/button'; +import { TigApisService } from './services/tig-apis.service'; +import { ToastModule } from 'primeng/toast'; +import { MessageService } from 'primeng/api'; + +@Component({ + selector: 'app-root', + standalone: true, + imports: [ + RouterOutlet, + NavBarComponent, + SidebarModule, + FormsModule, + ReactiveFormsModule, + ToastModule, + ButtonModule, + ], + templateUrl: './app.component.html', + styleUrl: './app.component.scss', + providers: [MessageService, TigApisService], +}) +export class AppComponent { + tigService = inject(TigApisService); + sidebarVisible = true; + title = 'tig-brenchmarker-ui'; +} diff --git a/tig-benchmarker/ui/src/app/app.config.ts b/tig-benchmarker/ui/src/app/app.config.ts new file mode 100644 index 0000000..4200802 --- /dev/null +++ b/tig-benchmarker/ui/src/app/app.config.ts @@ -0,0 +1,14 @@ +import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { CodeEditorModule } from '@ngstack/code-editor'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { routes } from './app.routes'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAnimations(), + importProvidersFrom(CodeEditorModule.forRoot()), + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + ], +}; diff --git a/tig-benchmarker/ui/src/app/app.routes.ts b/tig-benchmarker/ui/src/app/app.routes.ts new file mode 100644 index 0000000..bb140c6 --- /dev/null +++ b/tig-benchmarker/ui/src/app/app.routes.ts @@ -0,0 +1,15 @@ +import { Routes } from '@angular/router'; +import { HomeComponent } from './pages/home/home.component'; +import { ConfigComponent } from './pages/config/config.component'; + +export const routes: Routes = [ + { + path: 'home', + component: HomeComponent, + }, + { + path: 'config', + component: ConfigComponent, + }, + { path: '**', redirectTo: 'home' }, +]; diff --git a/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.html b/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.html new file mode 100644 index 0000000..3b98a7f --- /dev/null +++ b/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.html @@ -0,0 +1,199 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +

Home

+
+
+ +

Config

+
+ + + +
+ +
+

Version : 1.0

+
+
diff --git a/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.scss b/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.ts b/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.ts new file mode 100644 index 0000000..b6db467 --- /dev/null +++ b/tig-benchmarker/ui/src/app/components/nav-bar/nav-bar.component.ts @@ -0,0 +1,25 @@ +import { Component, inject, NgZone } from '@angular/core'; +import { ButtonModule } from 'primeng/button'; +import { DialogModule } from 'primeng/dialog'; +import { Router } from '@angular/router'; +import { TigApisService } from '../../services/tig-apis.service'; +import { MessageService } from 'primeng/api'; +import { ProgressBarModule } from 'primeng/progressbar'; +@Component({ + selector: 'app-nav-bar', + standalone: true, + imports: [ButtonModule, DialogModule, ProgressBarModule], + templateUrl: './nav-bar.component.html', + styleUrl: './nav-bar.component.scss', + providers: [MessageService], +}) +export class NavBarComponent { + tigService = inject(TigApisService); + router = inject(Router); + + + + constructor() {} + + +} diff --git a/tig-benchmarker/ui/src/app/interfaces/IBatch.ts b/tig-benchmarker/ui/src/app/interfaces/IBatch.ts new file mode 100644 index 0000000..53f667d --- /dev/null +++ b/tig-benchmarker/ui/src/app/interfaces/IBatch.ts @@ -0,0 +1,12 @@ +export interface IBatch { + age: number; + solutions: number; + difficulty: string; + qualifiers: number; + imbalance: number; + num_solutions: number; + num_nonces: number; + slave_id: string; + elapsed_time: number; + status: string; +} diff --git a/tig-benchmarker/ui/src/app/interfaces/IBenchmark.ts b/tig-benchmarker/ui/src/app/interfaces/IBenchmark.ts new file mode 100644 index 0000000..94656f2 --- /dev/null +++ b/tig-benchmarker/ui/src/app/interfaces/IBenchmark.ts @@ -0,0 +1,15 @@ +export interface IBenchmark { + id:string; + age: number; + challenge_id: string; + algorithm_id: string; + solutions: number; + difficulty: string; + submission_delay: number; + qualifiers: number; + start_time:string; + end_time:string; + number_of_nonces:number; + status:string; + time_elapsed?:number; +} \ No newline at end of file diff --git a/tig-benchmarker/ui/src/app/interfaces/IBlock.ts b/tig-benchmarker/ui/src/app/interfaces/IBlock.ts new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/interfaces/IChallangeSummary.ts b/tig-benchmarker/ui/src/app/interfaces/IChallangeSummary.ts new file mode 100644 index 0000000..6fc6efb --- /dev/null +++ b/tig-benchmarker/ui/src/app/interfaces/IChallangeSummary.ts @@ -0,0 +1,5 @@ +export interface IChallengeSummary { + base_fee: string; + solutions: number; + qualifiers: number; +} diff --git a/tig-benchmarker/ui/src/app/interfaces/IConfig.ts b/tig-benchmarker/ui/src/app/interfaces/IConfig.ts new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/interfaces/ICutoff.ts b/tig-benchmarker/ui/src/app/interfaces/ICutoff.ts new file mode 100644 index 0000000..2648acd --- /dev/null +++ b/tig-benchmarker/ui/src/app/interfaces/ICutoff.ts @@ -0,0 +1,8 @@ +export interface ICutoff { + cutoff: number; + c001_qualifiers: number; + c002_qualifiers: number; + c003_qualifiers: number; + c004_qualifiers: number; + deposit: number; +} \ No newline at end of file diff --git a/tig-benchmarker/ui/src/app/interfaces/IImbalance.ts b/tig-benchmarker/ui/src/app/interfaces/IImbalance.ts new file mode 100644 index 0000000..57193dc --- /dev/null +++ b/tig-benchmarker/ui/src/app/interfaces/IImbalance.ts @@ -0,0 +1,13 @@ +export interface IImbalance { + imbalance: number; + c001_qualifiers: number; + c002_qualifiers: number; + c003_qualifiers: number; + c004_qualifiers: number; + c001_solutions: number; + c002_solutions: number; + c003_solutions: number; + c004_solutions: number; + deposit: number; + deposit_qualifiers: number; +} diff --git a/tig-benchmarker/ui/src/app/interfaces/ISlave.ts b/tig-benchmarker/ui/src/app/interfaces/ISlave.ts new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/pages/config/config.component.html b/tig-benchmarker/ui/src/app/pages/config/config.component.html new file mode 100644 index 0000000..dd6894d --- /dev/null +++ b/tig-benchmarker/ui/src/app/pages/config/config.component.html @@ -0,0 +1,47 @@ +
+ +
+ +
+ +
diff --git a/tig-benchmarker/ui/src/app/pages/config/config.component.scss b/tig-benchmarker/ui/src/app/pages/config/config.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/tig-benchmarker/ui/src/app/pages/config/config.component.ts b/tig-benchmarker/ui/src/app/pages/config/config.component.ts new file mode 100644 index 0000000..f32dbfe --- /dev/null +++ b/tig-benchmarker/ui/src/app/pages/config/config.component.ts @@ -0,0 +1,89 @@ +import { Component, inject, input, signal } from '@angular/core'; + +import { CodeEditorModule, CodeModel } from '@ngstack/code-editor'; +import { + FormControl, + FormGroup, + FormsModule, + ReactiveFormsModule, +} from '@angular/forms'; +import { InputTextModule } from 'primeng/inputtext'; +import { CardModule } from 'primeng/card'; +import { InputNumberModule } from 'primeng/inputnumber'; +import { ButtonModule } from 'primeng/button'; +import { merge, tap } from 'rxjs'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { TigApisService } from '../../services/tig-apis.service'; +import { SliderModule } from 'primeng/slider'; +import { DropdownModule } from 'primeng/dropdown'; +@Component({ + selector: 'app-settings', + standalone: true, + + imports: [ + CardModule, + ButtonModule, + ReactiveFormsModule, + FormsModule, + CodeEditorModule, + SliderModule, + DropdownModule, + InputNumberModule, + InputTextModule, + ], + templateUrl: './config.component.html', + styleUrl: './config.component.scss', +}) +export class ConfigComponent { + tigService: any = inject(TigApisService); + settings: any = signal(null); + slave: any = input(null); + error: any = signal(null); + currentConfig = signal(null); + theme = 'vs-dark'; + model: CodeModel = { + language: 'json', + uri: 'main.json', + value: '{}', + }; + options = { + contextmenu: true, + minimap: { + enabled: true, + }, + }; + async ngOnInit() { + this.tigService.config$.subscribe((data: any) => { + this.setCode(data); + }); + } + + onCodeChanged(value: any) { + this.currentConfig.set(value); + this.testCode(); + } + + testCode() { + // check valid json + try { + const code: any = this.currentConfig(); + JSON.parse(code); + this.error.set(null); + } catch (e) { + this.error.set('Config is not valid JSON'); + } + } + + setCode(code: any) { + this.currentConfig.set(code); + this.model = { + language: 'json', + uri: 'main.json', + value: JSON.stringify(code, null, 2), + }; + } + + async save() { + this.tigService.saveConfig(this.currentConfig()); + } +} diff --git a/tig-benchmarker/ui/src/app/pages/home/home.component.html b/tig-benchmarker/ui/src/app/pages/home/home.component.html new file mode 100644 index 0000000..ea76a45 --- /dev/null +++ b/tig-benchmarker/ui/src/app/pages/home/home.component.html @@ -0,0 +1,335 @@ +@if(tigService.ready()){ +
+ + @if(benchmarks()){ +
+
+ +
+ + + {{ 10 - value / 10 }}s + + +
+ +

Data Refresh

+
+
+ + + + + + + BENCHMARK ID + + + CHALLENGE + + + ALGORITHM + + + DIFFICULTY + + + BATCH SIZE + + + #NONCES + + #SOLUTIONS + + + STATUS + + ELAPSED + + + + + + + + + + +
+ {{ benchmark.benchmark_id_display }} + +
+ + +
+ {{ benchmark.challenge }} +
+ + +
+ {{ benchmark.algorithm }} +
+ + +
+ [{{ benchmark.difficulty }}] +
+ + +
+ {{ benchmark.batch_size }} +
+ + +
+ {{ benchmark.num_nonces }} +
+ + +
+ {{ benchmark.num_solutions }} +
+ + + +
+ +
+ + + +
+ {{ benchmark.time_elapsed | timeConverter }} +
+ + +
+ +
+ + +
+ + + +
+ + + + + BATCH NO + + + SLAVE + + + #NONCES + + + + #SOLUTIONS + + + STATUS + + + ELAPSED + + + + + + + +
+ {{ batch.batch_idx + 1 }} +
+ + +
+ {{ batch.slave }} +
+ + +
+ {{ batch.num_nonces }} +
+ + +
+ {{ batch.end_time ? batch.num_solutions : "" }} +
+ + +
+ +
+ + +
+ {{ + batch.time_elapsed != null ? batch.time_elapsed + "ms" : "" + }} +
+ + +
+ +
+ + +
+ + + + There are no batches for this Benchmark yet. + + + +
+
+ + + +
+
+ } @else { +
+ +
+ } + +
+} @else { +
+ +
+} diff --git a/tig-benchmarker/ui/src/app/pages/home/home.component.scss b/tig-benchmarker/ui/src/app/pages/home/home.component.scss new file mode 100644 index 0000000..16f5b56 --- /dev/null +++ b/tig-benchmarker/ui/src/app/pages/home/home.component.scss @@ -0,0 +1,80 @@ +/* app.component.css */ +.custom-panel { + border-radius: 10px; + padding: 20px; + font-family: Arial, sans-serif; +} + +.cutoff-section { + display: flex; + align-items: center; + font-size: 14px; + margin-bottom: 15px; +} + +.cutoff-label { + font-weight: bold; + margin-right: 5px; +} + +.cutoff-value { + font-size: 18px; + font-weight: bold; + margin: 0 5px; +} + +.cutoff-equation { + font-style: italic; +} + +.solutions-section { + border-left: 2px solid #000; + border-right: 2px solid #000; + border-radius: 25px; + padding: 10px; +} + +.active-solutions-title { + font-weight: bold; + font-size: 12px; + text-align: right; + margin-bottom: 10px; +} + +.solution-item { + display: flex; + width: 100%; + align-items: center; + font-size: 14px; + margin: 5px 0; +} + +.highlighted { + background-color: #f8d7da; /* Light red background */ + padding: 3px 5px; + border-radius: 5px; +} + +.row-cell-dark { + color: #ececee !important; + background-color: #000128 !important; + padding: 0.5rem; + text-transform: capitalize !important; +} + +.customProgress .ui-progressbar .ui-progressbar-label { + color: yellow !important; +} + +.customProgress .ui-progressbar .ui-progressbar-value { + background-color: #000128 !important; +} + +.row-cell-light { + color: #000128 !important; + background-color: #ececee; +} + +.tig-header { + text-transform: uppercase; +} diff --git a/tig-benchmarker/ui/src/app/pages/home/home.component.ts b/tig-benchmarker/ui/src/app/pages/home/home.component.ts new file mode 100644 index 0000000..dd5bb01 --- /dev/null +++ b/tig-benchmarker/ui/src/app/pages/home/home.component.ts @@ -0,0 +1,126 @@ +import { Component, inject, NgZone, signal } from '@angular/core'; +import { CardModule } from 'primeng/card'; +import { TableModule } from 'primeng/table'; +import { IconFieldModule } from 'primeng/iconfield'; +import { InputIconModule } from 'primeng/inputicon'; +import { InputTextModule } from 'primeng/inputtext'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ProgressSpinnerModule } from 'primeng/progressspinner'; +import { TagModule } from 'primeng/tag'; +import { ButtonModule } from 'primeng/button'; +import { TigApisService } from '../../services/tig-apis.service'; +import { TabViewModule } from 'primeng/tabview'; +import { ChartModule } from 'primeng/chart'; +import { IBenchmark } from '../../interfaces/IBenchmark'; +import { TimeConverterPipe } from '../../pipes/time-converter.pipe'; +import { PanelModule } from 'primeng/panel'; +import { DividerModule } from 'primeng/divider'; +import { MessageService } from 'primeng/api'; +import { ProgressBarModule } from 'primeng/progressbar'; +@Component({ + selector: 'app-home', + standalone: true, + imports: [ + CardModule, + TableModule, + TagModule, + InputTextModule, + ButtonModule, + InputIconModule, + PanelModule, + ProgressSpinnerModule, + DividerModule, + IconFieldModule, + FormsModule, + ReactiveFormsModule, + TabViewModule, + ProgressBarModule, + ChartModule, + TimeConverterPipe, + ], + templateUrl: './home.component.html', + styleUrl: './home.component.scss', +}) +export class HomeComponent { + tigService = inject(TigApisService); + + // Benchmarks Table + benchmarks: any = signal(null); + expandedRows = {}; + + constructor(private messageService: MessageService, private ngZone: NgZone) { + this.init(); + } + + init() { + this.tigService.benchmarks$.subscribe((data: any) => { + this.getBenchmarks(); + }); + } + + getBenchmarks() { + const benchmark_data: IBenchmark[] = this.tigService.benchmarks(); + this.benchmarks.set(benchmark_data); + } + + expandAll() { + this.expandedRows = this.benchmarks().reduce( + (acc: any, p: any) => (acc[p.benchmark_id] = true) && acc, + {} + ); + } + + collapseAll() { + this.expandedRows = {}; + } + + copyToClipboard(value: any) { + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard + .writeText(value) + .then(() => { + console.log('Text copied to clipboard:', value); + }) + .catch((err) => { + console.error('Failed to copy text to clipboard:', err); + }); + } + } + + value: number = 0; + timer: number = 0; + interval: any; + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + this.interval = setInterval(() => { + this.ngZone.run(() => { + this.timer = this.timer + 1; + this.value = Math.round((this.timer / 10) * 100); + if (this.timer >= 10) { + this.tigService.init(); + this.timer = 0; + this.value = 0; + this.messageService.add({ + severity: 'info', + summary: 'Data Refreshed', + detail: 'Process Completed', + }); + } else { + } + }); + }, 1000); + }); + } + + refreshTimer() { + this.tigService.init(); + this.timer = 0; + this.value = 0; + } + + ngOnDestroy() { + if (this.interval) { + clearInterval(this.interval); + } + } +} diff --git a/tig-benchmarker/ui/src/app/pipes/time-converter.pipe.ts b/tig-benchmarker/ui/src/app/pipes/time-converter.pipe.ts new file mode 100644 index 0000000..af8aa91 --- /dev/null +++ b/tig-benchmarker/ui/src/app/pipes/time-converter.pipe.ts @@ -0,0 +1,37 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'timeConverter', + standalone: true, +}) +export class TimeConverterPipe implements PipeTransform { + transform(value: number, ...args: unknown[]): unknown { + if (value >= 0) { + // Convert milliseconds to seconds + let seconds = Math.floor(value / 1000); + + // Calculate days, hours, minutes, and seconds + const days = Math.floor(seconds / (24 * 60 * 60)); + seconds %= 24 * 60 * 60; // Remaining seconds after extracting days + + const hours = Math.floor(seconds / (60 * 60)); + seconds %= 60 * 60; // Remaining seconds after extracting hours + + const minutes = Math.floor(seconds / 60); + seconds %= 60; // Remaining seconds after extracting minutes + + // Build the output string + if (days > 0) { + return `${days}d ${hours}h`; + } else if (hours > 0) { + return `${hours}h ${minutes}m`; + } else if (minutes > 0) { + return `${minutes}m ${seconds}s`; + } else { + return `${seconds}s`; + } + } else { + return 'N/A'; + } + } +} diff --git a/tig-benchmarker/ui/src/app/services/tig-apis.service.ts b/tig-benchmarker/ui/src/app/services/tig-apis.service.ts new file mode 100644 index 0000000..18fdf1f --- /dev/null +++ b/tig-benchmarker/ui/src/app/services/tig-apis.service.ts @@ -0,0 +1,128 @@ +import axios from 'axios'; +import { inject, Injectable, signal } from '@angular/core'; +import { toObservable } from '@angular/core/rxjs-interop'; +import { Router } from '@angular/router'; +import { MessageService } from 'primeng/api'; + +@Injectable({ + providedIn: 'root', +}) +export class TigApisService { + // Services + router = inject(Router); + messageService = inject(MessageService); + + // Signals + ready: any = signal(false); + ready$ = toObservable(this.ready); + config: any = signal(null); + config$ = toObservable(this.config); + benchmarks: any = signal([]); + benchmarks$ = toObservable(this.benchmarks); + constructor() { + this.init(); + } + + async init() { + await this.getConfig(); + await this.getBenchmarks(); + } + + checkReady() { + if (this.config()) { + this.ready.set(true); + } + } + + async getConfig() { + const result = (await axios.get('/get-config')).data; + this.config.set(result); + this.checkReady(); + } + + async saveConfig(data: any) { + try { + const result = (await axios.post('/update-config', data)).data; + this.config.set(result); + this.messageService.add({ + severity: 'success', + summary: 'Success', + detail: 'Config saved successfully', + key: 'global-toast', + life: 5000, + }); + this.getConfig(); + } catch (e) { + this.messageService.add({ + severity: 'error', + summary: 'Error', + detail: 'Error saving config', + key: 'global-toast', + life: 5000, + }); + console.error('saveConfig', e); + } + } + + async getBenchmarks() { + const result = (await axios.get('/get-jobs')).data; + if (result) { + this.benchmarks.set( + result.map((b: any) => { + const benchmark_id_display = + b.benchmark_id.slice(0, 4) + '...' + b.benchmark_id.slice(-4); + let time_elapsed = 0; + if (!b.end_time) { + const seconds = b.start_time; + const utcDate1 = new Date(Date.now()); + const utcDate2 = new Date(utcDate1.toUTCString()); + const now = utcDate2.getTime(); + time_elapsed = now - seconds; + } else { + time_elapsed = b.end_time - b.start_time; + } + + const batches = b.batches.map((batch: any) => { + let time_elapsed = null; + if (batch.start_time && !batch.end_time) { + const seconds = batch.start_time; + const utcDate1 = new Date(Date.now()); + const utcDate2 = new Date(utcDate1.toUTCString()); + const now = utcDate2.getTime(); + time_elapsed = now - seconds; + } else if (batch.end_time && batch.start_time) { + time_elapsed = batch.end_time - batch.start_time; + } + + return { + ...batch, + time_elapsed, + }; + }); + + return { + ...b, + time_elapsed, + benchmark_id_display, + batches, + }; + }) + ); + } + } + + verifyBatch(batch: any) { + console.log('verifyBatch', batch); + const url = `/verify-batch/${batch.benchmark_id}_${batch.batch_number}`; + axios.get(url).then(() => { + this.getBenchmarks(); + }); + } + stopBenchmark(benchmark: any) { + console.log('benchmark', benchmark); + const url = `/stop/${benchmark.benchmark_id}`; + axios.get(url).then(() => { + this.getBenchmarks(); + }); + } +} diff --git a/tig-benchmarker/ui/src/assets/fonts/InputSans-Regular.ttf b/tig-benchmarker/ui/src/assets/fonts/InputSans-Regular.ttf new file mode 100644 index 0000000..8e183a8 Binary files /dev/null and b/tig-benchmarker/ui/src/assets/fonts/InputSans-Regular.ttf differ diff --git a/tig-benchmarker/ui/src/index.html b/tig-benchmarker/ui/src/index.html new file mode 100644 index 0000000..c3a43ba --- /dev/null +++ b/tig-benchmarker/ui/src/index.html @@ -0,0 +1,13 @@ + + + + + TIG Mining UI + + + + + + + + diff --git a/tig-benchmarker/ui/src/main.ts b/tig-benchmarker/ui/src/main.ts new file mode 100644 index 0000000..79060a1 --- /dev/null +++ b/tig-benchmarker/ui/src/main.ts @@ -0,0 +1,25 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { AppComponent } from './app/app.component'; + +bootstrapApplication(AppComponent, appConfig).catch((err) => + console.error(err) +); + +(window as any).MonacoEnvironment = { + getWorkerUrl: function (_moduleId: string, label: string) { + if (label === 'json') { + return '/assets/monaco/vs/language/json/json.worker.js'; + } + if (label === 'css') { + return '/assets/monaco/vs/language/css/css.worker.js'; + } + if (label === 'html') { + return '/assets/monaco/vs/language/html/html.worker.js'; + } + if (label === 'typescript' || label === 'javascript') { + return '/assets/monaco/vs/language/typescript/ts.worker.js'; + } + return '/assets/monaco/vs/editor/editor.worker.js'; + }, +}; diff --git a/tig-benchmarker/ui/src/styles.scss b/tig-benchmarker/ui/src/styles.scss new file mode 100644 index 0000000..e4dd5fc --- /dev/null +++ b/tig-benchmarker/ui/src/styles.scss @@ -0,0 +1,126 @@ +/* You can add global styles to this file, and also import other style files */ +@import "primeng/resources/themes/lara-light-blue/theme.css"; +@import "primeng/resources/primeng.css"; +@import "primeicons/primeicons.css"; + +@font-face { + font-family: "Input-Font"; + src: url("assets/fonts/InputSans-Regular.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +:root { + --primary-font: Input-Font; +} + +body { + font-family: var(--primary-font); + background-color: #ececee; +} + +.banner { + grid-column-gap: 1.5rem; + grid-row-gap: 0.5rem; + border-radius: 0; + flex-flow: column; + justify-content: center; + align-items: center; + height: auto; + padding: 1rem 1.5rem; + display: flex; + position: relative; +} + +.banner-video { + z-index: -10; + object-position: 50% 100%; + border-radius: 10px; + width: 100%; + position: absolute; + inset: 0%; + overflow: hidden; +} +.banner-label { + justify-content: flex-start; + align-items: center; + width: 100%; + padding-left: 0.5rem; + display: flex; + position: relative; + color: #ececee; +} + +.p-datatable .p-datatable-header { + background: #000128 !important; + text-transform: capitalize !important; +} + +.p-datatable .p-datatable-thead > tr > th { + background: #000128 !important; + color: #ececee !important; + text-transform: capitalize !important; +} + +.tig-white { + color: #ececee; +} + +.tig-dark { + color: #000128; +} + +.p-datatable .p-datatable-tbody > tr { + background: #ececee !important; +} + +.p-paginator { + background: #ececee !important; +} + +.text-hover-underline { + text-decoration: none; // Ensure no underline by default + position: relative; + + &::after { + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; // Adjust thickness as needed + background-color: currentColor; // Use text color + transition: width 0.3s ease; // Smooth transition + } + + &:hover::after { + width: 100%; // Full underline on hover + } +} + +.p-datatable .p-datatable-tbody > tr > td { + border: 1px solid #b6b6b8 !important; + border-radius: 2px; +} + +.p-datatable .p-datatable-thead > tr > th { + border: 1px solid #b6b6b8 !important; + border-radius: 2px; +} + +.p-datatable .p-datatable-tbody > tr:nth-child(even):hover { + background-color: #c8c8cf !important; +} + +.p-datatable .p-datatable-tbody > tr:nth-child(odd):hover { + background-color: #c8c8cf !important; +} + +.p-datatable-wrapper { + border: 1px solid #b6b6b8 !important; + border-radius: 20px 20px 0px 0px; +} + +.p-tag-value{ + text-align: center !important; +} \ No newline at end of file diff --git a/tig-benchmarker/ui/tsconfig.app.json b/tig-benchmarker/ui/tsconfig.app.json new file mode 100644 index 0000000..3775b37 --- /dev/null +++ b/tig-benchmarker/ui/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": [ + "src/main.ts" + ], + "include": [ + "src/**/*.d.ts" + ] +} diff --git a/tig-benchmarker/ui/tsconfig.json b/tig-benchmarker/ui/tsconfig.json new file mode 100644 index 0000000..a8bb65b --- /dev/null +++ b/tig-benchmarker/ui/tsconfig.json @@ -0,0 +1,33 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "lib": [ + "ES2022", + "dom" + ] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/tig-benchmarker/ui/tsconfig.spec.json b/tig-benchmarker/ui/tsconfig.spec.json new file mode 100644 index 0000000..5fb748d --- /dev/null +++ b/tig-benchmarker/ui/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +} diff --git a/tig-benchmarker/update_config.py b/tig-benchmarker/update_config.py new file mode 100644 index 0000000..cd3cc6b --- /dev/null +++ b/tig-benchmarker/update_config.py @@ -0,0 +1,110 @@ +from common.structs import * +import requests +import json +import random + +print("THIS IS AN EXAMPLE SCRIPT TO UPDATE YOUR MASTER CONFIG") +MASTER_IP = input("Enter Master IP: ") +MASTER_PORT = input("Enter Master Port: ") + + +# Get latest data +print("Fetching latest data from Master") +data = requests.get(f"http://{MASTER_IP}:{MASTER_PORT}/get-latest-data").json() +block = Block.from_dict(data["block"]) +algorithms = {k: Algorithm.from_dict(v) for k, v in data["algorithms"].items()} +wasms = {k: Binary.from_dict(v) for k, v in data["wasms"].items()} +precommits = {k: Precommit.from_dict(v) for k, v in data["precommits"].items()} +benchmarks = {k: Benchmark.from_dict(v) for k, v in data["benchmarks"].items()} +proofs = {k: Proof.from_dict(v) for k, v in data["proofs"].items()} +frauds = {k: Fraud.from_dict(v) for k, v in data["frauds"].items()} +challenges = {k: Challenge.from_dict(v) for k, v in data["challenges"].items()} +difficulty_data = {k: [DifficultyData.from_dict(x) for x in v] for k, v in data["difficulty_data"].items()} + +challenge_id_2_name = {c.id: c.details.name for c in challenges.values()} + + +# Calculate solutions, qualifiers and cutoff +solutions = {c_name: 0 for c_name in challenge_id_2_name.values()} +potential_qualifiers = {c_name: 0 for c_name in challenge_id_2_name.values()} + +for benchmark_id in benchmarks: + if benchmark_id in frauds: + continue + challenge_id = precommits[benchmark_id].settings.challenge_id + c_name = challenge_id_2_name[challenge_id] + difficulty = precommits[benchmark_id].settings.difficulty + num_solutions = benchmarks[benchmark_id].details.num_solutions + solutions[c_name] += num_solutions + if difficulty in challenges[challenge_id].block_data.qualifier_difficulties: + potential_qualifiers[c_name] += num_solutions + +cutoff = int(min(solutions.values()) * block.config["opow"]["cutoff_multiplier"]) + +print("Stats (including non yet active benchmarks)") +print("#solutions: ", solutions) +print("#potential qualifiers: ", potential_qualifiers) +print("cutoff: ", cutoff) + + +# Calculate challenge weights +if cutoff == 0: + weights = {c_name: 1.0 for c_name in challenge_id_2_name.values()} +elif cutoff < max(potential_qualifiers.values()): + print("Adjusting weights to increase cutoff") + m = max(potential_qualifiers.values()) + weights = { + k: (m - v) / m + 0.1 + for k, v in potential_qualifiers.items() + } +else: + print("Adjusting weights to improve imbalance") + m = max(solutions.values()) + weights = { + k: (m - v) / m + 0.1 + for k, v in solutions.items() + } +total_weight = sum(weights.values()) +weights = {k: v / total_weight for k, v in weights.items()} +print("Weights: ", weights) + + +# Selecting difficulties +selected_difficulties = {} +for c_id, stats in difficulty_data.items(): + solution_p = [ + ( + s.num_solutions / s.num_nonces, + s.difficulty + ) + for s in stats + ] + solution_p.sort(reverse=True) + print(f"({challenges[c_id].details.name}) Top 10 difficulties by %solution: ", solution_p[:10]) + selection = set() + # select 25 randomly offset difficulties around the top 10 + for _, difficulty in solution_p[:10]: + selection.add(difficulty) + for _ in range(25): + selection.add(( + difficulty[0] + random.randint(-10, 10), + difficulty[1] + random.randint(-10, 10) + )) + c_name = challenge_id_2_name[c_id] + selected_difficulties[c_name] = list(selection) + + +# Update Master Config +config = requests.get(f"http://{MASTER_IP}:{MASTER_PORT}/get-config").json() +for c_name, c in config["precommit_manager_config"]["algo_selection"].items(): + c["weight"] = weights[c_name] + +for c_name, c in config["difficulty_sampler_config"]["selected_difficulties"].items(): + c.clear() + c.extend(selected_difficulties[c_name]) + +print("Updating your Master Config") +requests.post(f"http://{MASTER_IP}:{MASTER_PORT}/update-config", data=json.dumps(config), headers={"Content-Type": "application/json"}) + + +print("Done") \ No newline at end of file diff --git a/tig-breakthroughs/README.md b/tig-breakthroughs/README.md new file mode 100644 index 0000000..9c0afc4 --- /dev/null +++ b/tig-breakthroughs/README.md @@ -0,0 +1,34 @@ +# tig-breakthroughs + +A folder that hosts submissions of algorithmic methods made by Innovators in TIG. + +Each submissions is committed to their own branch with the naming pattern: + +`\method\` + +## Making a Submission + +1. Read important docs for what is a Breakthrough and how it is rewarded: + * [Implementations vs Breakthroughs](../docs/guides/breakthroughs.md) + + * [Voting Guidelines for Token Holders](../docs/guides/voting.md) + +2. Copy `template.md` from the relevant challenges folder (e.g. [`knapsack/tempate.md`](./knapsack/template.md)), and fill in the details with evidence of your breakthrough + +3. Copy [invention_assignment.doc](../docs/agreements/invention_assignment.doc), fill in the details, and sign + +4. Email your invention assignment to contact@tig.foundation with subject "Invention Assignment" + +5. Submit your evidence via https://play.tig.foundation/innovator + * 250 TIG will be deducted from your Available Fee Balance to make a submission + * You can topup via the [Benchmarker page](https://play.tig.foundation/benchmarker) + +## Method Submission Flow + +1. New submissions get their branch pushed to a private version of this repository +2. A new submission made during round `X` will have its branch pushed to the public version of this repository at the start of round `X + 2` +3. From the start of round `X + 2` till the start of round `X + 4`, token holders can vote on whether they consider the method to be a breakthrough based off the submitted evidence +4. At the start of round `X + 4`, if the submission has at least 50% yes votes, it becomes active +5. Every block, a method's adoption is the sum of all algorithm adoption, where the algorithm is attributed to that method. Methods with at least 50% adoption earn rewards and a merge point +6. At the end of a round, a method from each challenge with the most merge points, meeting the minimum threshold of 5040, gets merged to the `main` branch + * Merged methods are considered breakthroughs, and receive rewards every block where their adoption is greater than 0% \ No newline at end of file diff --git a/tig-breakthroughs/knapsack/template.md b/tig-breakthroughs/knapsack/template.md new file mode 100644 index 0000000..43c8fa0 --- /dev/null +++ b/tig-breakthroughs/knapsack/template.md @@ -0,0 +1,133 @@ +# EVIDENCE + +THIS IS A TEMPLATE FOR EVIDENCE TO BE SUBMITTED IN SUPPORT OF A REQUEST FOR ELIGIBILITY FOR BREAKTHROUGH REWARDS + +## OVERVIEW + +- TIG TOKEN HOLDERS WILL VOTE ON WHETHER YOUR ALGORITHMIC METHOD IS ELIGIBLE FOR BREAKTHROUGH REWARDS + +- TIG TOKEN HOLDERS ARE FREE TO VOTE AS THEY LIKE BUT HAVE BEEN ADVISED THAT IF THEY WANT TO MAXIMISE THE VALUE OF THE TOKENS THAT THEY HOLD, THEN THEY SHOULD BE SATISFYING THEMSELVES THAT ALGORITHIC METHODS THAT THEY VOTE AS ELIGIBLE WILL BE BOTH NOVEL AND INVENTIVE + +- THE REASON WHY NOVELTY AND INVENTIVENESS ARE IMPORTANT ATTRIBUTES IS BECAUSE THEY ARE PREREQUISITES OF PATENTATBILITY. + +- **THE PURPOSE OF THIS DOCUMENT IS TO:** + - CAPTURE A DESCRIPTION OF THE ALGORITHMIC METHOD THAT YOU WANT TO BE CONSIDERED FOR ELIGIBILTY. + + - TO IDENTIFY THE CREATOR OF THE ALGORITHMIC METHOD. + + - TO PROMPT YOU TO PROVIDE THE BEST EVIDNCE TO SUPPORT THE CASE THAT THE ALGORITHMIC METHOD IS NOVEL AND INVENTIVE. + + - TO PROMPT YOU TO PROVIDE SUGGESTIONS FOR REAL WORLD APPLICATIONS OF YOUR ALGORITHMIC METHOD WHERE YIOU CAN. + +WHEN PROVIDING EVIDENCE, YOU MAY CITE LINKS TO EXTERNAL DATA SOURCES. + +## UNIQUE ALGORITHM IDENTIFIER (UAI) + +> UAI PLACEHOLDER - DO NOT EDIT + +## DESCRIPTION OF ALGORITHMIC METHOD + +PLEASE IDENTIFY WHICH TIG CHALLENGE THE ALGORITHMIC METHOD ADDRESSES. + +> knapsack - DO NOT EDIT + +PLEASE DESCRIBE THE ALGORITHMIC METHOD AND THE PROBLEM THAT IT SOLVES. + +> YOUR RESPONSE HERE + +## ATTRIBUTION + +PLEASE PROVIDE THE IDENTITY OF THE CREATOR OF THE ALGORITHMIC METHOD (this should be a natural person or legal entity. If an artificial intelligence has been used to assist in the creation of the algorithmic method then the creator is the operator of the artificial intelligence) + +> YOUR RESPONSE HERE + +PLEASE PROVIDE EVIDENCE OF AUTHORSHIP WHERE IT IS AVAILABLE. + +> YOUR RESPONSE HERE + +## NOVELTY AND INVENTIVENESS + +To support your claim that an algorithmic method is novel and inventive, you should provide evidence that demonstrates both its uniqueness (novelty) and its non-obviousness (inventiveness) in relation to the existing state of the art. + +### Establish the State of the Art + +- **Prior Art Search:** Conduct a comprehensive review of existing methods, algorithms, patents, academic papers, and industry practices to identify prior art in the domain. + - Highlight documents and technologies most closely related to your method. + + > YOUR RESPONSE HERE + + - Show where these existing methods fall short or lack the features your algorithmic method provides. + + > YOUR RESPONSE HERE + +- **Technical Context:** Describe the common approaches and challenges in the field prior to your innovation. + + > YOUR RESPONSE HERE + +### Evidence of Novelty + +- **Unique Features:** List the features, mechanisms, or aspects of your algorithmic method that are absent in prior art. + + > YOUR RESPONSE HERE + +- **New Problem Solved:** Describe how your algorithmic method provides a novel solution to an existing problem. + + > YOUR RESPONSE HERE + +- **Comparative Analysis:** Use a side-by-side comparison table to highlight the differences between your method and similar existing methods, clearly showing what’s new. + + > YOUR RESPONSE HERE + +### Evidence Inventiveness of Inventiveness + +- **Non-Obviousness:** Argue why a skilled person in the field would not have arrived at your method by simply combining existing ideas or extending known techniques. + - Demonstrate that the development involved an inventive step beyond straightforward application of existing principles. + - Unexpected Results: Highlight results or benefits that could not have been predicted based on prior art. + + > YOUR RESPONSE HERE + +- **Advantages:** Provide evidence of how your algorithm outperforms or offers significant advantages over existing methods, such as: + - Increased efficiency. + - Greater accuracy. + - Reduced computational complexity. + - Novel applications. + + > YOUR RESPONSE HERE + +### Supporting Data + +- **Experimental Results:** Include performance benchmarks, simulations, or empirical data that substantiate your claims of novelty and inventiveness. + + > YOUR RESPONSE HERE + +- **Proof of Concept:** If possible, show a working prototype or implementation that validates the method. + + > YOUR RESPONSE HERE + +### Citations and Expert Opinions + +- **Literature Gaps:** Affirm, to the best of your knowledge, the absence of similar solutions in published literature to reinforce your novelty claim. + + > YOUR RESPONSE HERE + +- **Endorsements:** Include reviews or opinions from industry experts, researchers, or peer-reviewed publications that evidences the novelty and impact of your algorithm. + + > YOUR RESPONSE HERE + +### EVIDENCE TO SUPPORT PATENTABILITY + +- **Development Records:** Please provide documentation of the invention process, including notes, sketches, and software versions, to establish a timeline of your innovation. + + > YOUR RESPONSE HERE + +### SUGGESTED APPLICATIONS + +- Please provide suggestions for any real world applications of your abstract algorithmic method that occur to you. + + > YOUR RESPONSE HERE + +### ANY OTHER INFORMATION + +- Please provide any other evidence or argument that you think might help support you request for eligibility for Breakthrough Rewards. + + > YOUR RESPONSE HERE \ No newline at end of file diff --git a/tig-breakthroughs/satisfiability/template.md b/tig-breakthroughs/satisfiability/template.md new file mode 100644 index 0000000..a6c86a0 --- /dev/null +++ b/tig-breakthroughs/satisfiability/template.md @@ -0,0 +1,133 @@ +# EVIDENCE + +THIS IS A TEMPLATE FOR EVIDENCE TO BE SUBMITTED IN SUPPORT OF A REQUEST FOR ELIGIBILITY FOR BREAKTHROUGH REWARDS + +## OVERVIEW + +- TIG TOKEN HOLDERS WILL VOTE ON WHETHER YOUR ALGORITHMIC METHOD IS ELIGIBLE FOR BREAKTHROUGH REWARDS + +- TIG TOKEN HOLDERS ARE FREE TO VOTE AS THEY LIKE BUT HAVE BEEN ADVISED THAT IF THEY WANT TO MAXIMISE THE VALUE OF THE TOKENS THAT THEY HOLD, THEN THEY SHOULD BE SATISFYING THEMSELVES THAT ALGORITHIC METHODS THAT THEY VOTE AS ELIGIBLE WILL BE BOTH NOVEL AND INVENTIVE + +- THE REASON WHY NOVELTY AND INVENTIVENESS ARE IMPORTANT ATTRIBUTES IS BECAUSE THEY ARE PREREQUISITES OF PATENTATBILITY. + +- **THE PURPOSE OF THIS DOCUMENT IS TO:** + - CAPTURE A DESCRIPTION OF THE ALGORITHMIC METHOD THAT YOU WANT TO BE CONSIDERED FOR ELIGIBILTY. + + - TO IDENTIFY THE CREATOR OF THE ALGORITHMIC METHOD. + + - TO PROMPT YOU TO PROVIDE THE BEST EVIDNCE TO SUPPORT THE CASE THAT THE ALGORITHMIC METHOD IS NOVEL AND INVENTIVE. + + - TO PROMPT YOU TO PROVIDE SUGGESTIONS FOR REAL WORLD APPLICATIONS OF YOUR ALGORITHMIC METHOD WHERE YIOU CAN. + +WHEN PROVIDING EVIDENCE, YOU MAY CITE LINKS TO EXTERNAL DATA SOURCES. + +## UNIQUE ALGORITHM IDENTIFIER (UAI) + +> UAI PLACEHOLDER - DO NOT EDIT + +## DESCRIPTION OF ALGORITHMIC METHOD + +PLEASE IDENTIFY WHICH TIG CHALLENGE THE ALGORITHMIC METHOD ADDRESSES. + +> satisfiability - DO NOT EDIT + +PLEASE DESCRIBE THE ALGORITHMIC METHOD AND THE PROBLEM THAT IT SOLVES. + +> YOUR RESPONSE HERE + +## ATTRIBUTION + +PLEASE PROVIDE THE IDENTITY OF THE CREATOR OF THE ALGORITHMIC METHOD (this should be a natural person or legal entity. If an artificial intelligence has been used to assist in the creation of the algorithmic method then the creator is the operator of the artificial intelligence) + +> YOUR RESPONSE HERE + +PLEASE PROVIDE EVIDENCE OF AUTHORSHIP WHERE IT IS AVAILABLE. + +> YOUR RESPONSE HERE + +## NOVELTY AND INVENTIVENESS + +To support your claim that an algorithmic method is novel and inventive, you should provide evidence that demonstrates both its uniqueness (novelty) and its non-obviousness (inventiveness) in relation to the existing state of the art. + +### Establish the State of the Art + +- **Prior Art Search:** Conduct a comprehensive review of existing methods, algorithms, patents, academic papers, and industry practices to identify prior art in the domain. + - Highlight documents and technologies most closely related to your method. + + > YOUR RESPONSE HERE + + - Show where these existing methods fall short or lack the features your algorithmic method provides. + + > YOUR RESPONSE HERE + +- **Technical Context:** Describe the common approaches and challenges in the field prior to your innovation. + + > YOUR RESPONSE HERE + +### Evidence of Novelty + +- **Unique Features:** List the features, mechanisms, or aspects of your algorithmic method that are absent in prior art. + + > YOUR RESPONSE HERE + +- **New Problem Solved:** Describe how your algorithmic method provides a novel solution to an existing problem. + + > YOUR RESPONSE HERE + +- **Comparative Analysis:** Use a side-by-side comparison table to highlight the differences between your method and similar existing methods, clearly showing what’s new. + + > YOUR RESPONSE HERE + +### Evidence Inventiveness of Inventiveness + +- **Non-Obviousness:** Argue why a skilled person in the field would not have arrived at your method by simply combining existing ideas or extending known techniques. + - Demonstrate that the development involved an inventive step beyond straightforward application of existing principles. + - Unexpected Results: Highlight results or benefits that could not have been predicted based on prior art. + + > YOUR RESPONSE HERE + +- **Advantages:** Provide evidence of how your algorithm outperforms or offers significant advantages over existing methods, such as: + - Increased efficiency. + - Greater accuracy. + - Reduced computational complexity. + - Novel applications. + + > YOUR RESPONSE HERE + +### Supporting Data + +- **Experimental Results:** Include performance benchmarks, simulations, or empirical data that substantiate your claims of novelty and inventiveness. + + > YOUR RESPONSE HERE + +- **Proof of Concept:** If possible, show a working prototype or implementation that validates the method. + + > YOUR RESPONSE HERE + +### Citations and Expert Opinions + +- **Literature Gaps:** Affirm, to the best of your knowledge, the absence of similar solutions in published literature to reinforce your novelty claim. + + > YOUR RESPONSE HERE + +- **Endorsements:** Include reviews or opinions from industry experts, researchers, or peer-reviewed publications that evidences the novelty and impact of your algorithm. + + > YOUR RESPONSE HERE + +### EVIDENCE TO SUPPORT PATENTABILITY + +- **Development Records:** Please provide documentation of the invention process, including notes, sketches, and software versions, to establish a timeline of your innovation. + + > YOUR RESPONSE HERE + +### SUGGESTED APPLICATIONS + +- Please provide suggestions for any real world applications of your abstract algorithmic method that occur to you. + + > YOUR RESPONSE HERE + +### ANY OTHER INFORMATION + +- Please provide any other evidence or argument that you think might help support you request for eligibility for Breakthrough Rewards. + + > YOUR RESPONSE HERE \ No newline at end of file diff --git a/tig-breakthroughs/vector_search/template.md b/tig-breakthroughs/vector_search/template.md new file mode 100644 index 0000000..846f3b8 --- /dev/null +++ b/tig-breakthroughs/vector_search/template.md @@ -0,0 +1,133 @@ +# EVIDENCE + +THIS IS A TEMPLATE FOR EVIDENCE TO BE SUBMITTED IN SUPPORT OF A REQUEST FOR ELIGIBILITY FOR BREAKTHROUGH REWARDS + +## OVERVIEW + +- TIG TOKEN HOLDERS WILL VOTE ON WHETHER YOUR ALGORITHMIC METHOD IS ELIGIBLE FOR BREAKTHROUGH REWARDS + +- TIG TOKEN HOLDERS ARE FREE TO VOTE AS THEY LIKE BUT HAVE BEEN ADVISED THAT IF THEY WANT TO MAXIMISE THE VALUE OF THE TOKENS THAT THEY HOLD, THEN THEY SHOULD BE SATISFYING THEMSELVES THAT ALGORITHIC METHODS THAT THEY VOTE AS ELIGIBLE WILL BE BOTH NOVEL AND INVENTIVE + +- THE REASON WHY NOVELTY AND INVENTIVENESS ARE IMPORTANT ATTRIBUTES IS BECAUSE THEY ARE PREREQUISITES OF PATENTATBILITY. + +- **THE PURPOSE OF THIS DOCUMENT IS TO:** + - CAPTURE A DESCRIPTION OF THE ALGORITHMIC METHOD THAT YOU WANT TO BE CONSIDERED FOR ELIGIBILTY. + + - TO IDENTIFY THE CREATOR OF THE ALGORITHMIC METHOD. + + - TO PROMPT YOU TO PROVIDE THE BEST EVIDNCE TO SUPPORT THE CASE THAT THE ALGORITHMIC METHOD IS NOVEL AND INVENTIVE. + + - TO PROMPT YOU TO PROVIDE SUGGESTIONS FOR REAL WORLD APPLICATIONS OF YOUR ALGORITHMIC METHOD WHERE YIOU CAN. + +WHEN PROVIDING EVIDENCE, YOU MAY CITE LINKS TO EXTERNAL DATA SOURCES. + +## UNIQUE ALGORITHM IDENTIFIER (UAI) + +> UAI PLACEHOLDER - DO NOT EDIT + +## DESCRIPTION OF ALGORITHMIC METHOD + +PLEASE IDENTIFY WHICH TIG CHALLENGE THE ALGORITHMIC METHOD ADDRESSES. + +> vector_search - DO NOT EDIT + +PLEASE DESCRIBE THE ALGORITHMIC METHOD AND THE PROBLEM THAT IT SOLVES. + +> YOUR RESPONSE HERE + +## ATTRIBUTION + +PLEASE PROVIDE THE IDENTITY OF THE CREATOR OF THE ALGORITHMIC METHOD (this should be a natural person or legal entity. If an artificial intelligence has been used to assist in the creation of the algorithmic method then the creator is the operator of the artificial intelligence) + +> YOUR RESPONSE HERE + +PLEASE PROVIDE EVIDENCE OF AUTHORSHIP WHERE IT IS AVAILABLE. + +> YOUR RESPONSE HERE + +## NOVELTY AND INVENTIVENESS + +To support your claim that an algorithmic method is novel and inventive, you should provide evidence that demonstrates both its uniqueness (novelty) and its non-obviousness (inventiveness) in relation to the existing state of the art. + +### Establish the State of the Art + +- **Prior Art Search:** Conduct a comprehensive review of existing methods, algorithms, patents, academic papers, and industry practices to identify prior art in the domain. + - Highlight documents and technologies most closely related to your method. + + > YOUR RESPONSE HERE + + - Show where these existing methods fall short or lack the features your algorithmic method provides. + + > YOUR RESPONSE HERE + +- **Technical Context:** Describe the common approaches and challenges in the field prior to your innovation. + + > YOUR RESPONSE HERE + +### Evidence of Novelty + +- **Unique Features:** List the features, mechanisms, or aspects of your algorithmic method that are absent in prior art. + + > YOUR RESPONSE HERE + +- **New Problem Solved:** Describe how your algorithmic method provides a novel solution to an existing problem. + + > YOUR RESPONSE HERE + +- **Comparative Analysis:** Use a side-by-side comparison table to highlight the differences between your method and similar existing methods, clearly showing what’s new. + + > YOUR RESPONSE HERE + +### Evidence Inventiveness of Inventiveness + +- **Non-Obviousness:** Argue why a skilled person in the field would not have arrived at your method by simply combining existing ideas or extending known techniques. + - Demonstrate that the development involved an inventive step beyond straightforward application of existing principles. + - Unexpected Results: Highlight results or benefits that could not have been predicted based on prior art. + + > YOUR RESPONSE HERE + +- **Advantages:** Provide evidence of how your algorithm outperforms or offers significant advantages over existing methods, such as: + - Increased efficiency. + - Greater accuracy. + - Reduced computational complexity. + - Novel applications. + + > YOUR RESPONSE HERE + +### Supporting Data + +- **Experimental Results:** Include performance benchmarks, simulations, or empirical data that substantiate your claims of novelty and inventiveness. + + > YOUR RESPONSE HERE + +- **Proof of Concept:** If possible, show a working prototype or implementation that validates the method. + + > YOUR RESPONSE HERE + +### Citations and Expert Opinions + +- **Literature Gaps:** Affirm, to the best of your knowledge, the absence of similar solutions in published literature to reinforce your novelty claim. + + > YOUR RESPONSE HERE + +- **Endorsements:** Include reviews or opinions from industry experts, researchers, or peer-reviewed publications that evidences the novelty and impact of your algorithm. + + > YOUR RESPONSE HERE + +### EVIDENCE TO SUPPORT PATENTABILITY + +- **Development Records:** Please provide documentation of the invention process, including notes, sketches, and software versions, to establish a timeline of your innovation. + + > YOUR RESPONSE HERE + +### SUGGESTED APPLICATIONS + +- Please provide suggestions for any real world applications of your abstract algorithmic method that occur to you. + + > YOUR RESPONSE HERE + +### ANY OTHER INFORMATION + +- Please provide any other evidence or argument that you think might help support you request for eligibility for Breakthrough Rewards. + + > YOUR RESPONSE HERE \ No newline at end of file diff --git a/tig-breakthroughs/vehicle_routing/template.md b/tig-breakthroughs/vehicle_routing/template.md new file mode 100644 index 0000000..8837425 --- /dev/null +++ b/tig-breakthroughs/vehicle_routing/template.md @@ -0,0 +1,133 @@ +# EVIDENCE + +THIS IS A TEMPLATE FOR EVIDENCE TO BE SUBMITTED IN SUPPORT OF A REQUEST FOR ELIGIBILITY FOR BREAKTHROUGH REWARDS + +## OVERVIEW + +- TIG TOKEN HOLDERS WILL VOTE ON WHETHER YOUR ALGORITHMIC METHOD IS ELIGIBLE FOR BREAKTHROUGH REWARDS + +- TIG TOKEN HOLDERS ARE FREE TO VOTE AS THEY LIKE BUT HAVE BEEN ADVISED THAT IF THEY WANT TO MAXIMISE THE VALUE OF THE TOKENS THAT THEY HOLD, THEN THEY SHOULD BE SATISFYING THEMSELVES THAT ALGORITHIC METHODS THAT THEY VOTE AS ELIGIBLE WILL BE BOTH NOVEL AND INVENTIVE + +- THE REASON WHY NOVELTY AND INVENTIVENESS ARE IMPORTANT ATTRIBUTES IS BECAUSE THEY ARE PREREQUISITES OF PATENTATBILITY. + +- **THE PURPOSE OF THIS DOCUMENT IS TO:** + - CAPTURE A DESCRIPTION OF THE ALGORITHMIC METHOD THAT YOU WANT TO BE CONSIDERED FOR ELIGIBILTY. + + - TO IDENTIFY THE CREATOR OF THE ALGORITHMIC METHOD. + + - TO PROMPT YOU TO PROVIDE THE BEST EVIDNCE TO SUPPORT THE CASE THAT THE ALGORITHMIC METHOD IS NOVEL AND INVENTIVE. + + - TO PROMPT YOU TO PROVIDE SUGGESTIONS FOR REAL WORLD APPLICATIONS OF YOUR ALGORITHMIC METHOD WHERE YIOU CAN. + +WHEN PROVIDING EVIDENCE, YOU MAY CITE LINKS TO EXTERNAL DATA SOURCES. + +## UNIQUE ALGORITHM IDENTIFIER (UAI) + +> UAI PLACEHOLDER - DO NOT EDIT + +## DESCRIPTION OF ALGORITHMIC METHOD + +PLEASE IDENTIFY WHICH TIG CHALLENGE THE ALGORITHMIC METHOD ADDRESSES. + +> vehicle_routing - DO NOT EDIT + +PLEASE DESCRIBE THE ALGORITHMIC METHOD AND THE PROBLEM THAT IT SOLVES. + +> YOUR RESPONSE HERE + +## ATTRIBUTION + +PLEASE PROVIDE THE IDENTITY OF THE CREATOR OF THE ALGORITHMIC METHOD (this should be a natural person or legal entity. If an artificial intelligence has been used to assist in the creation of the algorithmic method then the creator is the operator of the artificial intelligence) + +> YOUR RESPONSE HERE + +PLEASE PROVIDE EVIDENCE OF AUTHORSHIP WHERE IT IS AVAILABLE. + +> YOUR RESPONSE HERE + +## NOVELTY AND INVENTIVENESS + +To support your claim that an algorithmic method is novel and inventive, you should provide evidence that demonstrates both its uniqueness (novelty) and its non-obviousness (inventiveness) in relation to the existing state of the art. + +### Establish the State of the Art + +- **Prior Art Search:** Conduct a comprehensive review of existing methods, algorithms, patents, academic papers, and industry practices to identify prior art in the domain. + - Highlight documents and technologies most closely related to your method. + + > YOUR RESPONSE HERE + + - Show where these existing methods fall short or lack the features your algorithmic method provides. + + > YOUR RESPONSE HERE + +- **Technical Context:** Describe the common approaches and challenges in the field prior to your innovation. + + > YOUR RESPONSE HERE + +### Evidence of Novelty + +- **Unique Features:** List the features, mechanisms, or aspects of your algorithmic method that are absent in prior art. + + > YOUR RESPONSE HERE + +- **New Problem Solved:** Describe how your algorithmic method provides a novel solution to an existing problem. + + > YOUR RESPONSE HERE + +- **Comparative Analysis:** Use a side-by-side comparison table to highlight the differences between your method and similar existing methods, clearly showing what’s new. + + > YOUR RESPONSE HERE + +### Evidence Inventiveness of Inventiveness + +- **Non-Obviousness:** Argue why a skilled person in the field would not have arrived at your method by simply combining existing ideas or extending known techniques. + - Demonstrate that the development involved an inventive step beyond straightforward application of existing principles. + - Unexpected Results: Highlight results or benefits that could not have been predicted based on prior art. + + > YOUR RESPONSE HERE + +- **Advantages:** Provide evidence of how your algorithm outperforms or offers significant advantages over existing methods, such as: + - Increased efficiency. + - Greater accuracy. + - Reduced computational complexity. + - Novel applications. + + > YOUR RESPONSE HERE + +### Supporting Data + +- **Experimental Results:** Include performance benchmarks, simulations, or empirical data that substantiate your claims of novelty and inventiveness. + + > YOUR RESPONSE HERE + +- **Proof of Concept:** If possible, show a working prototype or implementation that validates the method. + + > YOUR RESPONSE HERE + +### Citations and Expert Opinions + +- **Literature Gaps:** Affirm, to the best of your knowledge, the absence of similar solutions in published literature to reinforce your novelty claim. + + > YOUR RESPONSE HERE + +- **Endorsements:** Include reviews or opinions from industry experts, researchers, or peer-reviewed publications that evidences the novelty and impact of your algorithm. + + > YOUR RESPONSE HERE + +### EVIDENCE TO SUPPORT PATENTABILITY + +- **Development Records:** Please provide documentation of the invention process, including notes, sketches, and software versions, to establish a timeline of your innovation. + + > YOUR RESPONSE HERE + +### SUGGESTED APPLICATIONS + +- Please provide suggestions for any real world applications of your abstract algorithmic method that occur to you. + + > YOUR RESPONSE HERE + +### ANY OTHER INFORMATION + +- Please provide any other evidence or argument that you think might help support you request for eligibility for Breakthrough Rewards. + + > YOUR RESPONSE HERE \ No newline at end of file diff --git a/tig-challenges/src/knapsack.rs b/tig-challenges/src/knapsack.rs index e65e238..c6e8245 100644 --- a/tig-challenges/src/knapsack.rs +++ b/tig-challenges/src/knapsack.rs @@ -112,9 +112,8 @@ impl crate::ChallengeTrait for Challenge { .collect(); // Sort the list of tuples by value-to-weight ratio in descending order - value_weight_ratios.sort_unstable_by(|&(_, ratio_a, _), &(_, ratio_b, _)| { - ratio_b.partial_cmp(&ratio_a).unwrap() - }); + value_weight_ratios + .sort_by(|&(_, ratio_a, _), &(_, ratio_b, _)| ratio_b.partial_cmp(&ratio_a).unwrap()); let mut total_weight = 0; let mut selected_indices = Vec::new(); @@ -124,7 +123,7 @@ impl crate::ChallengeTrait for Challenge { total_weight += weight; } } - selected_indices.sort_unstable(); + selected_indices.sort(); let mut min_value = calculate_total_value(&selected_indices, &values, &interaction_values); min_value = (min_value as f32 * (1.0 + difficulty.better_than_baseline as f32 / 1000.0)) @@ -187,7 +186,7 @@ pub fn calculate_total_value( interaction_values: &Vec>, ) -> u32 { let mut indices = indices.clone(); - indices.sort_unstable(); + indices.sort(); let mut total_value = 0i32; diff --git a/tig-protocol/src/context.rs b/tig-protocol/src/context.rs index 5336f24..6e89fb4 100644 --- a/tig-protocol/src/context.rs +++ b/tig-protocol/src/context.rs @@ -25,6 +25,12 @@ pub trait Context { ) -> Result<()>; async fn get_latest_block_id(&self) -> String; async fn get_block_details(&self, block_id: &String) -> Option; + async fn get_breakthrough_state(&self, breakthrough_id: &String) -> Option; + async fn add_breakthrough_to_mempool( + &self, + details: BreakthroughDetails, + evidence: String, + ) -> Result; async fn get_challenge_state(&self, challenge_id: &String) -> Option; async fn get_challenge_block_data( &self, @@ -34,14 +40,25 @@ pub trait Context { async fn get_config(&self) -> ProtocolConfig; async fn add_deposit_to_mempool(&self, details: DepositDetails) -> Result; async fn add_fraud_to_mempool(&self, benchmark_id: String, allegation: String) -> Result<()>; + async fn get_player_details(&self, player_id: &String) -> Option; async fn get_player_state(&self, player_id: &String) -> Option; async fn get_player_block_data( &self, player_id: &String, block_id: &String, ) -> Option; - async fn set_player_delegatee(&self, player_id: String, delegatee: String) -> Result<()>; + async fn set_player_delegatees( + &self, + player_id: String, + delegatees: HashMap, + ) -> Result<()>; async fn set_player_reward_share(&self, player_id: String, reward_share: f64) -> Result<()>; + async fn set_player_vote( + &self, + player_id: String, + breakthrough_id: String, + yes: bool, + ) -> Result<()>; async fn get_precommit_settings(&self, benchmark_id: &String) -> Option; async fn get_precommit_details(&self, benchmark_id: &String) -> Option; async fn add_precommit_to_mempool( @@ -74,5 +91,9 @@ pub struct AddBlockCache { pub active_algorithms_state: HashMap, pub active_algorithms_details: HashMap, pub active_algorithms_block_data: HashMap, + pub voting_breakthroughs_state: HashMap, + pub active_breakthroughs_state: HashMap, + pub active_breakthroughs_details: HashMap, + pub active_breakthroughs_block_data: HashMap, pub active_solutions: HashMap, } diff --git a/tig-protocol/src/contracts/algorithms.rs b/tig-protocol/src/contracts/algorithms.rs index 6e4de37..1cd7787 100644 --- a/tig-protocol/src/contracts/algorithms.rs +++ b/tig-protocol/src/contracts/algorithms.rs @@ -11,6 +11,7 @@ pub async fn submit_algorithm( player_id: String, algorithm_name: String, challenge_id: String, + breakthrough_id: Option, code: String, ) -> Result { let config = ctx.get_config().await; @@ -24,6 +25,12 @@ pub async fn submit_algorithm( return Err(anyhow!("Invalid challenge '{}'", challenge_id)); } + if let Some(breakthrough_id) = &breakthrough_id { + if ctx.get_breakthrough_state(breakthrough_id).await.is_none() { + return Err(anyhow!("Invalid breakthrough '{}'", breakthrough_id)); + } + } + if !ctx .get_player_state(&player_id) .await @@ -38,7 +45,7 @@ pub async fn submit_algorithm( name: algorithm_name, challenge_id, player_id, - breakthrough_id: None, + breakthrough_id, r#type: AlgorithmType::Wasm, fee_paid: config.algorithms.submission_fee, }, @@ -48,6 +55,47 @@ pub async fn submit_algorithm( Ok(algorithm_id) } +#[time] +pub async fn submit_breakthrough( + ctx: &T, + player_id: String, + breakthrough_name: String, + challenge_id: String, + evidence: String, +) -> Result { + let config = ctx.get_config().await; + let latest_block_id = ctx.get_latest_block_id().await; + let latest_block_details = ctx.get_block_details(&latest_block_id).await.unwrap(); + if !ctx + .get_challenge_state(&challenge_id) + .await + .is_some_and(|s| s.round_active <= latest_block_details.round) + { + return Err(anyhow!("Invalid challenge '{}'", challenge_id)); + } + + if !ctx + .get_player_state(&player_id) + .await + .is_some_and(|s| s.available_fee_balance >= config.breakthroughs.submission_fee) + { + return Err(anyhow!("Insufficient balance")); + } + + let breakthrough_id = ctx + .add_breakthrough_to_mempool( + BreakthroughDetails { + name: breakthrough_name, + challenge_id, + player_id, + fee_paid: config.breakthroughs.submission_fee, + }, + evidence, + ) + .await?; + Ok(breakthrough_id) +} + #[time] pub async fn submit_binary( ctx: &T, @@ -88,13 +136,42 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { active_algorithms_details, active_algorithms_state, active_algorithms_block_data, + active_breakthroughs_state, + active_breakthroughs_block_data, active_opow_block_data, + voting_breakthroughs_state, + active_players_state, + active_players_block_data, .. } = cache; let active_algorithm_ids = &block_data.active_ids[&ActiveType::Algorithm]; + let active_breakthrough_ids = &block_data.active_ids[&ActiveType::Breakthrough]; let active_challenge_ids = &block_data.active_ids[&ActiveType::Challenge]; + // update votes + for breakthrough_state in voting_breakthroughs_state.values_mut() { + breakthrough_state.votes_tally = HashMap::from([ + (true, PreciseNumber::from(0)), + (false, PreciseNumber::from(0)), + ]); + } + for (player_id, player_state) in active_players_state.iter() { + let player_data = &active_players_block_data[player_id]; + for (breakthrough_id, vote) in player_state.votes.iter() { + let yes = vote.value; + if let Some(breakthrough_state) = voting_breakthroughs_state.get_mut(breakthrough_id) { + let n = breakthrough_state.round_votes_tallied - block_details.round; + let votes: PreciseNumber = player_data + .deposit_by_locked_period + .iter() + .skip(n as usize) + .sum(); + *breakthrough_state.votes_tally.get_mut(&yes).unwrap() += votes; + } + } + } + // update adoption let mut algorithms_by_challenge = HashMap::>::new(); for algorithm_id in active_algorithm_ids.iter() { @@ -133,11 +210,19 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { active_algorithms_block_data .get_mut(algorithm_id) .unwrap() - .adoption = adoption; + .adoption = adoption.clone(); + + if let Some(breakthrough_id) = &active_algorithms_details[algorithm_id].breakthrough_id + { + active_breakthroughs_block_data + .get_mut(breakthrough_id) + .unwrap() + .adoption += adoption; + } } } - // updat merge points + // update algorithm merge points let adoption_threshold = PreciseNumber::from_f64(config.algorithms.adoption_threshold); for algorithm_id in active_algorithm_ids.iter() { let is_merged = active_algorithms_state[algorithm_id].round_merged.is_some(); @@ -148,7 +233,22 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { } } - // update merges on last block of the round + // update breakthrough merge points + let adoption_threshold = PreciseNumber::from_f64(config.breakthroughs.adoption_threshold); + for breakthrough_id in active_breakthrough_ids.iter() { + let is_merged = active_breakthroughs_state[breakthrough_id] + .round_merged + .is_some(); + let breakthrough_data = active_breakthroughs_block_data + .get_mut(breakthrough_id) + .unwrap(); + + if !is_merged && breakthrough_data.adoption >= adoption_threshold { + breakthrough_data.merge_points += 1; + } + } + + // update merges at last block of the round if (block_details.height + 1) % config.rounds.blocks_per_round == 0 { for algorithm_ids in algorithms_by_challenge.values() { let algorithm_id = algorithm_ids @@ -167,5 +267,34 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .unwrap() .round_merged = Some(block_details.round + 1); } + + for breakthrough_id in active_breakthrough_ids.iter() { + if active_breakthroughs_block_data[breakthrough_id].merge_points + < config.breakthroughs.merge_points_threshold + { + continue; + } + + active_breakthroughs_state + .get_mut(breakthrough_id) + .unwrap() + .round_merged = Some(block_details.round + 1); + } + } + + // update breakthroughs + if (block_details.height + 1) % config.rounds.blocks_per_round == 0 { + let yes_threshold = PreciseNumber::from_f64(config.breakthroughs.min_percent_yes_votes); + let zero = PreciseNumber::from(0); + for breakthrough in voting_breakthroughs_state.values_mut() { + if breakthrough.round_votes_tallied == block_details.round + 1 { + let yes = &breakthrough.votes_tally[&true]; + let no = &breakthrough.votes_tally[&false]; + let total = yes + no; + if total != zero && yes / total >= yes_threshold { + breakthrough.round_active = Some(block_details.round + 1); + } + } + } } } diff --git a/tig-protocol/src/contracts/benchmarks.rs b/tig-protocol/src/contracts/benchmarks.rs index f907d84..3625fbd 100644 --- a/tig-protocol/src/contracts/benchmarks.rs +++ b/tig-protocol/src/contracts/benchmarks.rs @@ -48,7 +48,7 @@ pub async fn submit_precommit( if !ctx .get_algorithm_state(&settings.algorithm_id) .await - .is_some_and(|s| !s.banned && s.round_active <= block_details.round) + .is_some_and(|s| !s.banned && s.round_active.is_some_and(|r| r <= block_details.round)) { return Err(anyhow!("Invalid algorithm '{}'", settings.algorithm_id)); } @@ -76,10 +76,10 @@ pub async fn submit_precommit( }; if lower_frontier .iter() - .any(|lower_point| difficulty.pareto_compare(lower_point) == ParetoCompare::BDominatesA) - || upper_frontier - .iter() - .any(|upper_point| difficulty.pareto_compare(upper_point) == ParetoCompare::ADominatesB) + .any(|lower_point| pareto_compare(difficulty, lower_point) == ParetoCompare::BDominatesA) + || upper_frontier.iter().any(|upper_point| { + pareto_compare(difficulty, upper_point) == ParetoCompare::ADominatesB + }) { return Err(anyhow!("Invalid difficulty. Out of bounds")); } diff --git a/tig-protocol/src/contracts/opow.rs b/tig-protocol/src/contracts/opow.rs index ca4a67f..aa00551 100644 --- a/tig-protocol/src/contracts/opow.rs +++ b/tig-protocol/src/contracts/opow.rs @@ -43,7 +43,11 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let mut phase_in_challenge_ids: HashSet = active_challenge_ids.clone(); for algorithm_id in active_algorithm_ids.iter() { - if active_algorithms_state[algorithm_id].round_active + 1 <= block_details.round { + if active_algorithms_state[algorithm_id] + .round_active + .as_ref() + .is_some_and(|r| *r + 1 <= block_details.round) + { phase_in_challenge_ids.remove(&active_algorithms_details[algorithm_id].challenge_id); } } @@ -62,25 +66,22 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let phase_in_start = (block_details.round - 1) * config.rounds.blocks_per_round; let phase_in_period = config.opow.cutoff_phase_in_period; let phase_in_end = phase_in_start + phase_in_period; - let min_cutoff = config.opow.min_cutoff; let cutoff_cap = (self_deposit[player_id] / deposit_to_cutoff_cap_ratio).to_f64() as u32; let min_num_solutions = active_challenge_ids .iter() .map(|id| num_solutions_by_challenge.get(id).unwrap_or(&0).clone()) .min() .unwrap(); - let mut cutoff = min_cutoff - .max((min_num_solutions as f64 * config.opow.cutoff_multiplier).ceil() as u32) - .max(cutoff_cap); - // if phase_in_challenge_ids.len() > 0 && phase_in_end > block_details.height { - if phase_in_end > block_details.height { + let mut cutoff = cutoff_cap + .min((min_num_solutions as f64 * config.opow.cutoff_multiplier).ceil() as u32); + if phase_in_challenge_ids.len() > 0 && phase_in_end > block_details.height { let phase_in_min_num_solutions = active_challenge_ids .iter() .filter(|&id| !phase_in_challenge_ids.contains(id)) .map(|id| num_solutions_by_challenge.get(id).unwrap_or(&0).clone()) .min() .unwrap(); - let phase_in_cutoff = min_cutoff.max( + let phase_in_cutoff = cutoff_cap.min( (phase_in_min_num_solutions as f64 * config.opow.cutoff_multiplier).ceil() as u32, ); let phase_in_weight = @@ -120,7 +121,7 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .map(|(settings, _)| settings.difficulty.clone()) .collect::(); let mut frontier_indexes = HashMap::::new(); - for (frontier_index, frontier) in pareto_algorithm(points, false).into_iter().enumerate() { + for (frontier_index, frontier) in pareto_algorithm(&points, false).into_iter().enumerate() { for point in frontier { frontier_indexes.insert(point, frontier_index); } @@ -197,23 +198,41 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .collect::(); let (base_frontier, scaling_factor, scaled_frontier) = if points.len() == 0 { let base_frontier: Frontier = vec![min_difficulty.clone()].into_iter().collect(); - let scaling_factor = 0.0; + let scaling_factor = 1.0; let scaled_frontier = base_frontier.clone(); (base_frontier, scaling_factor, scaled_frontier) } else { - let base_frontier = pareto_algorithm(points, true) + let mut base_frontier = pareto_algorithm(&points, true) .pop() .unwrap() .into_iter() .map(|d| d.into_iter().map(|x| -x).collect()) - .collect::() // mirror the points back; - .extend(&min_difficulty, &max_difficulty); - let scaling_factor = (challenge_data.num_qualifiers as f64 + .collect::(); // mirror the points back; + base_frontier = extend_frontier(&base_frontier, &min_difficulty, &max_difficulty); + + let mut scaling_factor = (challenge_data.num_qualifiers as f64 / config.opow.total_qualifiers_threshold as f64) .min(config.challenges.max_scaling_factor); - let scaled_frontier = base_frontier - .scale(&min_difficulty, &max_difficulty, scaling_factor) - .extend(&min_difficulty, &max_difficulty); + + if scaling_factor < 1.0 { + base_frontier = scale_frontier( + &base_frontier, + &min_difficulty, + &max_difficulty, + scaling_factor, + ); + base_frontier = extend_frontier(&base_frontier, &min_difficulty, &max_difficulty); + scaling_factor = (1.0 / scaling_factor).min(config.challenges.max_scaling_factor); + } + + let mut scaled_frontier = scale_frontier( + &base_frontier, + &min_difficulty, + &max_difficulty, + scaling_factor, + ); + scaled_frontier = extend_frontier(&scaled_frontier, &min_difficulty, &max_difficulty); + (base_frontier, scaling_factor, scaled_frontier) }; @@ -248,23 +267,28 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let player_data = active_players_block_data.get_mut(player_id).unwrap(); let player_state = &active_players_state[player_id]; if active_opow_ids.contains(player_id) { - player_data.delegatee = Some(player_id.clone()); - } else if let Some(delegatee) = &player_state.delegatee { - if !active_opow_ids.contains(&delegatee.value) - // || self_deposit[player_id] < config.deposits.delegator_min_deposit - || self_deposit[&delegatee.value] < config.deposits.delegatee_min_deposit - { - continue; - } - player_data.delegatee = Some(delegatee.value.clone()); + // benchmarkers self-delegate 100% to themselves + player_data.delegatees = HashMap::from([(player_id.clone(), 1.0)]); + } else if let Some(delegatees) = &player_state.delegatees { + player_data.delegatees = delegatees + .value + .iter() + .filter(|(delegatee, _)| { + active_opow_ids.contains(delegatee.as_str()) + && self_deposit[delegatee.as_str()] >= config.deposits.delegatee_min_deposit + }) + .map(|(delegatee, fraction)| (delegatee.clone(), *fraction)) + .collect(); } else { continue; } - let opow_data = active_opow_block_data - .get_mut(player_data.delegatee.as_ref().unwrap()) - .unwrap(); - opow_data.delegators.insert(player_id.clone()); - opow_data.delegated_weighted_deposit += player_data.weighted_deposit; + + for (delegatee, fraction) in player_data.delegatees.iter() { + let fraction = PreciseNumber::from_f64(*fraction); + let opow_data = active_opow_block_data.get_mut(delegatee).unwrap(); + opow_data.delegators.insert(player_id.clone()); + opow_data.delegated_weighted_deposit += player_data.weighted_deposit * fraction; + } } let total_deposit = active_opow_block_data .values() @@ -272,7 +296,6 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .sum::(); let zero = PreciseNumber::from(0); - let one = PreciseNumber::from(1); let imbalance_multiplier = PreciseNumber::from_f64(config.opow.imbalance_multiplier); let num_challenges = PreciseNumber::from(active_challenge_ids.len()); @@ -303,9 +326,9 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { let mean_percent_qualifiers = percent_qualifiers.arithmetic_mean(); let max_deposit_to_qualifier_ratio = PreciseNumber::from_f64(config.opow.max_deposit_to_qualifier_ratio); - if mean_percent_qualifiers != zero - && percent_deposit / mean_percent_qualifiers > max_deposit_to_qualifier_ratio - { + if mean_percent_qualifiers == zero { + percent_deposit = zero.clone(); + } else if percent_deposit / mean_percent_qualifiers > max_deposit_to_qualifier_ratio { percent_deposit = mean_percent_qualifiers * max_deposit_to_qualifier_ratio; } @@ -337,51 +360,3 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { data.influence = influence; } } - -fn find_smallest_range_dimension(points: &Frontier) -> usize { - (0..2) - .min_by_key(|&d| { - let (min, max) = points - .iter() - .map(|p| p[d]) - .fold((i32::MAX, i32::MIN), |(min, max), val| { - (min.min(val), max.max(val)) - }); - max - min - }) - .unwrap() -} - -fn pareto_algorithm(points: Frontier, only_one: bool) -> Vec { - if points.is_empty() { - return Vec::new(); - } - let dimension = find_smallest_range_dimension(&points); - let sort_dimension = 1 - dimension; - - let mut buckets: HashMap> = HashMap::new(); - for point in points { - buckets.entry(point[dimension]).or_default().push(point); - } - for (_, group) in buckets.iter_mut() { - // sort descending - group.sort_unstable_by(|a, b| b[sort_dimension].cmp(&a[sort_dimension])); - } - let mut result = Vec::new(); - while !buckets.is_empty() { - let points: HashSet = buckets.values().map(|group| group[0].clone()).collect(); - let frontier = points.pareto_frontier(); - for point in frontier.iter() { - let bucket = buckets.get_mut(&point[dimension]).unwrap(); - bucket.remove(0); - if bucket.is_empty() { - buckets.remove(&point[dimension]); - } - } - result.push(frontier); - if only_one { - break; - } - } - result -} diff --git a/tig-protocol/src/contracts/players.rs b/tig-protocol/src/contracts/players.rs index 376ee72..fb3cc34 100644 --- a/tig-protocol/src/contracts/players.rs +++ b/tig-protocol/src/contracts/players.rs @@ -1,7 +1,10 @@ use crate::context::*; use anyhow::{anyhow, Result}; use logging_timer::time; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::{ + collections::HashMap, + time::{SystemTime, UNIX_EPOCH}, +}; use tig_structs::core::*; use tig_utils::*; @@ -104,31 +107,50 @@ pub async fn submit_deposit( } #[time] -pub async fn set_delegatee( +pub async fn set_delegatees( ctx: &T, player_id: String, - delegatee: String, + delegatees: HashMap, ) -> Result<()> { let config = ctx.get_config().await; let latest_block_id = ctx.get_latest_block_id().await; let latest_block_details = ctx.get_block_details(&latest_block_id).await.unwrap(); let player_state = ctx.get_player_state(&player_id).await.unwrap(); - if let Some(curr_delegatee) = &player_state.delegatee { - if curr_delegatee.value == delegatee { - return Err(anyhow!("Delegatee is already set to {}", delegatee)); - } - if latest_block_details.height - curr_delegatee.block_set - < config.deposits.delegatee_update_period + if delegatees.len() > config.deposits.max_delegations as usize { + return Err(anyhow!( + "Cannot delegate to more than {} players", + config.deposits.max_delegations + )); + } + if let Some(curr_delegatees) = &player_state.delegatees { + if latest_block_details.height - curr_delegatees.block_set + < config.deposits.delegatees_update_period { return Err(anyhow!( - "Can only update delegatee every {} blocks", - config.deposits.delegatee_update_period + "Can only update delegatees every {} blocks. Please wait {} blocks", + config.deposits.delegatees_update_period, + config.deposits.delegatees_update_period + - (latest_block_details.height - curr_delegatees.block_set) )); } } - ctx.set_player_delegatee(player_id, delegatee).await?; + if delegatees.values().any(|&v| v < 0.0) { + return Err(anyhow!("Fraction to delegate cannot be negative")); + } + + if delegatees.values().cloned().sum::() > 1.0 { + return Err(anyhow!("Total fraction to delegate cannot exceed 1.0")); + } + + for delegatee in delegatees.keys() { + if ctx.get_player_details(delegatee).await.is_none() { + return Err(anyhow!("Invalid delegatee '{}'", delegatee)); + } + } + + ctx.set_player_delegatees(player_id, delegatees).await?; Ok(()) } @@ -151,8 +173,10 @@ pub async fn set_reward_share( < config.deposits.reward_share_update_period { return Err(anyhow!( - "Can only update reward share every {} blocks", + "Can only update reward share every {} blocks. Please wait {} blocks", + config.deposits.reward_share_update_period, config.deposits.reward_share_update_period + - (latest_block_details.height - curr_reward_share.block_set) )); } } @@ -172,6 +196,57 @@ pub async fn set_reward_share( Ok(()) } +#[time] +pub async fn set_vote( + ctx: &T, + player_id: String, + breakthrough_id: String, + yes: bool, +) -> Result<()> { + let config = ctx.get_config().await; + let latest_block_id = ctx.get_latest_block_id().await; + let latest_block_details = ctx.get_block_details(&latest_block_id).await.unwrap(); + let player_state = ctx.get_player_state(&player_id).await.unwrap(); + + let breakthrough_state = ctx + .get_breakthrough_state(&breakthrough_id) + .await + .ok_or_else(|| anyhow!("Invalid breakthrough '{}'", breakthrough_id))?; + if breakthrough_state.round_pushed > latest_block_details.round + && latest_block_details.round >= breakthrough_state.round_votes_tallied + { + return Err(anyhow!("Cannot vote on breakthrough '{}'", breakthrough_id)); + } + + if player_state.votes.contains_key(&breakthrough_id) { + return Err(anyhow!( + "You have already voted on breakthrough '{}'", + breakthrough_id + )); + } + + let player_data = ctx + .get_player_block_data(&player_id, &latest_block_id) + .await; + let n = breakthrough_state.round_votes_tallied - latest_block_details.round + + config.breakthroughs.min_lock_period_to_vote; + let zero = PreciseNumber::from(0); + if !player_data.is_some_and(|d| { + d.deposit_by_locked_period + .iter() + .skip(n as usize) + .any(|x| *x > zero) + }) { + return Err(anyhow!( + "You must have deposit still locked {} rounds from now to vote", + n + )); + } + + ctx.set_player_vote(player_id, breakthrough_id, yes).await?; + Ok(()) +} + #[time] pub(crate) async fn update(cache: &mut AddBlockCache) { let AddBlockCache { diff --git a/tig-protocol/src/contracts/rewards.rs b/tig-protocol/src/contracts/rewards.rs index c203eed..286645d 100644 --- a/tig-protocol/src/contracts/rewards.rs +++ b/tig-protocol/src/contracts/rewards.rs @@ -15,10 +15,15 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { active_opow_block_data, active_players_block_data, active_players_state, + active_breakthroughs_state, + active_breakthroughs_block_data, + active_breakthroughs_details, .. } = cache; let active_algorithm_ids = &block_data.active_ids[&ActiveType::Algorithm]; + let active_breakthrough_ids = &block_data.active_ids[&ActiveType::Breakthrough]; + let active_challenge_ids = &block_data.active_ids[&ActiveType::Challenge]; let block_reward = PreciseNumber::from_f64( config @@ -36,7 +41,7 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .block_reward, ); - // update innovator rewards + // update algorithm rewards let adoption_threshold = PreciseNumber::from_f64(config.algorithms.adoption_threshold); let zero = PreciseNumber::from(0); let mut eligible_algorithms_by_challenge = HashMap::>::new(); @@ -56,8 +61,8 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { .reward_by_type .insert(RewardType::Algorithm, zero.clone()); - if (algorithm_data.adoption >= adoption_threshold - || (is_merged && algorithm_data.adoption > zero)) + if algorithm_data.adoption >= adoption_threshold + || (is_merged && algorithm_data.adoption > zero) { eligible_algorithms_by_challenge .entry(algorithm_details.challenge_id.clone()) @@ -91,6 +96,41 @@ pub(crate) async fn update(cache: &mut AddBlockCache) { } } + // update breakthrough rewards + let adoption_threshold = PreciseNumber::from_f64(config.breakthroughs.adoption_threshold); + let reward_pool_per_challenge = block_reward + * PreciseNumber::from_f64(config.rewards.distribution.breakthroughs) + / PreciseNumber::from(active_challenge_ids.len()); + for breakthrough_id in active_breakthrough_ids.iter() { + let breakthrough_state = &active_breakthroughs_state[breakthrough_id]; + let breakthrough_details = &active_breakthroughs_details[breakthrough_id]; + let breakthrough_data = &active_breakthroughs_block_data[breakthrough_id]; + + let is_merged = breakthrough_state.round_merged.is_some(); + if breakthrough_state.banned { + continue; + } + + let reward = if breakthrough_data.adoption >= adoption_threshold + || (is_merged && breakthrough_data.adoption > zero) + { + reward_pool_per_challenge * breakthrough_data.adoption + } else { + zero.clone() + }; + + *active_players_block_data + .get_mut(&breakthrough_details.player_id) + .unwrap() + .reward_by_type + .entry(RewardType::Breakthrough) + .or_insert(zero.clone()) += reward; + active_breakthroughs_block_data + .get_mut(breakthrough_id) + .unwrap() + .reward = reward; + } + // update benchmark rewards let reward_pool = block_reward * PreciseNumber::from_f64(config.rewards.distribution.opow); let zero = PreciseNumber::from(0); diff --git a/tig-protocol/src/lib.rs b/tig-protocol/src/lib.rs index ea29a88..accceaf 100644 --- a/tig-protocol/src/lib.rs +++ b/tig-protocol/src/lib.rs @@ -3,9 +3,9 @@ mod contracts; use context::*; pub use contracts::{ - algorithms::{submit_algorithm, submit_binary}, + algorithms::{submit_algorithm, submit_binary, submit_breakthrough}, benchmarks::{submit_benchmark, submit_fraud, submit_precommit, submit_proof}, - players::{set_delegatee, set_reward_share, submit_deposit, submit_topup}, + players::{set_delegatees, set_reward_share, set_vote, submit_deposit, submit_topup}, }; pub async fn add_block(ctx: &T) { diff --git a/tig-structs/src/config.rs b/tig-structs/src/config.rs index a84a03b..4da83da 100644 --- a/tig-structs/src/config.rs +++ b/tig-structs/src/config.rs @@ -46,8 +46,9 @@ serializable_struct_with_getters! { max_reward_share: f64, default_reward_share: f64, reward_share_update_period: u32, - delegatee_update_period: u32, + delegatees_update_period: u32, delegatee_min_deposit: PreciseNumber, + max_delegations: usize, } } serializable_struct_with_getters! { @@ -104,7 +105,6 @@ serializable_struct_with_getters! { cutoff_phase_in_period: u32, cutoff_multiplier: f64, total_qualifiers_threshold: u32, - min_cutoff: u32, max_deposit_to_qualifier_ratio: f64, deposit_multiplier: f64, deposit_to_cutoff_ratio: f64, diff --git a/tig-structs/src/core.rs b/tig-structs/src/core.rs index 302beda..6b88564 100644 --- a/tig-structs/src/core.rs +++ b/tig-structs/src/core.rs @@ -131,8 +131,8 @@ serializable_struct_with_getters! { AlgorithmState { block_confirmed: u32, round_submitted: u32, - round_pushed: u32, - round_active: u32, + round_pushed: Option, + round_active: Option, round_merged: Option, banned: bool, } @@ -264,6 +264,7 @@ serializable_struct_with_getters! { name: String, player_id: String, challenge_id: String, + fee_paid: PreciseNumber, } } serializable_struct_with_getters! { @@ -271,9 +272,11 @@ serializable_struct_with_getters! { block_confirmed: u32, round_submitted: u32, round_pushed: u32, + round_votes_tallied: u32, + votes_tally: HashMap, round_active: Option, round_merged: Option, - vote_tally: HashMap, + banned: bool, } } serializable_struct_with_getters! { @@ -362,7 +365,7 @@ serializable_struct_with_getters! { PlayerState { total_fees_paid: PreciseNumber, available_fee_balance: PreciseNumber, - delegatee: Option>, + delegatees: Option>>, votes: HashMap>, reward_share: Option>, } @@ -377,7 +380,7 @@ pub enum RewardType { } serializable_struct_with_getters! { PlayerBlockData { - delegatee: Option, + delegatees: HashMap, reward_by_type: HashMap, deposit_by_locked_period: Vec, weighted_deposit: PreciseNumber, diff --git a/tig-token/VestedRoundEarnings.sol b/tig-token/VestedRoundEarnings.sol deleted file mode 100644 index 1b321f7..0000000 --- a/tig-token/VestedRoundEarnings.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; - -contract VestedRoundEarnings is Ownable, ReentrancyGuard { - using SafeMath for uint256; - - IERC20 public token; - uint256 public startTime; - uint256 public constant VESTING_PERIOD = 7 days; - uint256 public totalRoundEarnings; - uint256 public totalAllocatedEarnings; - - bool public withdrawalsEnabled; - - struct Beneficiary { - uint256 roundEarnings; - uint256 withdrawnEarnings; - } - - mapping(address => Beneficiary) public beneficiaries; - - event EarningsAdded(address beneficiary, uint256 amount); - event TokensWithdrawn(address beneficiary, uint256 amount); - event WithdrawalsEnabled(); - - constructor(address _tokenAddress, uint256 _totalRoundEarnings) Ownable(msg.sender) { - token = IERC20(_tokenAddress); - totalRoundEarnings = _totalRoundEarnings; - withdrawalsEnabled = false; - } - - function addBeneficiaries(address[] memory _beneficiaries, uint256[] memory _earnings) external onlyOwner { - require(!withdrawalsEnabled, "Withdrawals are already enabled"); - require(_beneficiaries.length == _earnings.length, "Arrays must have the same length"); - - for (uint256 i = 0; i < _beneficiaries.length; i++) { - address beneficiaryAddress = _beneficiaries[i]; - uint256 earnings = _earnings[i]; - - require(beneficiaryAddress != address(0), "Invalid address"); - require(earnings > 0, "Earnings must be greater than 0"); - - Beneficiary storage beneficiary = beneficiaries[beneficiaryAddress]; - beneficiary.roundEarnings = beneficiary.roundEarnings.add(earnings); - totalAllocatedEarnings = totalAllocatedEarnings.add(earnings); - - emit EarningsAdded(beneficiaryAddress, earnings); - } - } - - function enableWithdrawals() external onlyOwner { - require(!withdrawalsEnabled, "Withdrawals are already enabled"); - require(totalAllocatedEarnings == totalRoundEarnings, "Total allocated earnings do not match totalRoundEarnings"); - require(token.balanceOf(address(this)) >= totalRoundEarnings, "Contract balance does not match totalRoundEarnings"); - - startTime = block.timestamp; - withdrawalsEnabled = true; - - emit WithdrawalsEnabled(); - } - - function withdraw() external nonReentrant { - require(withdrawalsEnabled, "Withdrawals are not enabled yet"); - Beneficiary storage beneficiary = beneficiaries[msg.sender]; - require(beneficiary.roundEarnings > 0, "No earnings available"); - - uint256 withdrawableAmount = getWithdrawableAmount(msg.sender); - require(withdrawableAmount > 0, "No tokens available for withdrawal"); - - beneficiary.withdrawnEarnings = beneficiary.withdrawnEarnings.add(withdrawableAmount); - - require(token.transfer(msg.sender, withdrawableAmount), "Withdrawal failed"); - - emit TokensWithdrawn(msg.sender, withdrawableAmount); - } - - function getWithdrawableAmount(address _beneficiary) public view returns (uint256) { - require(withdrawalsEnabled, "Withdrawals are not enabled yet"); - Beneficiary storage beneficiary = beneficiaries[_beneficiary]; - uint256 elapsedTime = block.timestamp.sub(startTime); - uint256 vestedAmount; - if (elapsedTime >= VESTING_PERIOD) { - vestedAmount = beneficiary.roundEarnings; - } else { - vestedAmount = beneficiary.roundEarnings.mul(elapsedTime).div(VESTING_PERIOD); - } - uint256 withdrawableAmount = vestedAmount.sub(beneficiary.withdrawnEarnings); - return withdrawableAmount; - } - - function getRoundEarnings(address _beneficiary) external view returns (uint256) { - return beneficiaries[_beneficiary].roundEarnings; - } - - function getWithdrawnEarnings(address _beneficiary) external view returns (uint256) { - return beneficiaries[_beneficiary].withdrawnEarnings; - } - - function getRemainingVestingPeriod() external view returns (uint256) { - require(withdrawalsEnabled, "Withdrawals are not enabled yet"); - uint256 elapsedTime = block.timestamp.sub(startTime); - if (elapsedTime >= VESTING_PERIOD) { - return 0; - } - return VESTING_PERIOD.sub(elapsedTime); - } -} \ No newline at end of file diff --git a/tig-token/tig-logo.png b/tig-token/tig-logo.png new file mode 100644 index 0000000..95267c9 Binary files /dev/null and b/tig-token/tig-logo.png differ diff --git a/tig-utils/src/frontiers.rs b/tig-utils/src/frontiers.rs index 73526bb..708da54 100644 --- a/tig-utils/src/frontiers.rs +++ b/tig-utils/src/frontiers.rs @@ -1,219 +1,283 @@ -use rand::Rng; -use std::cmp::min; -use std::collections::HashSet; +use std::{cmp::min, collections::HashMap}; pub type Point = Vec; -pub type Frontier

= HashSet

; +pub type Frontier = Vec; -#[derive(Debug, Clone, PartialEq)] -pub enum PointCompareFrontiers { - Below, - Within, - Above, +fn is_pareto_front_2d(costs: &Vec>) -> Vec { + let n_observations = costs.len(); + if n_observations == 0 { + return vec![]; + } + + let mut indices: Vec = (0..n_observations).collect(); + + // sort by y, then x + indices.sort_by(|&a, &b| { + costs[a][1] + .cmp(&costs[b][1]) + .then(costs[a][0].cmp(&costs[b][0])) + }); + + let mut on_front = vec![true; n_observations]; + let mut stack = Vec::new(); + + // First pass: check dominance based on x-coordinate + for &curr_idx in &indices { + while let Some(&top_idx) = stack.last() { + let cost1: &Vec = &costs[top_idx]; + let cost2: &Vec = &costs[curr_idx]; + + if cost1[0] <= cost2[0] { + break; + } + + stack.pop(); + } + + if let Some(&top_idx) = stack.last() { + let cost1: &Vec = &costs[top_idx]; + let cost2: &Vec = &costs[curr_idx]; + + if cost1[0] <= cost2[0] { + on_front[curr_idx] = false; + } + } + + stack.push(curr_idx); + } + + // Second pass: handle points with equal y-coordinates + let mut i = 0; + while i < indices.len() { + let mut j = i + 1; + while j < indices.len() && costs[indices[j]][1] == costs[indices[i]][1] { + j += 1; + } + + if j - i > 1 { + let min_x_idx = indices[i..j].iter().min_by_key(|&&k| costs[k][0]).unwrap(); + + for &k in &indices[i..j] { + if k != *min_x_idx { + on_front[k] = false; + } + } + } + + i = j; + } + + return on_front; } -#[derive(Debug, Clone, PartialEq)] +pub fn is_pareto_front(costs: &Vec>, assume_unique_lexsorted: bool) -> Vec { + let apply_unique = !assume_unique_lexsorted; + let (unique_costs, order_inv) = if apply_unique { + let (unique, indices) = unique_with_indices(costs); + + (Some(unique), Some(indices)) + } else { + (None, None) + }; + + let on_front = if unique_costs.is_some() { + is_pareto_front_2d(&unique_costs.unwrap()) + } else { + is_pareto_front_2d(costs) + }; + + if let Some(inv) = order_inv { + return inv.iter().map(|&i| on_front[i]).collect(); + } + + return on_front; +} + +// will be about 1.3x faster if we use this and cache it somehow instead of calling it repeatedely on the same points +pub fn unique_with_indices(arr: &Vec>) -> (Vec>, Vec) { + let n = arr.len(); + let mut unique = Vec::with_capacity(n); + let mut indices = Vec::with_capacity(n); + let mut seen = HashMap::with_capacity(n); + + for point in arr.iter() { + if let Some(&idx) = seen.get(point) { + indices.push(idx); + } else { + seen.insert(point, unique.len()); + unique.push(point.clone()); + indices.push(unique.len() - 1); + } + } + + return (unique, indices); +} + +#[derive(PartialEq, Debug)] pub enum ParetoCompare { ADominatesB, Equal, BDominatesA, } -pub trait PointOps { - type Point; - - fn pareto_compare(&self, other: &Self) -> ParetoCompare; - fn scale(&self, min_point: &Self, max_point: &Self, multiplier: f64) -> Self::Point; - fn within( - &self, - lower_frontier: &Frontier, - upper_frontier: &Frontier, - ) -> PointCompareFrontiers; -} -pub trait FrontierOps { - type Point; - - fn pareto_frontier(&self) -> Frontier; - fn extend(&self, min_point: &Self::Point, max_point: &Self::Point) -> Frontier; - fn scale( - &self, - min_point: &Self::Point, - max_point: &Self::Point, - multiplier: f64, - ) -> Frontier; - fn sample(&self, rng: &mut T) -> Self::Point; -} - -impl PointOps for Point { - type Point = Point; - - fn pareto_compare(&self, other: &Self) -> ParetoCompare { - let mut a_dominate_b = false; - let mut b_dominate_a = false; - for (a_val, b_val) in self.iter().zip(other) { - if a_val < b_val { - b_dominate_a = true; - } else if a_val > b_val { - a_dominate_b = true; - } - } - if a_dominate_b == b_dominate_a { - ParetoCompare::Equal - } else if a_dominate_b { - ParetoCompare::ADominatesB - } else { - ParetoCompare::BDominatesA +pub fn pareto_compare(point: &Point, other: &Point) -> ParetoCompare { + let mut a_dominate_b = false; + let mut b_dominate_a = false; + for (a_val, b_val) in point.iter().zip(other) { + if a_val < b_val { + b_dominate_a = true; + } else if a_val > b_val { + a_dominate_b = true; } } - fn scale( - &self, - min_point: &Self::Point, - max_point: &Self::Point, - multiplier: f64, - ) -> Self::Point { - self.iter() - .enumerate() - .map(|(i, value)| { - // Calculate the offset for the current dimension - let offset = ((value - min_point[i] + 1) as f64) * multiplier; - // Scale the point and clamp it between min_point and max_point - (min_point[i] + offset.ceil() as i32 - 1).clamp(min_point[i], max_point[i]) - }) - .collect() + + if a_dominate_b == b_dominate_a { + return ParetoCompare::Equal; + } else if a_dominate_b { + return ParetoCompare::ADominatesB; + } else { + return ParetoCompare::BDominatesA; } - fn within( - &self, - lower_frontier: &Frontier, - upper_frontier: &Frontier, - ) -> PointCompareFrontiers { - // Check if the point is not dominated by any point in the lower frontier - if lower_frontier - .iter() - .any(|lower_point| self.pareto_compare(lower_point) == ParetoCompare::BDominatesA) - { +} + +#[derive(PartialEq, Debug)] +pub enum PointCompareFrontiers { + Below, + Within, + Above, +} + +pub fn pareto_within( + point: &Point, + lower_frontier: &Frontier, + upper_frontier: &Frontier, +) -> PointCompareFrontiers { + for point_ in lower_frontier.iter() { + if pareto_compare(point, point_) == ParetoCompare::BDominatesA { return PointCompareFrontiers::Below; } + } - // Check if the point does not dominate any point in the upper frontier - if upper_frontier - .iter() - .any(|upper_point| self.pareto_compare(upper_point) == ParetoCompare::ADominatesB) - { + for point_ in upper_frontier.iter() { + if pareto_compare(point, point_) == ParetoCompare::ADominatesB { return PointCompareFrontiers::Above; } - - PointCompareFrontiers::Within } + + return PointCompareFrontiers::Within; } -impl FrontierOps for Frontier { - type Point = Point; +pub fn scale_point(point: &Point, min_point: &Point, max_point: &Point, multiplier: f64) -> Point { + return point + .iter() + .enumerate() + .map(|(i, value)| { + let offset = ((value - min_point[i] + 1) as f64) * multiplier; + (min_point[i] + offset.ceil() as i32 - 1).clamp(min_point[i], max_point[i]) + }) + .collect(); +} - fn pareto_frontier(&self) -> Frontier { - let mut frontier = self.clone(); - - for point in self.iter() { - if !frontier.contains(point) { - continue; - } - - let mut dominated_points = HashSet::new(); - for other_point in frontier.iter() { - match point.pareto_compare(other_point) { - ParetoCompare::ADominatesB => { - dominated_points.insert(other_point.clone()); - } - ParetoCompare::BDominatesA => { - dominated_points.insert(point.clone()); - break; - } - ParetoCompare::Equal => {} - } - } - frontier = frontier.difference(&dominated_points).cloned().collect(); - } - - frontier +pub fn scale_frontier( + frontier: &Frontier, + min_point: &Point, + max_point: &Point, + multiplier: f64, +) -> Frontier { + if frontier.is_empty() { + return vec![]; } - fn extend(&self, min_point: &Self::Point, max_point: &Self::Point) -> Frontier { - let mut frontier = self.clone(); - (0..min_point.len()).into_iter().for_each(|i| { - let mut d = min_point.clone(); - if let Some(v) = frontier.iter().map(|d| d[i]).max() { - d[i] = v; - } - if !frontier.contains(&d) { - d[i] = min(d[i] + 1, max_point[i]); - frontier.insert(d); - } - }); - frontier + + let scaled_frontier = frontier + .iter() + .map(|point| scale_point(&point, min_point, max_point, multiplier)) + .collect(); + + if multiplier > 1.0 { + return pareto_frontier(&scaled_frontier); } - fn scale( - &self, - min_point: &Self::Point, - max_point: &Self::Point, - multiplier: f64, - ) -> Frontier { - let frontier: Frontier = self + + let mirrored_frontier = scaled_frontier + .into_iter() + .map(|d| d.iter().map(|x| -x).collect()) // mirror the points so easiest difficulties are first + .collect::(); + + return pareto_frontier(&mirrored_frontier) + .iter() + .map(|d| d.iter().map(|x| -x).collect()) + .collect(); +} + +pub fn pareto_algorithm(points: &Vec>, only_one: bool) -> Vec> { + if points.len() == 0 { + return vec![]; + } + + let points_inverted = points + .iter() + .map(|d| d.iter().map(|x| -x).collect()) + .collect::>(); + + let mut frontiers = Vec::new(); + let (mut remaining_points, _) = unique_with_indices(&points_inverted); + + loop { + let on_front = is_pareto_front(&remaining_points, true); + + // Extract frontier points + let frontier: Vec<_> = remaining_points .iter() - .map(|point| point.scale(min_point, max_point, multiplier)) + .zip(on_front.iter()) + .filter(|(_, &is_front)| is_front) + .map(|(point, _)| point.to_vec()) .collect(); - if multiplier > 1.0 { - frontier.pareto_frontier() - } else { - frontier - .into_iter() - .map(|d| d.iter().map(|x| -x).collect()) // mirror the points so easiest difficulties are first + + frontiers.push(frontier); + + let new_points: Vec<_> = remaining_points + .iter() + .zip(on_front.iter()) + .filter(|(_, &is_front)| !is_front) + .map(|(point, _)| point.to_vec()) + .collect(); + + if new_points.is_empty() { + break; + } + + remaining_points = new_points; + + if only_one { + break; + } + } + + return frontiers + .iter() + .map(|d| { + d.iter() + .map(|x| x.iter().map(|y| -y).collect()) .collect::() - .pareto_frontier() - .iter() - .map(|d| d.iter().map(|x| -x).collect()) - .collect() - } - } - fn sample(&self, rng: &mut R) -> Self::Point { - // FIXME only works for 2 dimensional points - // Potential strategy for >2d: triangulate -> sample triangle -> sample point in triangle - match self.iter().next() { - None => panic!("Frontier is empty"), - Some(point) => { - if point.len() != 2 { - panic!("Only 2 dimensional points are supported"); - } - } - }; - // randomly pick a dimension - let dim = (rng.next_u32() % 2) as usize; - let dim2 = (dim + 1) % 2; - - // sort points by that dimension - let mut sorted_points: Vec<&Point> = self.iter().collect(); - sorted_points.sort_by(|a, b| a[dim].cmp(&b[dim])); - - // sample value in that dimension - let min_v = sorted_points.first().unwrap()[dim]; - let max_v = sorted_points.last().unwrap()[dim]; - let rand_v = rng.gen_range(min_v..=max_v); - - // interpolate value in the other dimension - match sorted_points.binary_search_by(|point| point[dim].cmp(&rand_v)) { - Ok(idx) => sorted_points[idx].clone(), - Err(idx) => { - let a = sorted_points[idx - 1]; - let b = sorted_points[idx]; - let ratio = (rand_v - a[dim]) as f64 / (b[dim] - a[dim]) as f64; - let rand_v2 = (a[dim2] as f64 + ratio * (b[dim2] - a[dim2]) as f64).ceil() as i32; - // a is smaller than b in dim, but larger in dim2 - if rand_v2 == a[dim2] { - a.clone() - } else { - (0..2) - .into_iter() - .map(|i| if i == dim { rand_v } else { rand_v2 }) - .collect() - } - } - } - } + }) + .collect(); +} + +pub fn pareto_frontier(frontier: &Frontier) -> Frontier { + return pareto_algorithm(frontier, true).first().unwrap().to_vec(); +} + +pub fn extend_frontier(frontier: &Frontier, min_point: &Point, max_point: &Point) -> Frontier { + let mut frontier = frontier.clone(); + (0..min_point.len()).into_iter().for_each(|i| { + let mut d = min_point.clone(); + if let Some(v) = frontier.iter().map(|d| d[i]).max() { + d[i] = v; + } + if !frontier.contains(&d) { + d[i] = min(d[i] + 1, max_point[i]); + frontier.push(d); + } + }); + + return frontier; } diff --git a/tig-utils/src/lib.rs b/tig-utils/src/lib.rs index 834cc9b..ff95f59 100644 --- a/tig-utils/src/lib.rs +++ b/tig-utils/src/lib.rs @@ -1,7 +1,5 @@ mod eth; pub use eth::*; -mod frontiers; -pub use frontiers::*; mod hash; pub use hash::*; mod json; @@ -14,3 +12,5 @@ pub use number::*; mod request; #[cfg(any(feature = "request", feature = "request-js"))] pub use request::*; +mod frontiers; +pub use frontiers::*; diff --git a/tig-utils/tests/frontiers.rs b/tig-utils/tests/frontiers.rs index ddaa8a3..22fded0 100644 --- a/tig-utils/tests/frontiers.rs +++ b/tig-utils/tests/frontiers.rs @@ -1,15 +1,24 @@ -use tig_utils::{Frontier, FrontierOps, ParetoCompare, PointCompareFrontiers, PointOps}; +use tig_utils::{ + extend_frontier, pareto_compare, pareto_frontier, pareto_within, scale_frontier, scale_point, + Frontier, ParetoCompare, PointCompareFrontiers, +}; #[test] fn test_pareto_compare() { - assert_eq!(vec![1, 0].pareto_compare(&vec![1, 0]), ParetoCompare::Equal); - assert_eq!(vec![1, 0].pareto_compare(&vec![0, 1]), ParetoCompare::Equal); assert_eq!( - vec![1, 1].pareto_compare(&vec![0, 1]), + pareto_compare(&vec![1, 0], &vec![1, 0]), + ParetoCompare::Equal + ); + assert_eq!( + pareto_compare(&vec![0, 1], &vec![0, 1]), + ParetoCompare::Equal + ); + assert_eq!( + pareto_compare(&vec![1, 1], &vec![0, 1]), ParetoCompare::ADominatesB ); assert_eq!( - vec![1, 0].pareto_compare(&vec![1, 1]), + pareto_compare(&vec![1, 0], &vec![1, 1]), ParetoCompare::BDominatesA ); } @@ -29,8 +38,8 @@ fn test_pareto_frontier() { .into_iter() .collect(); assert_eq!( - points.pareto_frontier(), - vec![vec![2, 2], vec![3, 1], vec![1, 3]] + pareto_frontier(&points), + vec![vec![3, 1], vec![2, 2], vec![1, 3]] .into_iter() .collect::() ); @@ -40,11 +49,11 @@ fn test_pareto_frontier() { fn test_scale_point() { // ceil((x - min + 1) * multiplier) assert_eq!( - vec![3, 1].scale(&vec![0, 0], &vec![10, 10], 1.2), + scale_point(&vec![3, 1], &vec![0, 0], &vec![10, 10], 1.2), vec![4, 2] ); assert_eq!( - vec![6, 2].scale(&vec![0, 0], &vec![10, 10], 0.7), + scale_point(&vec![6, 2], &vec![0, 0], &vec![10, 10], 0.7), vec![4, 2] ); } @@ -55,13 +64,13 @@ fn test_scale_frontier() { .into_iter() .collect(); assert_eq!( - frontier.scale(&vec![0, 0], &vec![10, 10], 1.2), + scale_frontier(&frontier, &vec![0, 0], &vec![10, 10], 1.2), vec![vec![4, 2], vec![3, 3], vec![1, 5]] .into_iter() .collect::() ); assert_eq!( - frontier.scale(&vec![0, 0], &vec![10, 10], 0.6), + scale_frontier(&frontier, &vec![0, 0], &vec![10, 10], 0.6), vec![vec![1, 1], vec![0, 2]] .into_iter() .collect::() @@ -74,8 +83,8 @@ fn test_extend() { .into_iter() .collect(); assert_eq!( - frontier.extend(&vec![0, 0], &vec![10, 10]), - vec![vec![4, 0], vec![3, 1], vec![2, 2], vec![0, 4]] + extend_frontier(&frontier, &vec![0, 0], &vec![10, 10]), + vec![vec![3, 1], vec![2, 2], vec![0, 4], vec![4, 0]] .into_iter() .collect::() ); @@ -90,19 +99,19 @@ fn test_within() { .into_iter() .collect(); assert_eq!( - vec![4, 4].within(&frontier1, &frontier2), + pareto_within(&vec![4, 4], &frontier1, &frontier2), PointCompareFrontiers::Within ); assert_eq!( - vec![4, 0].within(&frontier1, &frontier2), + pareto_within(&vec![4, 0], &frontier1, &frontier2), PointCompareFrontiers::Within ); assert_eq!( - vec![5, 4].within(&frontier1, &frontier2), + pareto_within(&vec![5, 4], &frontier1, &frontier2), PointCompareFrontiers::Above ); assert_eq!( - vec![1, 2].within(&frontier1, &frontier2), + pareto_within(&vec![1, 2], &frontier1, &frontier2), PointCompareFrontiers::Below ); } diff --git a/tig-worker/src/main.rs b/tig-worker/src/main.rs index 173c58b..aec620e 100644 --- a/tig-worker/src/main.rs +++ b/tig-worker/src/main.rs @@ -3,9 +3,10 @@ use anyhow::{anyhow, Result}; use clap::{arg, Command}; use futures::stream::{self, StreamExt}; use serde_json::json; -use std::{collections::HashMap, fs, path::PathBuf, sync::Arc}; -use tig_structs::core::{BenchmarkSettings, MerkleProof}; -use tig_utils::{dejsonify, jsonify, MerkleHash, MerkleTree}; +use std::{fs, path::PathBuf, sync::Arc}; +use tig_structs::core::BenchmarkSettings; +use tig_utils::{compress_obj, dejsonify, jsonify, MerkleHash, MerkleTree}; +use tig_worker::OutputData; use tokio::runtime::Runtime; fn cli() -> Command { @@ -89,15 +90,14 @@ fn cli() -> Command { .default_value("1000000000") .value_parser(clap::value_parser!(u64)), ) - .arg( - arg!(--sampled [SAMPLED_NONCES] "Sampled nonces for which to generate proofs") - .num_args(1..) - .value_parser(clap::value_parser!(u64)), - ) .arg( arg!(--workers [WORKERS] "Number of worker threads") .default_value("1") .value_parser(clap::value_parser!(usize)), + ) + .arg( + arg!(--output [OUTPUT_FOLDER] "If set, the data for nonce will be saved as '.json' in this folder") + .value_parser(clap::value_parser!(PathBuf)), ), ) } @@ -120,7 +120,6 @@ fn main() { *sub_m.get_one::("NONCE").unwrap(), sub_m.get_one::("SOLUTION").unwrap().clone(), ), - Some(("compute_batch", sub_m)) => compute_batch( sub_m.get_one::("SETTINGS").unwrap().clone(), sub_m.get_one::("RAND_HASH").unwrap().clone(), @@ -130,11 +129,8 @@ fn main() { sub_m.get_one::("WASM").unwrap().clone(), *sub_m.get_one::("mem").unwrap(), *sub_m.get_one::("fuel").unwrap(), - sub_m - .get_many::("sampled") - .map(|values| values.cloned().collect()) - .unwrap_or_default(), *sub_m.get_one::("workers").unwrap(), + sub_m.get_one::("output").cloned(), ), _ => Err(anyhow!("Invalid subcommand")), } { @@ -199,46 +195,39 @@ fn compute_batch( wasm_path: PathBuf, max_memory: u64, max_fuel: u64, - sampled_nonces: Vec, num_workers: usize, + output_folder: Option, ) -> Result<()> { if num_nonces == 0 || batch_size < num_nonces { return Err(anyhow!( "Invalid number of nonces. Must be non-zero and less than batch size" )); } - let end_nonce = start_nonce + num_nonces; - for &nonce in &sampled_nonces { - if nonce < start_nonce || nonce >= end_nonce { - return Err(anyhow!( - "Sampled nonce {} is out of range [{}, {})", - nonce, - start_nonce, - end_nonce - )); - } - } if batch_size == 0 || (batch_size & (batch_size - 1)) != 0 { return Err(anyhow!("Batch size must be a power of 2")); } + if let Some(path) = &output_folder { + fs::create_dir_all(path)?; + } + let settings = Arc::new(load_settings(&settings)); let wasm = Arc::new(load_wasm(&wasm_path)); let runtime = Runtime::new()?; runtime.block_on(async { - let mut output_data_map = HashMap::new(); let mut hashes = vec![MerkleHash::null(); num_nonces as usize]; let mut solution_nonces = Vec::new(); // Create a stream of nonces and process them concurrently - let results = stream::iter(start_nonce..end_nonce) + let results = stream::iter(start_nonce..(start_nonce + num_nonces)) .map(|nonce| { let settings = Arc::clone(&settings); let wasm = Arc::clone(&wasm); let rand_hash = rand_hash.clone(); - let sampled_nonces = sampled_nonces.clone(); + let output_folder = output_folder.clone(); + tokio::spawn(async move { let (output_data, err_msg) = worker::compute_solution( &settings, @@ -257,17 +246,12 @@ fn compute_batch( ) .is_ok(); let hash = MerkleHash::from(output_data.clone()); - // only keep the data if required - let output_data = if sampled_nonces.contains(&nonce) { - Some(output_data) - } else { - None - }; - Ok::<(u64, Option, MerkleHash, bool), anyhow::Error>(( + + Ok::<(u64, MerkleHash, bool, Option), anyhow::Error>(( nonce, - output_data, hash, is_solution, + output_folder.is_some().then(|| output_data), )) }) }) @@ -275,31 +259,28 @@ fn compute_batch( .collect::>() .await; + let mut dump = Vec::new(); for result in results { - let (nonce, output_data, hash, is_solution) = result??; - if let Some(output_data) = output_data { - output_data_map.insert(nonce, output_data); - } + let (nonce, hash, is_solution, output_data) = result??; if is_solution { solution_nonces.push(nonce); } + if let Some(output_data) = output_data { + dump.push(output_data); + } *hashes.get_mut((nonce - start_nonce) as usize).unwrap() = hash; } + if let Some(path) = output_folder { + dump.sort_by_key(|data| data.nonce); + let file_path = path.join("data.zlib"); + fs::write(&file_path, compress_obj(&dump))?; + } let tree = MerkleTree::new(hashes, batch_size as usize)?; let merkle_root = tree.calc_merkle_root(); - let mut merkle_proofs = Vec::new(); - for (nonce, output_data) in output_data_map { - merkle_proofs.push(MerkleProof { - leaf: output_data, - branch: tree.calc_merkle_branch((nonce - start_nonce) as usize)?, - }); - } - let result = json!({ "merkle_root": merkle_root, - "merkle_proofs": merkle_proofs, "solution_nonces": solution_nonces, });