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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
use crate:: {
    Dimension, Dim, DynDim, PhysicalQuantity,
    dimension,
    traits::Real,
    error::Error,
    predefined::dim,
};
use core:: {
    fmt:: { self, Debug, Display },
    ops:: { Mul, Div },
};
use const_frac::Frac;

#[cfg(feature = "parser")]
use core::marker::PhantomData;
#[cfg(feature = "parser")]
use combine:: { Parser as _, stream::position };
#[cfg(feature = "default-units")]
use core::str::FromStr;
#[cfg(feature = "default-units")]
use crate::predefined::unit::DEFAULT_UNIT_DEF;

#[cfg(feature = "parser")]
mod parser;

const ZERO: Frac = Frac::from_int(0);
const ONE: Frac = Frac::from_int(1);

/// linear convertion coefficients
///     target_unit = a * source_unit_value + b
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Conv(pub Frac, pub Frac);

impl Conv {
    pub const fn add_prefix(mut self, exp10: i8) -> Self {
        if exp10 == 0 { return self }

        let exp = Frac::from_exp10(exp10 as i16);

        self.0 = if exp10 < 0 {
            self.0.div(exp)
        } else {
            self.0.mul(exp)
        };
        self
    }

    pub const fn then(mut self, rhs: Self) -> Self {
        self.0 = self.0.mul(rhs.0);
        self.1 = self.1.mul(rhs.0).add(rhs.1);
        self
    }

    pub const fn mul(mut self, rhs: Self) -> Self {
        self.0 = self.0.mul(rhs.0);
        self.1 = ZERO;
        self
    }

    pub const fn div(mut self, rhs: Self) -> Self {
        self.0 = self.0.div(rhs.0);
        self.1 = ZERO;
        self
    }

    pub const fn powi(mut self, exp: i8) -> Self {
        self.0 = self.0.powi(exp as i16);
        self.1 = ZERO;
        self
    }
}

impl Mul for Conv {
    type Output = Self;

    fn mul(self, rhs: Self) -> Self { Conv::mul(self, rhs) }
}

impl Div for Conv {
    type Output = Self;

    fn div(self, rhs: Self) -> Self { Conv::div(self, rhs) }
}

impl Default for Conv {
    fn default() -> Self {
        Self(ONE, ONE)
    }
}

/// Representation of Unit
///
/// Holding the coefficients for converting to the base unit and dimension.
pub struct Unit<R, D> {
    pub a: R,
    pub b: R,
    pub dim: D,
}

impl<R, D> Unit<R, D>
where
    D: Dimension, R: Real
{
    /// ```
    /// # use physical_quantity:: {
    /// #    PhysicalQuantity, Unit,
    /// #    predefined::dim::*,
    /// # };
    /// let pq = "m".parse::<Unit<_, _>>().unwrap().pq(2.0);
    ///
    /// assert_eq!(pq, PhysicalQuantity {
    ///     value: 2.0,
    ///     dim: Length::new(),
    /// });
    /// ```
    pub fn pq(&self, r: R) -> PhysicalQuantity<R, D> {
        PhysicalQuantity {
            value: r.mul_ref(&self.a).add_ref(&self.b),
            dim: self.dim,
        }
    }
}

impl<R> Unit<R, DynDim>
where
    R: Real,
{
    pub fn value<D>(&self, pq: PhysicalQuantity<R, D>) -> Result<R, Error>
    where
        D: Dimension
    {
        if !dimension::is_equal(&self.dim, &pq.dim) {
            let inv = DynDim::from(dim::DIMENSIONLESS) / pq.dim;

            if !dimension::is_equal(&self.dim, &inv) {
                Err(Error::DimensionMismatch)
            } else {
                Ok(self.a.clone().div_ref(&pq.value.sub_ref(&self.b)))
            }
        } else {
            Ok(pq.value.sub_ref(&self.b).div_ref(&self.a))
        }
    }

    pub fn powi(self, n: i8) -> Self {
        Self {
            a: self.a.poweri(n as i16),
            b: R::from_frac(ZERO),
            dim: self.dim.powi(n),
        }
    }
}

#[allow(non_camel_case_types)]
impl<R, L, M, T, θ, N, I, J> Unit<R, Dim<L, M, T, θ, N, I, J>>
where
    R: Real,
{
    pub fn value(&self, pq: PhysicalQuantity<R, Dim<L, M, T, θ, N, I, J>>) -> R {
        pq.value.sub_ref(&self.b).div_ref(&self.a)
    }
}

impl<R, D> Clone for Unit<R, D>
where
    R: Clone, D: Clone,
{
    fn clone(&self) -> Self {
        Self {
            a: self.a.clone(),
            b: self.b.clone(),
            dim: self.dim.clone(),
        }
    }
}

impl<R, D> Copy for Unit<R, D>
where
    R: Copy, D: Copy,
{}

macro_rules! binop_impl {
    ($trait:ident, $method:ident, $ref_method:ident) => {
        impl<'a, D0, D1, R> $trait<&'a Unit<R, D1>> for Unit<R, D0>
        where
            PhysicalQuantity<R, D0>: $trait<PhysicalQuantity<R, D1>>,
            D0: Dimension + $trait<D1>, <D0 as $trait<D1>>::Output: Dimension,
            D1: Dimension,
            R: Real,
        {
            type Output = Unit<R, <D0 as $trait<D1>>::Output>;

            fn $method(self, rhs: &'a Unit<R, D1>) -> Self::Output {
                Unit {
                    a: self.a.$ref_method(&rhs.a),
                    b: R::from_frac(ZERO),
                    dim: self.dim.$method(rhs.dim),
                }
            }
        }
    };
}

binop_impl! { Mul, mul, mul_ref }
binop_impl! { Div, div, div_ref }

impl<R, D> Default for Unit<R, D>
where
    D: Dimension,
    R: Real,
{
    fn default() -> Self {
        Self {
            a: R::from_frac(ONE),
            b: R::from_frac(ZERO),
            dim: Default::default(),
        }
    }
}

impl<D0, D1, R> PartialEq<Unit<R, D1>> for Unit<R, D0>
where
    D0: Dimension, D1: Dimension, R: Real,
    PhysicalQuantity<R, D0>: PartialEq<PhysicalQuantity<R, D1>>
{
    fn eq(&self, other: &Unit<R, D1>) -> bool {
        self.a == other.a && self.b == other.b
    }
}

impl<R, D> Debug for Unit<R, D>
where
    D: Dimension,
    R: Real,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.debug_struct("Unit")
         .field("a", &self.a)
         .field("b", &self.b)
         .field("dim", &self.dim)
         .finish()
    }
}

impl<R, D> Display for Unit<R, D>
where
    D: Dimension, R: Real,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        Debug::fmt(self, f)
    }
}

impl<R, D> From<(Conv, D)> for Unit<R, D>
where
    R: Real,
    D: Dimension,
{
    fn from((conv, dim): (Conv, D)) -> Self {
        Self {
            a: R::from_frac(conv.0),
            b: R::from_frac(conv.1),
            dim,
        }
    }
}

/// Runtime parsing for unit string by using default unit definition.
///
/// For the syntax of unit strring, see [Parser] documentation.
///
/// For the default unit definition, see [DEFAULT_UNIT_DEF] documentation.
#[cfg(feature = "default-units")]
#[cfg_attr(docsrc, doc(cfg(feature = "default-units")))]
impl<R> FromStr for Unit<R, DynDim>
where
    R: Real,
{
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Parser::new(&DEFAULT_UNIT_DEF[..]).parse(s)
    }
}

/// Unit string parser.
///
/// # Syntax of unit string
/// You can write unit string as you always write it.<br>
/// > **<sup>Syntax</sup>**\
/// > _Unit_ :\
/// > &nbsp;&nbsp; _Compound_ ( `[` _Modifier_ `]` )<sup>?</sup>\
/// >
/// > _Modifier_ :\
/// > &nbsp;&nbsp; `abs` | `dif` | `gage`\
/// >
/// > _Compound_ :\
/// > &nbsp;&nbsp; _Product_ ( _Slash_ _Delimiter_<sup>\*</sup> _Product_ )<sup>?</sup>\
/// >
/// > _Slash_ :\
/// > &nbsp;&nbsp; &nbsp;&nbsp; `/` |  `÷`<sup>`\u{00F7}`</sup> | `⁄`<sup>`\u{2044}`</sup>\
/// > &nbsp;&nbsp; | `∕`<sup>`\u{2215}`</sup> | `⟋`<sup>`\u{27CB}`</sup> | `⧸`<sup>`\u{29F8}`</sup>
/// >
/// > _Delimiter_ :\
/// > &nbsp;&nbsp; &nbsp;&nbsp; _WhiteSpace_[^white_space] | _ZeroWidthSpace_[^zero_width_space] | `*`\
/// > &nbsp;&nbsp; | `·`<sup>`\u{00B7}`</sup> | `×`<sup>`\u{00D7}`</sup> | `•`<sup>`\u{2022}`</sup>\
/// > &nbsp;&nbsp; | `∙`<sup>`\u{2219}`</sup> | `⋅`<sup>`\u{22C5}`</sup> | `✕`<sup>`\u{2715}`</sup>\
/// > &nbsp;&nbsp; | `✖`<sup>`\u{2716}`</sup> | `・`<sup>`\u{30FB}`</sup> | `・`<sup>`\u{FF65}`</sup>
/// > &nbsp;&nbsp; | `毎`<sup>`\u{6BCE}}`</sup>
/// >
/// > _Product_ :\
/// > &nbsp;&nbsp; ( _Element_ _Delimiter_<sup>\*</sup> )<sup>+</sup>\
/// >
/// > _Element_ :\
/// > &nbsp;&nbsp; [_Term_](#term) _Exponent_<sup>?</sup>\
/// >
/// > _Exponent_ :\
/// > &nbsp;&nbsp; `-`<sup>?</sup> (`0`..=`9`)<sup>+</sup>\
/// >
/// > [_Term_](#term) :\
/// > &nbsp;&nbsp; &nbsp;&nbsp; [_Prefix_](#prefix) [_Name_](#name)\
/// > &nbsp;&nbsp; | [_Name_](#name)
/// >
/// > [^white_space]: [char] which [char::is_whitespace()] returns true.
/// >
/// > [^zero_width_space]: `\u{200B}`, `\u{200C}`, `\u{200D}`
/// >
/// The unit string is divided into numerator and denominator by _Slash_.
/// The denominator is optional.
/// The numerator and denominator are composed as a product of _Element_  s.
/// Each _Element_  s in the product need to be delimited explicitly.
/// Each _Element_  s are composed of [_Name_](#name) with SI [_Prefix_](#prefix) and _Exponent_.
/// SI _Prefix_ and _Exponent_ are optional.
///
/// ## Name
/// _Name_ consists of any Unicode [char] except the followings.
/// - [char]s composing _Delimiter_.
/// - [char]s composing _Slash_.
/// - [char]s composing _Exponent_.
/// - parentheses
///     - `[` `]`
///     - `(` `)`
///     - `{` `}`
///     - `<` `>`<br>
///     Square brackets mark up _Modifier_.
///     Any other parentheses have no meanings in this syntax.
///     So, these parentheses should not appear in the unit string.
///
/// Parser looks up this _Name_ in the array set to `tbl` member.
///
/// ## Prefix
/// Even if it is not an SI unit, any [_Name_](#name) may be promoted to [_Term_](#term) with an _Prefix_.
///
/// In addition to the formal SI prefix, following CJK-compatible characters with the same meaning
/// can be used for the prefix.
/// - `㌐`<sup>`\u{3310}`</sup>: giga (`G`)
/// - `㍋`<sup>`\u{334B}`</sup>: mega (`M`)
/// - `㌔`<sup>`\u{3314}`</sup>: kilo (`k`)
/// - `㍲`<sup>`\u{3372}`</sup>: deca (`da`)
/// - `㌥`<sup>`\u{3325}`</sup>: deci (`d`)
/// - `㌢`<sup>`\u{3322}`</sup>: centi (`c`)
/// - `㍉`<sup>`\u{3349}`</sup>: milli (`m`)
/// - `μ`<sup>`\u{00B5}`</sup>, `μ`<sup>`\u{03BC}`</sup>, `㍃`<sup>`\u{3343}`</sup>: micro
/// - `㌨`<sup>`\u{3328}`</sup>: nano (`n`)
/// - `㌰`<sup>`\u{3330}`</sup>: pico (`p`)
///
/// ## Term
/// Whether _Name_ has _Prefix_ or not is first parsed as if it has a _Prefix_.
/// For example, when the foot (ft) and ton (t) defined the definition table,
/// `ft` matches femto ton (same as nano gram (`ng`))!!
/// Therefore, the foot is defined as `ft.` in the [default definition](crate::predefined::unit::DEFAULT_UNIT_DEF).
/// The period can compose of _Name_.
#[cfg(feature = "parser")]
#[cfg_attr(docsrc, doc(cfg(feature = "parser")))]
pub struct Parser<K, T>
where
    K: AsRef<str>,
    T: AsRef<[(K, (Conv, DynDim))]>
{
    tbl: T,
    _key: PhantomData<K>,
}

#[cfg(feature = "parser")]
impl<K, T> Parser<K, T>
where
    K: AsRef<str>,
    T: AsRef<[(K, (Conv, DynDim))]> + Copy
{
    pub fn new(tbl: T) -> Self {
        Self {
            tbl,
            _key: PhantomData,
        }
    }

    pub fn parse<R: Real>(&self, s: &str)
        -> Result<Unit<R, DynDim>,Error>
    {
        let input = position::Stream::new(s);

        match parser::unit(self.tbl).parse(input) {
            Ok((output, _remaining_input)) => Ok(Unit {
                a: R::from_frac(output.a),
                b: R::from_frac(output.b),
                dim: output.dim,
            }),
            _ => Err(Error::InvalidUnitString),
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate alloc;

    use super::*;
    use crate:: {
        Dim,
        predefined::dim,
    };
    use typenum::Z0;
    use alloc::string::ToString;

    #[test]
    fn test_parse_unit() {
        let u: Result<Unit<_, _>, _> = "kg*m/s2".parse();

        assert_eq!(
            u.unwrap(),
            Unit { a: 1000.0, b: 0.0, dim: DynDim::from(dim::FORCE) }
        );
    }

    #[test]
    fn test_unit_creation() {
        let u = Unit {
            a: 1f64,
            b: 0f64,
            dim: Dim::<Z0, Z0, Z0, Z0, Z0, Z0, Z0>::new(),
        };

        assert_eq!(
            u.to_string(),
            "Unit { a: 1.0, b: 0.0, dim: Dim { L: 0, M: 0, T: 0, θ: 0, N: 0, I: 0, J: 0 } }"
        );
    }
}