tiger2005's blog

By tiger2005, history, 3 months ago, In English

CPLib is a library written in C++ for processing test data of competitive programming problems. It helps you write clear and efficient checkers, interactors, validators, and generators. CPLib uses "variable input template" as its major feature, provides friendly feedback for humans and backends, and is working hard to reach better compatibility and efficiency.

You can get CPLib from Github Repository. The "single-header-snapshot" branch automatically updates single-header version of CPLib which is easy to use. Also, you can visit cplib.vercel.app for more information. For regex-related questions, please refer to the FAQ page.

Here is a basic example of a checker using CPLib. For more examples, visit the links above.

#include "cplib.hpp"
 
using namespace cplib;
 
CPLIB_REGISTER_CHECKER(chk);
 
void checker_main() {
  auto var_ans = var::i32("ans", -2000, 2000);
 
  int ouf_output = chk.ouf.read(var_ans);
  int ans_output = chk.ans.read(var_ans);
 
  if (ouf_output != ans_output) {
    chk.quit_wa(format("Expected %d, got %d", ans_output, ouf_output));
  }
 
  chk.quit_ac();
}

The project is mainly written by yzy1, and I, tiger2005, work on syntax designs and some tiny stuffs. The project is still under development, so feel free to make suggestions!

  • Vote: I like it
  • +161
  • Vote: I do not like it

»
3 months ago, # |
Rev. 2   Vote: I like it +51 Vote: I do not like it

CPLib welcomes contributions from the community! Feel free to star & fork & issue & pr!

»
3 months ago, # |
  Vote: I like it +27 Vote: I do not like it

What's wrong with testlib since this project exists?

Any success with creating problem in polygon? Or there is also chinise analog of polygon?

Why c++ but not rust?

  • »
    »
    3 months ago, # ^ |
    Rev. 2   Vote: I like it +30 Vote: I do not like it

    The code in testlib is full of historical legacy factors. For example, when in app mode, the XML output defaults to using a Russian encoding instead of UTF-8.

    Testlib has poor compatibility with higher-level testing systems. If you search for testlib on GitHub, you will find versions like testlib for lemon, testlib for DOMJudge, testlib for LibreOJ, and others that are compatible with various higher-level testing systems. In other words, if you want to make testlib compatible with a new testing system, you need to modify the entire header file of testlib, and these modified versions of testlib are based on different versions of the original testlib, which may result in API compatibility issues. However, it should be noted that the differences between these testing systems only lie in the command-line parameters used when invoking the testlib program, the locations of files or streams to be read or written, and the format of the report. On the other hand, by using CPLib, you only need to write an initializer for each testing system to adapt to multiple testing systems without changing the CPLib header file.

    The variable reading API in testlib is too simple. Testlib only provides functions to read integers/longs/strings and their corresponding vector types. In contrast, the variable reading template design in CPLib allows you to combine and reuse variables (you can refer to the documentation here).

    CPLib has optimized some of the more cumbersome APIs in testlib. For example, when writing a generator, the opt<T>("name", default) syntax in testlib can easily cause problems if the name is misspelled, while the corresponding design in CPLib first binds the command-line parameters to a structure before parsing, which can effectively avoid such problems.

    The pattern functionality in testlib is broken, and the syntax for alternative sets is incomplete. For example, id-(aa|bb) can be correctly parsed in testlib, but (aa|bb)-id cannot.

    As for why not use Rust, although Rust is a good programming language, I believe that over 95% of algorithmic competition participants who are skilled enough to create problems are proficient in C++, while the number of those who can master Rust is probably less than half.

    • »
      »
      »
      3 months ago, # ^ |
      Rev. 2   Vote: I like it +22 Vote: I do not like it

      Please explain more about generator problem. Testlib-generator will crash if argument are 1) not exist in args 2) are not used in generator. So what you mean by misspelled?

      Also example in link looks verbose comparing to testlib, especially this args. and .value everywhere

      • »
        »
        »
        »
        3 months ago, # ^ |
        Rev. 2   Vote: I like it 0 Vote: I do not like it

        Just like in this example:

        #include <bits/stdc++.h>
        
        #include "testlib.h"
        
        int main(int argc, char **argv) {
          registerGen(argc, argv, 1);
          int x = opt<int>("n-min", 1);
          // Do some work
          int y = opt<int>("n_min", 1); // Misspelled! This should be n-min!
          // Do some work
          return 0;
        }
        

        The design of testlib is to provide a string representing the name of the parameter every time it is needed on the command line, without any checking. This may lead to more errors compared to first defining all the parameters and then using them.

        As for the issue with args. and .value, it is necessary to keep args. because nobody wants to pollute the global namespace with a bunch of global variables or add ugly args_xyz prefixes to all parameter variables. However, if you find the name args too long, you can also rename it when defining generator_main. As for .value, the initial design was to overload the operator * to achieve the operation of taking the value (just like pointers), but I don't really like overloading operators that have different meanings from the original ones (It's weird to overload operator * for something that's not a pointer). so in the end, I chose the syntax of .value.

        • »
          »
          »
          »
          »
          3 months ago, # ^ |
          Rev. 2   Vote: I like it +12 Vote: I do not like it

          I finally got it — its about default parameters (I personally rarely using it). So it is good example.

          But this is also good example of bad programming — typing same code twice... Especially code wich potentially will be changed. So so.

          Looking at my typical generators I see other not comfortable thing. I am coding all generators in one file, so typical code is like

          auto type = opt<string>("tp");
          if(tp == "rand") {
          //...
          } else
          if(tp == "spec1") {
          //...
          } else ...
          

          In each if a can request additional arguments. So create general class is messy and dangerous here.

  • »
    »
    3 months ago, # ^ |
      Vote: I like it +22 Vote: I do not like it

    In short, our goal in developing CPLib is similar to that of the typst team's development of typst a year ago: why, when there was already LaTeX at the time, did someone still develop typst?

    Just as typst aimed to provide a simpler and more user-friendly interface for LaTeX users, CPLib aims to provide a more modern, flexible, and easy-to-use library for algorithmic competition problem setters. While testlib has been widely used in the past, it has several issues that CPLib seeks to address. By providing a more powerful and user-friendly library, we hope to make the process of creating high-quality problems for algorithmic competitions more accessible and enjoyable for everyone.

»
3 months ago, # |
  Vote: I like it +8 Vote: I do not like it

cool

»
3 months ago, # |
  Vote: I like it +15 Vote: I do not like it

Does this library guarantees the same result of generated data on different compiler versions / backends (like clang, gcc)?

  • »
    »
    3 months ago, # ^ |
      Vote: I like it +22 Vote: I do not like it

    It seems like no. It is just a wrapper around the STL distributions which are implementation-defined behaviour.

  • »
    »
    3 months ago, # ^ |
    Rev. 4   Vote: I like it -17 Vote: I do not like it

    Yes, it guarantees the same result of generated data on different compiler. Unless your code has undefined behavior, only the functions or classes used for randomization (rand(), mt19937) will affect the output of your code.

    The design of CPLib prohibits you from using random functions in the standard library such as std::rand, and instead uses cplib::Random. Under generator, the seed of cplib::Random is only related to the command line parameters of generator.

    EDIT: There are problems with the current implementation, and this is not what we expected to see. The problem will be fixed in the next few commits.

    EDIT 2: Fixed in latest commit!

    • »
      »
      »
      3 months ago, # ^ |
        Vote: I like it +28 Vote: I do not like it

      You use IntegerDist = std::uniform_int_distribution<T>; right? std::uniform_int_distribution<T> is implementation defined behavior and will (can) therefore behave differently depending on compiler and version. GCC just recently (with version 11) changed the implementation of this distribution. (In fact the c++ standard gives almost no guarantees for the distributions besides that they are unbiased).

      • »
        »
        »
        »
        3 months ago, # ^ |
          Vote: I like it +28 Vote: I do not like it

        Thank you for your feedback. We didn't notice this problem when implementing this part. The current effect is also inconsistent with the design of CPLib to eliminate platform independence. We will fix this problem in the next few commits.

      • »
        »
        »
        »
        3 months ago, # ^ |
          Vote: I like it +20 Vote: I do not like it

        Fixed in latest commit!

        It has been tested on Linux GCC (libstdc++) & Clang (libc++) and Windows GCC & MSVC and been confirmed that the same output can be obtained. And later, I will try how to use CI to automate the testing process on different platforms.

        Finally, thank you for your contribution in pointing out potential bugs in this project! CPLib developers will continue to pay attention to issues from the community.

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Cool!

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Why didn't you collab with Polygon team? Do you plan to make CPLib the default choice in Polygon?

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Really great job! thanks for sharing it with us. Is there a list of all possible gen.rnd.next functions like the one for testlib ? Thanks

  • »
    »
    3 months ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    The function gen.rnd.next has three overloaded versions: zero parameters, one parameter, or two parameters. The most commonly used version is the one with two parameters, which is used to generate a uniformly random integer or real number within a specified range. For example, gen.rnd.next(1,5) will generate an integer within the range [1,5], and gen.rnd.next('A','Z') will generate a character within the range ['A','Z']. The return type of the result will be automatically inferred based on the types of the parameters. In some cases, you can also manually specify the type by using something like gen.rnd.next<double>(1,5).

    When only one parameter is provided, it accepts a floating-point number true_prob and returns a bool type. The probability of the result being true is true_prob, and the probability of it being false is 1-true_prob.

    When zero parameters are provided, it is equivalent to gen.rnd.next(0.5), meaning it will randomly return true or false with a 50% probability each.

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

Hi, I followed the steps in your documentation for the checker example code, Currently in empty directory I placed cplib.hpp, chk.cpp and data folder mentioned in the doc., but after compiling the chk.cpp, I am getting an error on cplib.hpp file. Please help with this, I don't know if I am doing something wrong?

Error
  • »
    »
    3 months ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Can you provide a more detailed description of the environment, such as operating system type, architecture, compiler type and version?

    • »
      »
      »
      3 months ago, # ^ |
        Vote: I like it 0 Vote: I do not like it

      I am currently using WSL2 in my Windows11, Basically Ubuntu 22.04.3 LTS (GNU/Linux 5.15.133.1-microsoft-standard-WSL2 x86_64). Compiler I am using g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0.

      • »
        »
        »
        »
        3 months ago, # ^ |
          Vote: I like it 0 Vote: I do not like it

        This seems to be due to issues with the handling of friend functions and templates in older versions of gcc (lower than 11).

        The latest version commit fixes this problem and now it works on versions as low as gcc8.

        • »
          »
          »
          »
          »
          3 months ago, # ^ |
            Vote: I like it +8 Vote: I do not like it

          Cool, It worked. Although I will be starting to use g++-13 cause the previous version of your lib worked fine.

          I also liked your lib. It has some great and interesting features. Kudos.

    • »
      »
      »
      3 months ago, # ^ |
      Rev. 2   Vote: I like it 0 Vote: I do not like it

      Is it something wrong with WSL2?, I ran with same configuration in my Windows compiler. It is working fine, just had to update regex.h file as shown in FAQ.

»
3 months ago, # |
  Vote: I like it 0 Vote: I do not like it

How would I use this on Polygon? I think regex.h doesn't exist on Polygon for g++ and I am not sure how to add it to the system include path there.

  • »
    »
    3 months ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    https://github.com/rindag-devs/musl-regex-standalone Add this file in Polygon file system and cplib should work.

    • »
      »
      »
      3 months ago, # ^ |
      Rev. 3   Vote: I like it 0 Vote: I do not like it

      I already tried adding it but the source in cplib.hpp uses #include <regex.h>, and I think that doesn't look for local paths, only system paths.

      I can get it to work by changing all #include <regex.h>s in cplib.hpp to #include "regex.h", uploading the custom regex.h, and using winlibs gcc for source files.

      • »
        »
        »
        »
        2 months ago, # ^ |
          Vote: I like it +5 Vote: I do not like it

        This is because regex.h you added is a custom header. In fact, regex.h is implemented for Linux platform, and it has good performance, but it's somehow difficult to use regex.h in Windows, and the official <regex> seems too slow. We will find out a suitable way to import regex later on.

    • »
      »
      »
      2 months ago, # ^ |
        Vote: I like it +1 Vote: I do not like it

      When using on Polygon, a validator gives an error of "Validator 'validator.exe' returns exit code 1 [{"status": "internal_error", "message": "Unknown option: --testset"}]".

      • »
        »
        »
        »
        2 months ago, # ^ |
          Vote: I like it +5 Vote: I do not like it

        The issue is mainly caused by additional parameters from Polygon itself. We are working to make CPLib compatible with Polygon, but this will take some time because of personal schedules.