1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
//! Molar concentration (base unit mole per cubic meter, mol · m⁻³).

quantity! {
    /// Molar concentration (base unit mole per cubic meter, mol · m⁻³).
    quantity: MolarConcentration; "molar concentration";
    /// Dimension of molar concentration, L⁻³N (base unit mole per cubic meter, mol · m⁻³).
    dimension: ISQ<
        N3,     // length
        Z0,     // mass
        Z0,     // time
        Z0,     // electric current
        Z0,     // thermodynamic temperature
        P1,     // amount of substance
        Z0>;    // luminous intensity
    kind: dyn (crate::si::marker::ConstituentConcentrationKind);
    units {
        @yottamole_per_cubic_meter: prefix!(yotta); "Ymol/m³",
            "yottamole per cubic meter", "yottamoles per cubic meter";
        @zettamole_per_cubic_meter: prefix!(zetta); "Zmol/m³",
            "zettamole per cubic meter", "zettamoles per cubic meter";
        @examole_per_cubic_meter: prefix!(exa); "Emol/m³",
            "examole per cubic meter", "examoles per cubic meter";
        @petamole_per_cubic_meter: prefix!(peta); "Pmol/m³",
            "petamole per cubic meter", "petamoles per cubic meter";
        @teramole_per_cubic_meter: prefix!(tera); "Tmol/m³",
            "teramole per cubic meter", "teramoles per cubic meter";
        @gigamole_per_cubic_meter: prefix!(giga); "Gmol/m³",
            "gigamole per cubic meter", "gigamoles per cubic meter";
        @megamole_per_cubic_meter: prefix!(mega); "Mmol/m³",
            "megamole per cubic meter", "megamoles per cubic meter";
        @kilomole_per_cubic_meter: prefix!(kilo); "kmol/m³",
            "kilomole per cubic meter", "kilomoles per cubic meter";
        @hectomole_per_cubic_meter: prefix!(hecto); "hmol/m³",
            "hectomole per cubic meter", "hectomoles per cubic meter";
        @decamole_per_cubic_meter: prefix!(deca); "damol/m³",
            "decamole per cubic meter", "decamoles per cubic meter";
        @mole_per_cubic_meter: prefix!(none); "mol/m³",
            "mole per cubic meter", "moles per cubic meter";
        @decimole_per_cubic_meter: prefix!(deci); "dmol/m³",
            "decimole per cubic meter", "decimoles per cubic meter";
        @centimole_per_cubic_meter: prefix!(centi); "cmol/m³",
            "centimole per cubic meter", "centimoles per cubic meter";
        @millimole_per_cubic_meter: prefix!(milli); "mmol/m³",
            "millimole per cubic meter", "millimoles per cubic meter";
        @micromole_per_cubic_meter: prefix!(micro); "µmol/m³",
            "micromole per cubic meter", "micromoles per cubic meter";
        @nanomole_per_cubic_meter: prefix!(nano); "nmol/m³",
            "nanomole per cubic meter", "nanomoles per cubic meter";
        @picomole_per_cubic_meter: prefix!(pico); "pmol/m³",
            "picomole per cubic meter", "picomoles per cubic meter";
        @femtomole_per_cubic_meter: prefix!(femto); "fmol/m³",
            "femtomole per cubic meter", "femtomoles per cubic meter";
        @attomole_per_cubic_meter: prefix!(atto); "amol/m³",
            "attomole per cubic meter", "attomoles per cubic meter";
        @zeptomole_per_cubic_meter: prefix!(zepto); "zmol/m³",
            "zeptomole per cubic meter", "zeptomoles per cubic meter";
        @yoctomole_per_cubic_meter: prefix!(yocto); "ymol/m³",
            "yoctomole per cubic meter", "yoctomoles per cubic meter";

        @kilomole_per_cubic_decimeter:
            prefix!(kilo) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "kmol/dm³", "kilomole per cubic decimeter", "kilomoles per cubic decimeter";
        @kilomole_per_liter:
            prefix!(kilo) / prefix!(milli);
            "kmol/L", "kilomole per liter", "kilomoles per liter";
        @mole_per_cubic_decimeter:
            prefix!(none) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "mol/dm³", "mole per cubic decimeter", "moles per cubic decimeter";
        @mole_per_liter:
            prefix!(none) / prefix!(milli);
            "mol/L", "mole per liter", "moles per liter";
        @millimole_per_cubic_decimeter:
            prefix!(milli) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "mmol/dm³", "millimole per cubic decimeter", "millimoles per cubic decimeter";
        @millimole_per_liter:
            prefix!(milli) / prefix!(milli);
            "mmol/L", "millimole per liter", "millimoles per liter";
        @micromole_per_cubic_decimeter:
            prefix!(micro) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "μmol/dm³", "micromole per cubic decimeter", "micromoles per cubic decimeter";
        @micromole_per_liter:
            prefix!(micro) / prefix!(milli);
            "μmol/L", "micromole per liter", "micromoles per liter";
        @nanomole_per_cubic_decimeter:
            prefix!(nano) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "nmol/dm³", "nanomole per cubic decimeter", "nanomoles per cubic decimeter";
        @nanomole_per_liter:
            prefix!(nano) / prefix!(milli);
            "nmol/L", "nanomole per liter", "nanomoles per liter";
        @picomole_per_cubic_decimeter:
            prefix!(pico) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "pmol/dm³", "picomole per cubic decimeter", "picomoles per cubic decimeter";
        @picomole_per_liter:
            prefix!(pico) / prefix!(milli);
            "pmol/L", "picomole per liter", "picomoles per liter";
        @femtomole_per_cubic_decimeter:
            prefix!(femto) / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "fmol/dm³", "femtomole per cubic decimeter", "femtomoles per cubic decimeter";
        @femtomole_per_liter:
            prefix!(femto) / prefix!(milli);
            "fmol/L", "femtomole per liter", "femtomoles per liter";

        @kilomole_per_deciliter:
            prefix!(kilo) / prefix!(deci) / prefix!(milli);
            "kmol/dL", "kilomole per deciliter", "kilomoles per deciliter";
        @mole_per_deciliter:
            prefix!(none) / prefix!(deci) / prefix!(milli);
            "mol/dL", "mole per deciliter", "moles per deciliter";
        @millimole_per_deciliter:
            prefix!(milli) / prefix!(deci) / prefix!(milli);
            "mmol/dL", "millimole per deciliter", "millimoles per deciliter";
        @micromole_per_deciliter:
            prefix!(micro) / prefix!(deci) / prefix!(milli);
            "μmol/dL", "micromole per deciliter", "micromoles per deciliter";
        @nanomole_per_deciliter:
            prefix!(nano) / prefix!(deci) / prefix!(milli);
            "nmol/dL", "nanomole per deciliter", "nanomoles per deciliter";
        @picomole_per_deciliter:
            prefix!(pico) / prefix!(deci) / prefix!(milli);
            "pmol/dL", "picomole per deciliter", "picomoles per deciliter";
        @femtomole_per_deciliter:
            prefix!(femto) / prefix!(deci) / prefix!(milli);
            "fmol/dL", "femtomole per deciliter", "femtomoles per deciliter";

        @kilomole_per_milliliter:
            prefix!(kilo) / prefix!(milli) / prefix!(milli);
            "kmol/mL", "kilomole per milliliter", "kilomoles per milliliter";
        @mole_per_milliliter:
            prefix!(none) / prefix!(milli) / prefix!(milli);
            "mol/mL", "mole per milliliter", "moles per milliliter";
        @millimole_per_milliliter:
            prefix!(milli) / prefix!(milli) / prefix!(milli);
            "mmol/mL", "millimole per milliliter", "millimoles per milliliter";
        @micromole_per_milliliter:
            prefix!(micro) / prefix!(milli) / prefix!(milli);
            "μmol/mL", "micromole per milliliter", "micromoles per milliliter";
        @nanomole_per_milliliter:
            prefix!(nano) / prefix!(milli) / prefix!(milli);
            "nmol/mL", "nanomole per milliliter", "nanomoles per milliliter";
        @picomole_per_milliliter:
            prefix!(pico) / prefix!(milli) / prefix!(milli);
            "pmol/mL", "picomole per milliliter", "picomoles per milliliter";
        @femtomole_per_milliliter:
            prefix!(femto) / prefix!(milli) / prefix!(milli);
            "fmol/mL", "femtomole per milliliter", "femtomoles per milliliter";

        @particle_per_cubic_meter: 1.0_E0 / 6.022_140_76_E23; "particle/m³",
            "particle per cubic meter", "particles per cubic meter";
        @particle_per_cubic_decimeter:
            1.0_E0 / 6.022_140_76_E23 / prefix!(deci) / prefix!(deci) / prefix!(deci);
            "particle/dm³", "particle per cubic decimeter", "particles per cubic decimeter";
        @particle_per_liter: 1.0_E0 / 6.022_140_76_E23 / prefix!(milli); "particle/L",
            "particle per liter", "particles per liter";
        @particle_per_deciliter: 1.0_E0 / 6.022_140_76_E23 / prefix!(deci) / prefix!(milli);
            "particle/dL", "particle per deciliter", "particles per deciliter";
        @particle_per_milliliter: 1.0_E0 / 6.022_140_76_E23 / prefix!(milli) / prefix!(milli);
            "particle/mL", "particle per milliliter", "particles per milliliter";
    }
}

#[cfg(test)]
mod test {
    storage_types! {
        use crate::num::One;
        use crate::si::amount_of_substance as aos;
        use crate::si::molar_concentration as c;
        use crate::si::quantities::*;
        use crate::si::volume as v;
        use crate::tests::Test;

        #[test]
        fn check_dimension() {
            let _: MolarConcentration<V> = (AmountOfSubstance::new::<aos::mole>(V::one())
                / Volume::new::<v::cubic_meter>(V::one())).into();
        }

        #[test]
        fn check_units() {
            test::<aos::yottamole, v::cubic_meter, c::yottamole_per_cubic_meter>();
            test::<aos::zettamole, v::cubic_meter, c::zettamole_per_cubic_meter>();
            test::<aos::examole, v::cubic_meter, c::examole_per_cubic_meter>();
            test::<aos::petamole, v::cubic_meter, c::petamole_per_cubic_meter>();
            test::<aos::teramole, v::cubic_meter, c::teramole_per_cubic_meter>();
            test::<aos::gigamole, v::cubic_meter, c::gigamole_per_cubic_meter>();
            test::<aos::megamole, v::cubic_meter, c::megamole_per_cubic_meter>();
            test::<aos::kilomole, v::cubic_meter, c::kilomole_per_cubic_meter>();
            test::<aos::hectomole, v::cubic_meter, c::hectomole_per_cubic_meter>();
            test::<aos::decamole, v::cubic_meter, c::decamole_per_cubic_meter>();
            test::<aos::mole, v::cubic_meter, c::mole_per_cubic_meter>();
            test::<aos::decimole, v::cubic_meter, c::decimole_per_cubic_meter>();
            test::<aos::centimole, v::cubic_meter, c::centimole_per_cubic_meter>();
            test::<aos::millimole, v::cubic_meter, c::millimole_per_cubic_meter>();
            test::<aos::micromole, v::cubic_meter, c::micromole_per_cubic_meter>();
            test::<aos::nanomole, v::cubic_meter, c::nanomole_per_cubic_meter>();
            test::<aos::picomole, v::cubic_meter, c::picomole_per_cubic_meter>();
            test::<aos::femtomole, v::cubic_meter, c::femtomole_per_cubic_meter>();
            test::<aos::attomole, v::cubic_meter, c::attomole_per_cubic_meter>();
            test::<aos::zeptomole, v::cubic_meter, c::zeptomole_per_cubic_meter>();
            test::<aos::yoctomole, v::cubic_meter, c::yoctomole_per_cubic_meter>();

            test::<aos::kilomole, v::cubic_decimeter, c::kilomole_per_cubic_decimeter>();
            test::<aos::kilomole, v::liter, c::kilomole_per_liter>();
            test::<aos::mole, v::cubic_decimeter, c::mole_per_cubic_decimeter>();
            test::<aos::mole, v::liter, c::mole_per_liter>();
            test::<aos::millimole, v::cubic_decimeter, c::millimole_per_cubic_decimeter>();
            test::<aos::millimole, v::liter, c::millimole_per_liter>();
            test::<aos::micromole, v::cubic_decimeter, c::micromole_per_cubic_decimeter>();
            test::<aos::micromole, v::liter, c::micromole_per_liter>();
            test::<aos::nanomole, v::cubic_decimeter, c::nanomole_per_cubic_decimeter>();
            test::<aos::nanomole, v::liter, c::nanomole_per_liter>();
            test::<aos::picomole, v::cubic_decimeter, c::picomole_per_cubic_decimeter>();
            test::<aos::picomole, v::liter, c::picomole_per_liter>();
            test::<aos::femtomole, v::cubic_decimeter, c::femtomole_per_cubic_decimeter>();
            test::<aos::femtomole, v::liter, c::femtomole_per_liter>();

            test::<aos::kilomole, v::deciliter, c::kilomole_per_deciliter>();
            test::<aos::mole, v::deciliter, c::mole_per_deciliter>();
            test::<aos::millimole, v::deciliter, c::millimole_per_deciliter>();
            test::<aos::micromole, v::deciliter, c::micromole_per_deciliter>();
            test::<aos::nanomole, v::deciliter, c::nanomole_per_deciliter>();
            test::<aos::picomole, v::deciliter, c::picomole_per_deciliter>();
            test::<aos::femtomole, v::deciliter, c::femtomole_per_deciliter>();

            test::<aos::kilomole, v::milliliter, c::kilomole_per_milliliter>();
            test::<aos::mole, v::milliliter, c::mole_per_milliliter>();
            test::<aos::millimole, v::milliliter, c::millimole_per_milliliter>();
            test::<aos::micromole, v::milliliter, c::micromole_per_milliliter>();
            test::<aos::nanomole, v::milliliter, c::nanomole_per_milliliter>();
            test::<aos::picomole, v::milliliter, c::picomole_per_milliliter>();
            test::<aos::femtomole, v::milliliter, c::femtomole_per_milliliter>();

            test::<aos::particle, v::cubic_meter, c::particle_per_cubic_meter>();
            test::<aos::particle, v::cubic_decimeter, c::particle_per_cubic_decimeter>();
            test::<aos::particle, v::liter, c::particle_per_liter>();
            test::<aos::particle, v::deciliter, c::particle_per_deciliter>();
            test::<aos::particle, v::milliliter, c::particle_per_milliliter>();

            fn test<AOS: aos::Conversion<V>, U: v::Conversion<V>, C: c::Conversion<V>>() {
                Test::assert_approx_eq(&MolarConcentration::new::<C>(V::one()),
                    &(AmountOfSubstance::new::<AOS>(V::one()) / Volume::new::<U>(V::one())).into());
            }
        }
    }
}