Sphinx-based documentation added

This commit is contained in:
Mateusz Pusz
2020-03-09 18:55:41 +01:00
parent af6358fbdb
commit b65328d575
88 changed files with 6476 additions and 154 deletions
+22
View File
@@ -0,0 +1,22 @@
.. namespace:: units
Basic Concepts
==============
The most important concepts in the library are `Unit`, `Dimension`, and
`Quantity`:
.. image:: /_static/img/design.png
:align: center
`Unit` is a basic building block of the library. Every dimension works with
a concrete hierarchy of units. Such hierarchy defines a reference unit and
often a few scaled versions of it.
`Dimension` concept matches a dimension of either a base or derived quantity.
`base_dimension` is instantiated with a unique symbol identifier and a base
unit. `derived_dimension` is a list of exponents of either base or other
derived dimensions.
`Quantity` is a concrete amount of a unit for a specified dimension with a
specific representation.
@@ -0,0 +1,16 @@
.. namespace:: units
Conversions and Casting
=======================
Implicit Conversions
--------------------
constructors
Explicit Casting
----------------
quantity_cast
Example of casting to a dimension's coherent unit.
+178
View File
@@ -0,0 +1,178 @@
.. namespace:: units
Dimensions
==========
In the previous chapter we briefly introduced the notion of a physical
:term:`dimension`. Now it is time to learn much more about this subject.
Length, time, velocity, area, energy are only a few examples of physical
dimensions.
Operations
----------
Quantities of the same dimension can be easily added or subtracted with
each other and the result will always be a quantity of the same dimension:
.. code-block::
:emphasize-lines: 3-4
Length auto dist1 = 2q_m;
Length auto dist2 = 1q_m;
Length auto res1 = dist1 + dist2;
Length auto res2 = dist1 - dist2;
Additionally, we can always multiply or divide a quantity by a
:term:`scalar` and in such a case the quantity's dimension will also
not change:
.. code-block::
:emphasize-lines: 2-4
Length auto dist = 2q_m;
Length auto res1 = dist * 2; // 4 m
Length auto res2 = 3 * res1; // 12 m
Length auto res3 = res2 / 2; // 6 m
However, if we try to multiply or divide quantities of the same or
different dimensions, or we will divide a scalar by a quantity, we most
probably will always end up in a quantity of a yet another dimension:
.. code-block::
:emphasize-lines: 4-6
Length auto dist1 = 2q_m;
Length auto dist2 = 3q_m;
Time auto dur1 = 2q_s;
Area auto res1 = dist1 * dist2; // 6 m²
Velocity auto res2 = dist1 / dur1; // 1 m/s
Frequency auto res3 = 10 / dur1; // 5 Hz
However, please note that there is an exception from the above rule.
In case we divide the same dimensions, or multiply by the inverted
dimension, than we will end up with just a scalar type:
.. code-block::
:emphasize-lines: 4-5
Time auto dur1 = 10q_s;
Time auto dur2 = 2q_s;
Frequency auto fr1 = 5q_Hz;
Scalar auto v1 = dur1 / dur2; // 5
Scalar auto v2 = dur1 * fr1; // 50
Base dimensions
---------------
The quantities of base dimensions are called
:term:`base quantities <base quantity>` which are the atomic building blocks
of a :term:`system of quantities`. For example the The International System
of Units (:term:`SI`) defines 7 of them: length, mass, time, electric
current, thermodynamic temperature, substance, and luminous intensity.
To define a new base dimension the `base_dimension` class template is
provided. For example the SI base dimension of length can be defined as::
namespace si {
struct dim_length : base_dimension<"L", metre> {};
}
In the above code sample ``"L"`` is an base dimension's unique identifier
and `si::metre` is a :term:`base unit` of this base dimension. We can
obtain those back easily with::
static_assert(si::dim_length::symbol == "L");
static_assert(std::is_same_v<si::dim_length::base_unit, si::metre>);
Derived dimensions
------------------
The quantities of derived dimensions are called
:term:`derived quantities <derived quantity>` and are derived from base
quantities. This means that they are created by multiplying or dividing
quantities of other dimensions.
Looking at the previous code snippets the area, velocity, or frequency are
the examples of such quantities. Each derived quantity can be represented
as a unique list of exponents of base quantities. For example:
- an area is a length base quantity raised to the exponent ``2``
- a velocity is formed from the length base quantity with exponent ``1``
and time base quantity with exponent ``-1``.
The above dimensions can be defined in the library with the
`derived_dimension` class template as follows::
namespace si {
struct dim_area : derived_dimension<dim_area, square_metre,
exp<dim_length, 2>> {};
struct dim_velocity : derived_dimension<dim_velocity, metre_per_second,
exp<dim_length, 1>, exp<dim_time, -1>> {};
}
In the above code sample `si::square_metre` and `si::metre_per_second`
are the :term:`coherent derived units <coherent derived unit>` of those
derived dimensions.
Coherent unit argument is followed by the list of exponents that form this
derived dimension. This list is called a :term:`recipe` of this derived
dimension and may contain both base and derived dimensions. In the latter
case the dimension is being extracted to base dimensions by the framework
itself. The order and types of dimensions used in the recipe determine how
an dimension's unnamed unit symbol is being printed in the text output.
.. seealso::
More information on how the :term:`recipe` affect the printed symbol
of unnamed unit can be found in the :ref:`Derived Unnamed Units` chapter.
It is important to mention here that beside text output the order and
the number of elements in the `derived_dimension` definition does not
matter. Even if we define the above as:
.. code-block::
:emphasize-lines: 4, 6
namespace si {
struct dim_area : derived_dimension<dim_area, square_metre,
exp<dim_length, 1>, exp<dim_length, 1>> {};
struct dim_velocity : derived_dimension<dim_velocity, metre_per_second,
exp<dim_time, -1>, exp<dim_length, 1>> {};
}
the library will do its magic and will end up with the same
:term:`normalized derived dimension` which will allow the dimensional
analysis in the library to work as expected.
.. note::
The first template argument of `derived_dimension` is the type of the
child class inherited from the instantiation of this `derived_dimension`
class template. This is called a
:abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used
in many places in this library to provide :ref:`The Downcasting Facility`.
Hopefully if [P0847]_ will land in C++23 the additional CRTP-related
template parameter will be removed from this definition.
Obtaining a Unit of the Dimension
---------------------------------
In order to obtain the base/coherent unit of any dimension type a
`dimension_unit` helper was introduced::
static_assert(std::is_same_v<dimension_unit<si::dim_length>, si::metre>);
static_assert(std::is_same_v<dimension_unit<si::dim_velocity>, si::metre_per_second>);
.. rubric:: Citations:
.. [P0847] `"Deducing this" <https://wg21.link/P0847>`_, Programming Language C++ proposal
+159
View File
@@ -0,0 +1,159 @@
.. namespace:: units
Quantities
==========
A :term:`quantity` is a concrete amount of a unit for a specified dimension
with a specific representation and is represented in the library with a
`quantity` class template.
Construction
------------
To create the quantity object from a :term:`scalar` we just have to pass
the value to the `quantity` class template explicit constructor::
quantity<si::dim_length, si::kilometre, double> d(123);
.. note::
As the constructor is explicit, the quantity object can be created from
an "unsafe" fundamental type only via
`direct initialization <https://en.cppreference.com/w/cpp/language/direct_initialization>`_.
This is why the code below using
`copy initialization <https://en.cppreference.com/w/cpp/language/copy_initialization>`_
**does not compile**::
quantity<si::dim_length, si::kilometre, double> d = 123; // ERROR
To simplify `quantity` objects creation the library provides helper aliases for
quantities of each :term:`dimension` which additionally set the representation
type to ``double`` by default::
namespace si {
template<Unit U, Scalar Rep = double>
using length = quantity<dim_length, U, Rep>;
}
Thanks to that, the above example can be rewritten as follows::
si::length<si::kilometre> d(123);
To further simplify construction of quantities with compile-time known
values the library provides :abbr:`UDL (User Defined Literal)` s for each
:term:`unit` of every :term:`dimension`. Thanks to them the same code can
be as simple as::
using namespace si::literals;
constexpr auto d1 = 123q_km; // si::length<si::kilometre, std::int64_t>
constexpr auto d2 = 123.q_km; // si::length<si::kilometre, long double>
``123q_km`` should be read as a quantity of length in kilometers. Initially the
library did not use the ``q_`` prefix for UDLs but it turned out that there are
a few unit symbols that collide with literals already existing in C and C++
language (i.e. ``F`` (farad), ``J`` (joule), ``W`` (watt), ``K`` (kelvin),
``d`` (day), ``l`` or ``L`` (litre), ``erg``, ``ergps``). This is why the
``q_`` prefix was consistently applied to all the UDLs.
Dimension-specific concepts
---------------------------
In case the user does not care about the specific unit and representation but
requires quantity of a concrete dimension than dimension-specific concepts can
be used::
using namespace si::literals;
constexpr Length auto d = 123q_km; // si::length<si::kilometre, std::int64_t>
.. note::
All instances of `quantity` class always match the `Quantity` concept.
All other regular types that are not quantities are called
:term:`scalars <scalar>` by the library and match the `Scalar` concept.
However, the above is not the most important usage of those concepts. Let's
assume that the user wants to implement an ``avg_speed`` function that will
be calculating the average speed based on provided distance and duration
quantities. The usage of such a function can look as follows::
using namespace si::literals;
using namespace international::literals;
constexpr Velocity auto v1 = avg_speed(220q_km, 2q_h);
constexpr Velocity auto v2 = avg_speed(140q_mi, 2q_h);
In this and all other physical units libraries such a function can be
implemented as::
constexpr si::velocity<si::metre_per_second> avg_speed(si::length<si::metre> d,
si::time<si::second> t)
{
return d / t;
}
While being correct, this function performs unnecessary intermediate
conversions (from kilometers to meters, from hours to seconds,
and from meters per second to kilometers per hour) which can affect
runtime performance and the precision of the final result. To eliminate
all that overhead we have to write a template function::
template<typename U1, typename R1, typename U2, typename R2>
constexpr auto avg_speed(si::length<U1, R1> d, si::time<U2, R2> t)
{
return d / t;
}
This function will work for every SI unit and representation without any
unnecessary overhead. It is also simple enough to prove its implementation
being correct just by a simple inspection. However, it might not always be
the case. For more complicated calculations we would like to ensure that we
are returning a physical quantity of a correct dimension. For this
dimension-specific concepts come handy again and with usage of C++20 generic
functions our function can look as simple as::
constexpr Velocity auto avg_speed(Length auto d, Time auto t)
{
return d / t;
}
Now we are sure that the dimension of returned quantity is correct. Also
please note that with the above code we implemented a truly generic function
that works efficiently not only with SI units but also with other systems of
units like CGS.
.. seealso::
Please refer to :ref:`avg_speed` example for more information on different
kinds of interfaces supported by the library.
Working with constrained deduced quantity types
-----------------------------------------------
It is important to note that when we assign a result from the function to an
automatically deduced type, even if it is constrained by a dimension-specific
concept, we still do not know what is the exact unit and representation type
of such a quantity. In many cases it might be exactly what we want to get,
but often we would like to know a specific type too. We have two options here:
- query the actual dimension, unit, and representation types::
constexpr Velocity auto v = avg_speed(220q_km, 2q_h);
using quantity_type = decltype(v);
using dimension_type = quantity_type::dimension;
using unit_type = quantity_type::unit;
using rep_type = quantity_type::rep;
- convert or cast to a desired quantity type::
constexpr Velocity auto v1 = avg_speed(220.q_km, 2q_h);
constexpr si::velocity<si::metre_per_second> v2 = v1;
constexpr Velocity auto v3 = quantity_cast<si::velocity<si::metre_per_second>(v1);
.. seealso::
More information on this subject can be found in :ref:`Conversions and Casting`
chapter.
+45
View File
@@ -0,0 +1,45 @@
.. namespace:: units
Text output
===========
Beside providing dimensional analysis and units conversions, the library
also tries really hard to print any quantity in the most user friendly way.
Output streams
--------------
The easiest way to print a quantity is to provide its object to the output
stream::
using namespace si::literals;
using namespace international::literals;
constexpr Velocity auto v1 = avg_speed(220.q_km, 2q_h);
constexpr Velocity auto v2 = avg_speed(140.q_mi, 2q_h);
std::cout << v1 << '\n'; // 110 km/h
std::cout << v2 << '\n'; // 70 mi/h
The text output will always print the :term:`value of a quantity` followed
by the symbol of a :term:`unit` associated with this quantity. We will learn
more about units in the :ref:`Units` chapter, but for now, it is important
to remember that it is a good practice to always `quantity_cast()` a quantity
of an unknown ``auto`` type before passing it to the text output::
std::cout << quantity_cast<si::kilometre_per_hour>(v1) << '\n'; // 110 km/h
std::cout << quantity_cast<si::metre_per_second>(v1) << '\n'; // 30.5556 m/s
Formatting the output
---------------------
Grammar:
.. productionlist::
units-format-spec: fill-and-align[opt] sign[opt] width[opt] precision[opt] units-specs[opt]
units-specs: conversion-spec
: units-specs conversion-spec
: units-specs literal-char
literal-char: any character other than '{' or '}'
conversion-spec: '%' modifier[opt] type
modifier: one of 'E', 'O'
type: one of 'n', 'q', 'Q', 't', '%'
+319
View File
@@ -0,0 +1,319 @@
.. namespace:: units
Units
=====
Each quantity has a magnitude (a numerical value). In order to be able to
compare quantities of the same dimension a notion of a :term:`measurement unit`
was introduced. Units are designated by conventionally assigned names and
symbols. Thanks to them it is possible to compare two quantities of the
same dimension and express the ratio of the second quantity to the first
one as a number. For example ``10s`` is ``10`` times more than ``1s``.
Base quantities are expressed in terms of :term:`base units <base unit>`
(i.e. ``m`` (meter), ``s`` (second)), while derived quantities are expressed
in terms of :term:`derived units <derived unit>`.
Base Units
----------
:term:`Base units <base unit>` are the units of
:term:`base quantities <base quantity>` defined for
:term:`base dimensions <base dimension>`. For example in :term:`SI`
``m`` (meter) is a base unit of length, ``s`` (second) is a base unit of
time. In each :term:`coherent system of units`, there is only one base
unit for each base quantity. This is why a base unit type is required by
the `base_dimension` definition in this library. For example `si::dim_length`
can be defined in the following way::
namespace si {
struct dim_length : base_dimension<"L", metre> {};
}
where `si::metre` is defined as::
namespace si {
struct metre : named_unit<metre, "m", prefix> {};
}
In the above definition ``"m"`` is the unit symbol to be used in the text
output, and ``si::prefix`` specifies that the library should allow
definitions of prefixed units using `si::metre` as a reference (i.e.
`si::centimetre`).
.. seealso::
For more information on prefixes and scaling please refer to
`Scaled Units`_.
.. note::
The first template argument of `named_unit` is the type of the
child class inherited from the instantiation of this `named_unit`
class template. This is called a
:abbr:`CRTP (Curiously Recurring Template Parameter)` Idiom and is used
in many places in this library to provide :ref:`The Downcasting Facility`.
Hopefully if [P0847]_ will land in C++23 the additional CRTP-related
template parameter will be removed from this definition.
It is important to notice here that :term:`SI` is not the only system used
in the industry and the library has to support other systems too. Also
in some cases conversions between such systems should be allowed. The
fact that the `base_dimension` takes the base unit in its definition makes
it really easy to define other systems of units. For example length in the
`CGS <https://en.wikipedia.org/wiki/Centimetre%E2%80%93gram%E2%80%93second_system_of_units>`_
could be defined as::
namespace cgs {
struct dim_length : base_dimension<"L", si::centimetre> {};
}
The fact that both base dimensions use the same identifier ``"L"`` tells
the library that bot definitions refer to the same physical dimension of
length. The only difference is the measurement unit used to define their
base dimensions. Thanks to using `si::centimetre` in the `cgs::dim_length`
definition we also enabled the ability to easily convert between those
2 base dimensions (as the library knows how to convert `si::metre` to
`si::centimetre` and vice versa).
Derived Units
-------------
Derived units can be either named or unnamed.
Derived Named Units
^^^^^^^^^^^^^^^^^^^
Derived named units have a unique symbol (i.e. ``N`` (newton) or ``Pa``
(pascal)) and they are defined in the same way as base units (which
always have to be a named unit)::
namespace si {
struct newton : named_unit<newton, "N", prefix> {};
}
Derived Unnamed Units
^^^^^^^^^^^^^^^^^^^^^
Derived unnamed units are the units where the symbol is derived from the
base quantities symbols and the expression of the dependence of the derived
quantity on the base quantities (i.e. ``m/s`` (metre per second), ````
(square metre)). To support such use cases a library introduced a notion of
:term:`derived dimension recipe` which stores the information about the
order, exponents, and types of dimensions used to defined this particular
derived dimension. For example each of the below ``momentum`` definitions
will result in a different unnamed unit symbol:
.. code-block::
:emphasize-lines: 2-4, 6-8, 10-12
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
exp<si::dim_mass, 1>,
exp<si::dim_length, 1>,
exp<si::dim_time, -1>> {}; // kg⋅m/s
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
exp<si::dim_length, 1>,
exp<si::dim_mass, 1>,
exp<si::dim_time, -1>> {}; // m⋅kg/s
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
exp<si::dim_time, -1>,
exp<si::dim_length, 1>,
exp<si::dim_mass, 1>> {}; // 1/s⋅m⋅kg
where ``kilogram_metre_per_second`` is defined as::
struct kilogram_metre_per_second : unit<kilogram_metre_per_second> {};
However, the easiest way to define momentum is just to use the
`si::velocity` derived dimension in the recipe:
.. code-block::
:emphasize-lines: 3
struct dim_momentum : derived_dimension<dim_momentum, kilogram_metre_per_second,
exp<si::dim_mass, 1>,
exp<si::dim_velocity, 1>> {}; // kg⋅m/s
In such a case the library will do its magic and will automatically
unpack a provided derived dimension to its base dimensions in order to
end up with a :term:`normalized derived dimension` for a parent entity.
The need to support a derived dimension in the recipe is not just a
syntactic sugar that allows us to do less typing. It is worth to notice
here that some of the derived unnamed units are defined in terms of other
derived named units (i.e. surface tension quantity is measured in terms
of ``N/m``):
.. code-block::
:emphasize-lines: 2
struct dim_surface_tension : derived_dimension<dim_surface_tension, newton_per_metre,
exp<si::dim_force, 1>,
exp<si::dim_length, -1>> {}; // N/m
If we defined the above in terms of base units we would end up with
a ``kg/s²`` derived unit symbol.
Scaled Units
------------
Until now we talked mostly about
:term:`coherent units <coherent derived unit>` which are units used to
define dimensions and thus, in their system of units, have proportionality
factor/ratio equals one. However quantities of each dimension can also use
other units of measurement to describe their magnitude (numerical value).
Scaled Units
^^^^^^^^^^^^
We are used to use minutes, hours, or days to measure quantities of time.
Those units are the scaled versions of a time dimension's base unit,
namely second. Those can be defined easily in the library using
`named_scaled_unit` class template::
struct minute : named_scaled_unit<minute, "min", no_prefix, ratio<60>, second> {};
struct hour : named_scaled_unit<hour, "h", no_prefix, ratio<60>, minute> {};
struct day : named_scaled_unit<hour, "d", no_prefix, ratio<24>, hour> {};
where `no_prefix` is a special tag type describing that the library should
not allow to define a new prefixed unit that would use this unit as a
reference ("kilohours" does not have much sense, right?). The `ratio` type
used in the definition is really similar to ``std::ratio`` but it takes
the third additional argument that defines the exponent of the ratio.
Thanks to it we can address nearly infinite scaling factors between units
and define units like:
.. code-block::
:force:
struct electronvolt : named_scaled_unit<electronvolt, "eV", prefix,
ratio<1'602'176'634, 1'000'000'000, -19>, joule> {};
..
TODO Submit a bug for above lexing problem
Finally, the last of the `named_scaled_unit` class template parameters
provide a reference unit for scaling. Please note that it can be a dimension's
base/coherent unit (like `si::second`) or any other unit (i.e. `si::minute`,
`si::hour`) that is a scaled version of the dimension's base/coherent unit.
Prefixed Unit
^^^^^^^^^^^^^
Prefixed units are just scaled units with a standardized ratio. For example
:term:`SI` defines prefixes based on the exponent of ``10``. Here is a
complete list of all the :term:`SI` prefixes supported by the library::
namespace si {
struct prefix : prefix_type {};
struct yocto : units::prefix<yocto, prefix, "y", ratio<1, 1, -24>> {};
struct zepto : units::prefix<zepto, prefix, "z", ratio<1, 1, -21>> {};
struct atto : units::prefix<atto, prefix, "a", ratio<1, 1, -18>> {};
struct femto : units::prefix<femto, prefix, "f", ratio<1, 1, -15>> {};
struct pico : units::prefix<pico, prefix, "p", ratio<1, 1, -12>> {};
struct nano : units::prefix<nano, prefix, "n", ratio<1, 1, -9>> {};
struct micro : units::prefix<micro, prefix, "µ", ratio<1, 1, -6>> {};
struct milli : units::prefix<milli, prefix, "m", ratio<1, 1, -3>> {};
struct centi : units::prefix<centi, prefix, "c", ratio<1, 1, -2>> {};
struct deci : units::prefix<deci, prefix, "d", ratio<1, 1, -1>> {};
struct deca : units::prefix<deca, prefix, "da", ratio<1, 1, 1>> {};
struct hecto : units::prefix<hecto, prefix, "h", ratio<1, 1, 2>> {};
struct kilo : units::prefix<kilo, prefix, "k", ratio<1, 1, 3>> {};
struct mega : units::prefix<mega, prefix, "M", ratio<1, 1, 6>> {};
struct giga : units::prefix<giga, prefix, "G", ratio<1, 1, 9>> {};
struct tera : units::prefix<tera, prefix, "T", ratio<1, 1, 12>> {};
struct peta : units::prefix<peta, prefix, "P", ratio<1, 1, 15>> {};
struct exa : units::prefix<exa, prefix, "E", ratio<1, 1, 18>> {};
struct zetta : units::prefix<zetta, prefix, "Z", ratio<1, 1, 21>> {};
struct yotta : units::prefix<yotta, prefix, "Y", ratio<1, 1, 24>> {};
}
Alternative hierarchy of prefixes is the one used in data information
domain:
.. code-block::
:force:
namespace data {
struct prefix : prefix_type {};
struct kibi : units::prefix<kibi, prefix, "Ki", ratio< 1'024>> {};
struct mebi : units::prefix<mebi, prefix, "Mi", ratio< 1'048'576>> {};
struct gibi : units::prefix<gibi, prefix, "Gi", ratio< 1'073'741'824>> {};
struct tebi : units::prefix<tebi, prefix, "Ti", ratio< 1'099'511'627'776>> {};
struct pebi : units::prefix<pebi, prefix, "Pi", ratio< 1'125'899'906'842'624>> {};
struct exbi : units::prefix<exbi, prefix, "Ei", ratio<1'152'921'504'606'846'976>> {};
}
With the definitions like above we can easily define prefixed unit. For
example we can define `si::kilometre` as::
namespace si {
struct kilometre : prefixed_unit<kilometre, kilo, metre> {};
}
.. important::
Prefixed units have to use named units as a reference. For unnamed
units we could end up with some strange, misleading, and sometimes
wrong definitions ("kilo square metre" seams strange and spelled
as ``km²`` would be invalid).
Deduced Units
^^^^^^^^^^^^^
For some units determining of a correct scaling ratio may not be trivial,
and even if done correctly, may be a pain to maintain. For a simple example
let's take a "kilometre per hour" unit. What is the easiest to maintain
ratio in reference to "metre per second":
- ``1000/3600``
- ``10/36``
- ``5/18``
Whichever, we choose there will always be someone not happy with our choice.
Thanks to a `deduced_unit` class template provided by the library this problem
does not exist at all. With it `si::kilometre_per_hour` can be defined as::
namespace si {
struct kilometre_per_hour : deduced_unit<kilometre_per_hour, dim_velocity, kilometre, hour> {};
}
Please note that this is the only unit-related class template that takes
a dimension as its parameter. This derived dimension provides a :term:`recipe`
used for its definition. Based on the information stored in the recipe
(order, type, and exponents of composite dimensions) and the ratios of units
provided in the template parameter list after the derived dimension parameter,
the library calculates the final ratio for this unit.
.. rubric:: Citations:
.. [P0847] `"Deducing this" <https://wg21.link/P0847>`_, Programming Language C++ proposal