An equation solving library written purely in Dart.

Overview

dart_equations logo

An equation solving library written purely in Dart

CI status Stars count on GitHub Stars count on GitHub


Thanks to the equations package you will be able to solve numerical analysis problems with ease. It's been written purely in Dart, meaning that it has no platform-specific dependencies and it doens't require Flutter to work. You can use, for example, equations with Flutter for web, desktop and mobile. Here's a summary of what you can do with this package:

  • Solve polynomial equations with Algebraic types;
  • Solve nonlinear equations with Nonlinear types;
  • Solve linear systems of equations with SystemSolver types;
  • Evaluate integrals with NumericalIntegration types;
  • Interpolate data points with Interpolation types.

In addition, you can also find utilities to work with:

  • Real and complex matrices, using the Matrix<T> types;
  • Complex number, using the Complex type;
  • Fractions, using the Fraction and MixedFraction types.

This package is meant to be used with Dart 2.12 or higher because the code is entirely null safe. There is a demo, built with Flutter, that shows an example on how this library can be used (especially for numerical analysis apps) πŸš€

Equation Solver logo

Equation Solver - Flutter web demo

The source code of the above website can be found at example/flutter_example. In the following lines, you'll find an overview of the various types in this package and their APIs; make sure to visit the pub.dev documentation for details about methods signatures and docstring comments.


Algebraic (Polynomial equations)

Use one of the following classes to find the roots of a polynomial equation (also known as "algebraic equation"). You can use both complex numbers and fractions as coefficients.

Solver name Equation Params field
Constant f(x) = a a ∈ C
Linear f(x) = ax + b a, b ∈ C
Quadratic f(x) = ax2 + bx + c a, b, c ∈ C
Cubic f(x) = ax3 + bx2 + cx + d a, b, c, d ∈ C
Quartic f(x) = ax4 + bx3 + cx2 + dx + e a, b, c, d, e ∈ C
DurandKerner Any polynomial P(xi) where xi are coefficients xi ∈ C

There's a formula for polynomials up to the fourth degree, as explained by Galois Theory. Roots of polynomials whose degree is 5 or higher, must be seeked using DurandKerner's method or any other root-finding algorithm. For this reason, we suggest to go for the following approach:

  • Use Linear to find the roots of a polynomial whose degree is 1.
  • Use Quadratic to find the roots of a polynomial whose degree is 2.
  • Use Cubic to find the roots of a polynomial whose degree is 3.
  • Use Quartic to find the roots of a polynomial whose degree is 4.
  • Use DurandKerner to find the roots of a polynomial whose degree is 5 or higher.

Note that DurandKerner can be used with any polynomials, so you could use it (for example) to solve a cubic equation as well. However, DurandKerner internally uses loops, derivatives, and other mechanics to approximate the actual roots. When trying to solve a polynomial equation whose degree is 4 or lower, prefer using Quartic, Cubic, Quadratic and Linear since they use direct formulas to find the roots. Here's how you can solve a cubic:

// f(x) = (2-3i)x^3 + 6/5ix^2 - (-5+i)x - (9+6i)
final equation = Cubic(
  a: Complex(2, -3),
  b: Complex.fromImaginaryFraction(Fraction(6, 5)),
  c: Complex(5, -1),
  d: Complex(-9, -6)
);

final degree = equation.degree; // 3
final isReal = equation.isRealEquation; // false
final discr = equation.discriminant(); // -31299.688 + 27460.192i

// f(x) = (2 - 3i)x^3 + 1.2ix^2 + (5 - 1i)x + (-9 - 6i)
print("$equation");

// f(x) = (2 - 3i)x^3 + 6/5ix^2 + (5 - 1i)x + (-9 - 6i)
print(equation.toStringWithFractions());

/*
 * Prints the roots of the equation:
 *
 *  x1 = 0.348906207844 - 1.734303423032i
 *  x2 = -1.083892638909 + 0.961044482775
 *  x3 = 1.011909507988 + 0.588643555642
 * */
for (final root in equation.solutions()) {
  print(root);
}

Alternatively, you could have used DurandKerner to solve the same equation:

// f(x) = (2-3i)x^3 + 6/5ix^2 - (-5+i)x - (9+6i)
final equation = DurandKerner(
  coefficients: [
    Complex(2, -3),
    Complex.fromImaginaryFraction(Fraction(6, 5)),
    Complex(5, -1),
    Complex(-9, -6),
  ]
);

/*
 * Prints the roots of the equation:
 *
 *  x1 = 1.0119095 + 0.5886435
 *  x2 = 0.3489062 - 1.7343034i
 *  x3 = -1.0838926 + 0.9610444
 * */
for (final root in equation.solutions()) {
  print(root);
}

As we've already pointed out, both ways are equivalent but DurandKerner is computationally slower than Cubic and it doesn't guaranteed to always converge to the correct roots. Use DurandKerner only when the degree of your polynomial is greater or equal than 5.

final quadratic = Algebraic.from(const [
  Complex(2, -3),
  Complex.i(),
  Complex(1, 6)
]);

final quartic = Algebraic.fromReal(const [
  1, -2, 3, -4, 5
]);

The factory constructor Algebraic.from() automatically returns the best type of Algebraic according with the number of parameters you've passed.

Nonlinear equations

Use one of the following classes, representing a root-finding algorithm, to find a root of an equation. Only real numbers are allowed. This package supports the following root finding methods:

Solver name Params field
Bisection a, b ∈ R
Chords a, b ∈ R
Netwon x0 ∈ R
Secant a, b ∈ R
Steffensen x0 ∈ R
Brent a, b ∈ R
RegulaFalsi a, b ∈ R

Expressions are parsed using petitparser, a fasts, stable and well tested grammar parser. These algorithms only work with real numbers. Here's a simple example of how you can find the roots of an equation:

final newton = Newton("2*x+cos(x)", -1, maxSteps: 5);

final steps = newton.maxSteps; // 5
final tol = newton.tolerance; // 1.0e-10
final fx = newton.function; // 2*x+cos(x)
final guess = newton.x0; // -1

final solutions = await newton.solve();

final convergence = solutions.convergence.round(); // 2
final solutions = solutions.efficiency.round(); // 1

/*
 * The getter `solutions.guesses` returns the list of values computed by the algorithm
 *
 * -0.4862880170389824
 * -0.45041860473199363
 * -0.45018362150211116
 * -0.4501836112948736
 * -0.45018361129487355
 */
final List<double> guesses = solutions.guesses;

Note that certain algorithms don't always guarantee t converge to a correct root so read the documentation carefully before choosing the method.

Systems of equations

Use one of the following classes to solve systems of linear equations. Note that only real coefficients are allowed (so double is ok but Complex isn't) and you must define N equations in N variables (so a square matrix is needed). This package supports the following algorithms:

Solver name Iterative method
CholeskySolver ❌
GaussianElimination ❌
GaussSeidelSolver βœ”οΈ
JacobiSolver βœ”οΈ
LUSolver ❌
SORSolver βœ”οΈ

In any case, solvers only work with square matrices so N equations in N variables. These solvers are used to find the x in the Ax = b equation. Methods require at least the equation system matrix A and the vector b containing the unknowns. Iterative methods may require additional parameters such as an initial guess or a particular configuration value.

// Solve a system using LU decomposition
final luSolver = LUSolver(
  equations: const [
    [7, -2, 1],
    [14, -7, -3],
    [-7, 11, 18]
  ],
  constants: const [12, 17, 5]
);

final solutions = luSolver.solve(); // [-1, 4, 3]
final determinant = luSolver.determinant(); // -84.0

If you just want to work with matrices (for operations, LU/Cholesky/QR/SVD decompositions, etc...) you can consider using either RealMatrix (to work with the double data type) or ComplexMatrix (to work with the Complex data type). Both classes are of type Matrix<T> so they have the same public API.

final matrixA = RealMatrix.fromData(
  columns: 2,
  rows: 2,
  data: const [
    [2, 6],
    [-5, 0]
  ]
);

final matrixB = RealMatrix.fromData(
  columns: 2,
  rows: 2,
  data: const [
    [-4, 1],
    [7, -3],
  ]
);

final sum = matrixA + matrixB;
final sub = matrixA - matrixB;
final mul = matrixA * matrixB;
final div = matrixA / matrixB;

final lu = matrixA.luDecomposition();
final cholesky = matrixA.choleskyDecomposition();
final cholesky = matrixA.choleskyDecomposition();
final qr = matrixA.qrDecomposition();
final svd = matrixA.singleValueDecomposition();

final det = matrixA.determinant();
finak rank = matrixA.rank();

You can use toString() to print the content of the matrix but there's also the possibility to use toStringAugmented() which prints the augmented matrix (the matrix + one extra column with the known values vector).

final lu = LUSolver(
  equations: const [
    [7, -2, 1],
    [14, -7, -3],
    [-7, 11, 18]
  ],
  constants: const [12, 17, 5]
);

/*
 * Output with 'toString':
 *
 * [7.0, -2.0, 1.0]
 * [14.0, -7.0, -3.0]
 * [-7.0, 11.0, 18.0]
*/
print("$lu");

/*
 * Output with 'toStringAugmented':
 *
 * [7.0, -2.0, 1.0 | 12.0]
 * [14.0, -7.0, -3.0 | 17.0]
 * [-7.0, 11.0, 18.0 | 5.0]
*/
print("${lu.toStringAugmented()}");

The ComplexMatrix has the same API and the same usage as RealMatrix with the only difference that it works with complex numbers rather then real numbers.

Numerical integration

The numerical integration term refers to a group of algorithms for calculating the numerical value of a definite integral (on a given interval). The function must be continuous within the integration bounds. This package currently supports the following algorithms:

Algorithm type
MidpointRule
SimpsonRule
TrapezoidalRule

Other than the integration bounds (called lowerBound and lowerBound), the classes also have an optional parameter called intervals. It already has a good default value but if you wanted to change the number of parts in which the interval will be split, just make sure to set it!

const simpson = SimpsonRule(
  function: 'sin(x)*e^x',
  lowerBound: 2,
  upperBound: 4,
);

// Calculating the value of...
//
//   ∫ sin(x) * e^x dx
//
// ... between 2 and 4.
final results = simpson.integrate();

// Prints '-7.713'
print('${results.result.toStringAsFixed(3)}');

// Prints '32'
print('${results.guesses.length}');

The integrate() function returns an IntegralResults which is a simple wrapper for 2 values:

  1. result: the actual result, which is the value of the definite integral evaluated within lowerBound and lowerBound,
  2. guesses: the various intermetiate values that brought to the final result.

Interpolation

If you want to perform linear, polynomial or spline interpolation, then you can use the Interpolation types provided by this package. You just need to provide a few points in the constructor and then use compute(double x) to interpolate the value. This package currently supports the following algorithms:

Interpolation type
LinearInterpolation
PolynomialInterpolation
NewtonInterpolation
SplineInterpolation

You'll always find the compute(double x) method in any Interpolation type but some classes may have additional methods that others haven't. For example:

const newton = NewtonInterpolation(
  nodes: [
    InterpolationNode(x: 45, y: 0.7071),
    InterpolationNode(x: 50, y: 0.7660),
    InterpolationNode(x: 55, y: 0.8192),
    InterpolationNode(x: 60, y: 0.8660),
  ],
);

// Prints 0.788
final y = newton.compute(52);
print(y.toStringAsFixed(3));

// Prints the following:
//
// [0.7071, 0.05890000000000006, -0.005700000000000038, -0.0007000000000000339]
// [0.766, 0.053200000000000025, -0.006400000000000072, 0.0]
// [0.8192, 0.04679999999999995, 0.0, 0.0]
// [0.866, 0.0, 0.0, 0.0]
print('\n${newton.forwardDifferenceTable()}');

Since the newtown interpolation algorithm internally builds the "divided differences table", the API exposes two methods (forwardDifferenceTable() and forwardDifferenceTable()) to print those tables. Of course, you won't find forwardDifferenceTable() in other interpolation types because they just don't use it. By default, NewtonInterpolation uses the forward difference method but if you want the backwards one, just pass forwardDifference: false in the constructor.

const polynomial = PolynomialInterpolation(
  nodes: [
    InterpolationNode(x: 0, y: -1),
    InterpolationNode(x: 1, y: 1),
    InterpolationNode(x: 4, y: 1),
  ],
);

// Prints -4.54
final y = polynomial.compute(-1.15);
print(y.toStringAsFixed(2));

// Prints -0.5x^2 + 2.5x + -1
print('\n${polynomial.buildPolynomial()}');

This is another example with a different interpolation strategy. The buildPolynomial() returns the interpolation polynomial (as an Algebraic type) internally used to interpolate x.

Comments
  • Which expressions are available?

    Which expressions are available?

    I am trying to solve a non-linear equation and getting the error: Error: ExpressionParserException: The given expression cannot be parsed! Make sure that all operators are supported. Make also sure that the product of two values explicitly has the '*' symbol.

    How do I find which expressions are available?

    Here's my code:

    final newton = Newton(function: "0.03606737602222409*x*log(exp((3537.428612970299+x)/287.171070460561)+4914469.74557367)-15.455695470480395", x0: 5);
    newton.solve();
    
    enhancement 
    opened by jpiabrantes 5
  • how to solve the equation of the line

    how to solve the equation of the line "Ax + By + C = 0" in this library

    how to solve the equation of the line "y = (-C - Ax) / B" in this library

    var A = 1.3333333333333333;
    var B = -1;
    var C = 0.3333333333333335;
    
    //y = (-C - Ax) / B
    //Y = (-0.3333333333333335 - 1.3333333333333333 * X) / -1
    
    
    bug 
    opened by insinfo 1
  • inverse method bug for big matrix

    inverse method bug for big matrix

    I used the inverse method for the 12*12 matrix and the result was wrong. also, I checked this method with Matlab and Matlab worked correctly. but this method return NaN values I/flutter (31573): [-1.0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, -1.0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, -1.0, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 0.0, NaN, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, -2.0, NaN, NaN] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, -2.0, -0.0] I/flutter (31573): [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, -0.0, -2.0]

    bug 
    opened by geopardaz 1
  • chore: Update to Dart 2.18 and review example apps

    chore: Update to Dart 2.18 and review example apps

    Why?

    We need to:

    1. Update Dart to version 2.18
    2. Update dependencies
    3. Update examples in example/

    What?

    I have upgraded Dart to version 2.18 in the package and the examples

    Types of Changes

    • General improvements (quality updates to improve the stability of the project)

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    documentation enhancement 
    opened by albertodev01 1
  • Make `solve()` function async

    Make `solve()` function async

    In the readme, it seems that solve is async. You have the following example where you await for solve:

    final newton = Newton("2*x+cos(x)", -1, maxSteps: 5);
    final solutions = await newton.solve();
    

    However, solve is not async and does not return a Future.

    enhancement 
    opened by jpiabrantes 1
  • chore: version bump and examples updates

    chore: version bump and examples updates

    Why?

    • Dependencies versions need to be updated
    • The Flutter example needs to be updated to also support Desktop
    • The Dart examples should feature a complete Dart project

    What?

    • I've updated dependencies
    • Created a new Dart project at example/dart_example
    • Updated the Flutter project at example/flutter_example

    Types of Changes

    • Documentation change (change to update documentation)
    • General improvements (quality updates to improve the stability of the project)

    Notes

    With this update, the 1.0.0 version of the Flutter example for mobile, web and desktop builds will be added in the "Release" tab

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
  • chore: Added more linter rules and minor fixes

    chore: Added more linter rules and minor fixes

    Why?

    There should be some more linter rules to add and a few minor fixes for the Flutter and Dart examples

    What?

    I have updated the analysis_options.yaml file and updated the Dart and Flutter examples

    Types of Changes

    • General improvements (quality updates to improve the stability of the project)

    Notes

    n/a

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
  • chore: Updated analysis options and minor example fixes

    chore: Updated analysis options and minor example fixes

    Why?

    There should be some more linter rules to add and a few minor fixes for the Flutter and Dart examples

    What?

    I have updated the analysis_options.yaml file and updated the Dart and Flutter examples

    Types of Changes

    • General improvements (quality updates to improve the stability of the project)

    Notes

    n/a

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
  • feat: Adding integration tests to the Flutter app

    feat: Adding integration tests to the Flutter app

    Why?

    The Flutter example in the example/flutter_example requires some integration testing

    What?

    I have added the integration testing SDK, written integration tests in integration_test and updated CI to support integration tests

    Types of Changes

    • New feature (non-breaking change which adds functionality)

    Notes

    Tests are run on Android, iOS and Web

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
  • chore: Improving the Flutter example app

    chore: Improving the Flutter example app

    Why?

    I need to polish and improve the Flutter example app in example/flutter_example

    What?

    • Added more unit and widget tests
    • Updated some blocs after the 8.0.0 update
    • Reworking the internals of some widgets
    • Improving the responsiveness of certain widgets
    • Improving translations

    Types of Changes

    • General improvements (quality updates to improve the stability of the project)

    Notes

    n/a

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
  • feat: Added Riddler's method and updated Flutter example

    feat: Added Riddler's method and updated Flutter example

    Why?

    I need to update the Flutter example in example/flutter_example and add a new root finding algorithm to the core library

    What?

    I've added the new Riddler type and updated the Flutter example.

    Types of Changes

    • New feature (non-breaking change which adds functionality)
    • General improvements (quality updates to improve the stability of the project)

    Notes

    The Flutter example app is being worked on the flutter_example branch!

    Checklist

    • [x] I have provided a description of the proposed changes.
    • [x] I added unit tests for all relevant code.
    • [x] I added golden tests for all UI changes.
    • [x] I added documentation for all relevant code.
    opened by albertodev01 1
Owner
Alberto
Dart, Java and Delphi. Author of the book "Flutter Complete Reference"
Alberto
DoubtBin is a doubt solving and resources sharing portfolio.

DoubtBin is a doubt solving and resources sharing portfolio. It includes implemenation of concepts of Object Oriented Programming, Front-end Development and Back-end Development.

null 17 Oct 14, 2022
🎞 Flutter media playback, broadcast & recording library for Windows, Linux & macOS. Written in C++ using libVLC & libVLC++. (Both audio & video)

dart_vlc Flutter media playback, broadcast, recording & chromecast library for Windows, Linux & macOS. Written in C++ using libVLC & libVLC++. Install

Hitesh Kumar Saini 417 Dec 29, 2022
Demo library index written in Flutter

demo_books A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Nemanja Stoőić 1 Sep 20, 2022
A Dart library for creating a Dart object to represent directory trees.

Directory Tree A Dart library for creating a Dart object to represent directory trees. Getting Started Import and initialize package import 'package:d

Chiziaruhoma Ogbonda 5 Dec 1, 2021
This is a fully fledged Sudoku game written in Dart using Flutter.

This is a fully fledged Sudoku game written in Dart using Flutter.

Varun Shanbhag 56 Dec 29, 2022
A Dart FFI package to send πŸ’¬ toasts on Windows. Written in C++, based on WinToast.

desktoasts A Dart package to send native ?? toasts on Windows. Installation For Flutter dependencies: ... desktoasts: ^0.0.2 For Dart CLI here Sup

Hitesh Kumar Saini 37 Mar 7, 2022
Simple URL-shorter written in Dart with Notion as a database

Tuda A simple URL-shorter service written in pure Dart. With Notion as a database, it provides the simplest interface for setting your links. Environm

Parabola 8 Aug 19, 2022
Elpee is a multiplatform app written entirely in Dart/Flutter.

elpee - Explore and share music About Elpee is a multiplatform app written entirely in Dart/Flutter. The data is retrieved from the Spotify Web API an

Tom van Lieshout 86 Oct 25, 2022
The Fuse Wallet is a cross platform Ethereum wallet written in Dart and built on Flutter.

Fuse Wallet The Fuse Wallet is a cross platform Ethereum wallet written in Dart and built on Flutter. It's runninng on the Fuse network, but can be pl

null 4 Nov 9, 2022
Figma API client written in pure Dart

figma A library for interacting with Figma APIs. Created from templates made available by Stagehand under a BSD-style license. Usage A simple usage ex

Arne Molland 14 Oct 19, 2022
A modern voice chat application. The project has been written solely in Dart Language.

VChat A modern voice chat application. The application welcomes the user with the login screen. After a simple registration process, an empty "Home" s

Bulent Baris Kilic 6 Aug 18, 2022
Test telegram bot written in dart using teledart package

Dart-Tg-Bot Test telegram bot written in dart using teledart package. Run on local machine First install dart Clone the bot's source and install depen

I'm Not A Bot #Left_TG 8 Oct 31, 2022
Naj - An open-source code generation and file management system written in Dart

Naj An open-source code generation and file management system written in Dart Ov

Najibullah Khoda Rahim 1 Feb 14, 2022
A type-safe command-line parsing library for Dart

plade Plade is a type-safe CLI parsing library for Dart. Highlights Fully type-safe and null-safe. Support for a variety of different parsing styles (

Ryan Gonzalez 6 Jan 1, 2022
Agent library for Internet Computer, in Dart

agent_dart An agent library built for Internet Computer, a plugin package for dart and flutter apps. Developers can build ones to interact with Dfinit

null 87 Dec 31, 2022
ThingsBoard PE API client library for Dart developers.

ThingsBoard PE API client library for Dart developers. It's compatible with TB PE 3.3.0. Usage A simple usage example: import 'package:thingsboard_pe_

ThingsBoard - Open-source IoT Platform 45 Sep 28, 2022
A Dart library with a solution to use smart enums

Smart Enums Author: Jop Middelkamp A package that can help you create 'smarter' enums that could be extended with some domain logic. Usage final bestF

Jop Middelkamp 2 Oct 1, 2022
A mock library for Dart inspired by mockito

?? mocktail This repository contains mocking libraries for Dart inspired by mockito. package:mocktail A Dart mocking library which simplifies mocking

Felix Angelov 430 Dec 25, 2022
Bosun is a library for building organized command line applications in Dart.

Bosun A library for parsing CLI input and structuring CLI commands Features Structure CLI commands in a nice, uniform fashion. Parse args and flag inf

null 3 Oct 13, 2022