Merge remote-tracking branch 'origin/master' into docs-markup

This commit is contained in:
Tiffany Bennett 2024-05-27 16:48:50 -07:00
commit 21acc56f24
8 changed files with 169 additions and 33 deletions

View file

@ -50,18 +50,18 @@ htmldoc:
$(ASCIIDOCTOR) $(HTMLFLAGS) $(srcdir)/docs/rink-dates.5.adoc
installbin:
$(INSTALL) -Dm 0755 target/release/rink -t $(bindir)
$(INSTALL) -Dm 0755 target/release/rink $(bindir)
installman:
$(INSTALL) -Dm 0644 build/rink.1 -t $(man1dir)
$(INSTALL) -Dm 0644 build/rink.5 -t $(man5dir)
$(INSTALL) -Dm 0644 build/rink.7 -t $(man7dir)
$(INSTALL) -Dm 0644 build/rink-defs.5 -t $(man5dir)
$(INSTALL) -Dm 0644 build/rink-dates.5 -t $(man5dir)
$(INSTALL) -Dm 0644 build/rink.1 $(man1dir)
$(INSTALL) -Dm 0644 build/rink.5 $(man5dir)
$(INSTALL) -Dm 0644 build/rink.7 $(man7dir)
$(INSTALL) -Dm 0644 build/rink-defs.5 $(man5dir)
$(INSTALL) -Dm 0644 build/rink-dates.5 $(man5dir)
installfiles:
$(INSTALL) -Dm 0644 $(srcdir)/core/definitions.units -t $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/datepatterns.txt -t $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/currency.units -t $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/definitions.units $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/datepatterns.txt $(datadir)/rink
$(INSTALL) -Dm 0644 $(srcdir)/core/currency.units $(datadir)/rink
install: installbin installman installfiles

View file

@ -8,6 +8,9 @@ pub enum Digits {
Default,
FullInt,
Digits(u64),
Fraction,
Scientific,
Engineering,
}
#[derive(Debug, Clone, Serialize)]

View file

@ -836,6 +836,18 @@ pub fn parse_query(iter: &mut Iter<'_>) -> Query {
_ => Digits::FullInt,
}
}
Token::Ident(ref s) if s == "frac" || s == "fraction" || s == "ratio" => {
iter.next();
Digits::Fraction
}
Token::Ident(ref s) if s == "sci" || s == "scientific" => {
iter.next();
Digits::Scientific
}
Token::Ident(ref s) if s == "eng" || s == "engineering" => {
iter.next();
Digits::Engineering
}
_ => Digits::Default,
};
let base = match iter.peek().cloned().unwrap() {

View file

@ -860,8 +860,16 @@ pub(crate) fn eval_query(ctx: &Context, expr: &Query) -> Result<QueryReply, Quer
value: parts,
})))
}
Query::Convert(ref top, Conversion::None, base, digits @ Digits::Digits(_))
| Query::Convert(ref top, Conversion::None, base, digits @ Digits::FullInt) => {
Query::Convert(
ref top,
Conversion::None,
base,
digits @ Digits::Digits(_)
| digits @ Digits::FullInt
| digits @ Digits::Fraction
| digits @ Digits::Scientific
| digits @ Digits::Engineering,
) => {
let top = eval_expr(ctx, top)?;
let top = match top {
Value::Number(top) => top,
@ -873,17 +881,14 @@ pub(crate) fn eval_query(ctx: &Context, expr: &Query) -> Result<QueryReply, Quer
Digits::Default => unreachable!(),
Digits::FullInt => "digits".to_owned(),
Digits::Digits(n) => format!("{} digits", n),
Digits::Fraction => "fraction".to_owned(),
Digits::Scientific => "scientific".to_owned(),
Digits::Engineering => "engineering".to_owned(),
}
)))
}
};
let (exact, approx) = top.numeric_value(base.unwrap_or(10), digits);
let parts = NumberParts {
raw_value: Some(top.clone()),
exact_value: exact,
approx_value: approx,
..top.to_parts(ctx)
};
let parts = top.to_parts_digits(ctx, base.unwrap_or(10), digits);
Ok(QueryReply::Conversion(Box::new(ConversionReply {
value: parts,
})))
@ -1056,9 +1061,15 @@ pub(crate) fn eval_query(ctx: &Context, expr: &Query) -> Result<QueryReply, Quer
which, digits
)))
}
Query::Convert(ref _expr, ref which, _base, Digits::FullInt) => Err(QueryError::generic(
format!("Conversion to digits of {} is not defined", which),
)),
Query::Convert(
ref _expr,
ref which,
_base,
Digits::FullInt | Digits::Fraction | Digits::Scientific | Digits::Engineering,
) => Err(QueryError::generic(format!(
"Conversion to digits of {} is not defined",
which
))),
Query::Factorize(ref expr) => {
let mut val = None;
if let Expr::Unit { ref name } = *expr {

View file

@ -123,8 +123,8 @@ impl BigRat {
let exact = cursor == zero;
let placed_ints = n >= intdigits;
let ndigits = match digits {
Digits::Default => 6,
Digits::FullInt => 1000,
Digits::Default | Digits::Scientific | Digits::Engineering => 6,
Digits::FullInt | Digits::Fraction => 1000,
Digits::Digits(n) => intdigits as i32 + n as i32,
};
// Conditions for exiting:
@ -221,11 +221,22 @@ impl BigRat {
self * &BigRat::ratio(&absexp, &BigInt::one())
};
let ten = BigRat::small_ratio(base as i64, 1);
let (rational, intdigits) = if rational.abs() == ten {
let (mut rational, mut intdigits) = if rational.abs() == ten {
(&rational / &ten, intdigits + 1)
} else {
(rational, intdigits)
};
if digits == Digits::Engineering {
let adjust = (intdigits % 3 + 3) % 3;
rational = &rational
* &BigRat::ratio(
&BigInt::from(base as i64).pow(adjust as u32),
&BigInt::one(),
);
intdigits -= adjust;
}
let (is_exact, mut result) = rational.to_digits_impl(base, digits);
if !result.contains('.') {
result.push('.');
@ -241,15 +252,19 @@ impl BigRat {
return (true, "0".to_owned());
}
if digits == Digits::Fraction {
return (true, format!("{}", self));
}
let abs = self.abs();
let is_computer_base = base == 2 || base == 8 || base == 16 || base == 32;
let is_computer_integer = is_computer_base && self.denom() == BigInt::one();
let can_use_sci = digits == Digits::Default && !is_computer_integer;
let can_use_sci =
(digits == Digits::Default || digits == Digits::Engineering) && !is_computer_integer;
let in_range_for_sci = &abs >= &BigRat::small_ratio(1_000_000_000, 1)
|| &abs <= &BigRat::small_ratio(1, 1_000_000_000);
if can_use_sci
&& (&abs >= &BigRat::small_ratio(1_000_000_000, 1)
|| &abs <= &BigRat::small_ratio(1, 1_000_000_000))
{
if digits == Digits::Scientific || can_use_sci && in_range_for_sci {
self.to_scientific(base, digits)
} else {
self.to_digits_impl(base, digits)

View file

@ -277,6 +277,14 @@ impl Number {
}
}
pub fn with_pretty_unit(&self, context: &Context) -> Number {
let unit = self.pretty_unit(context);
Number {
value: self.value.clone(),
unit,
}
}
/// Convert the units of the number from base units to display
/// units, and possibly apply SI prefixes.
pub fn prettify(&self, context: &Context) -> Number {
@ -330,8 +338,16 @@ impl Number {
}
pub fn to_parts(&self, context: &Context) -> NumberParts {
let value = self.prettify(context);
let (exact, approx) = value.numeric_value(10, Digits::Default);
self.to_parts_digits(context, 10, Digits::Default)
}
pub fn to_parts_digits(&self, context: &Context, base: u8, digits: Digits) -> NumberParts {
let value = if digits == Digits::Default {
self.prettify(context)
} else {
self.with_pretty_unit(context)
};
let (exact, approx) = value.numeric_value(base, digits);
let quantity = context
.registry

View file

@ -806,3 +806,68 @@ fn test_bytes() {
test("100 byte^2", "6400 bit^2 (bit^2)");
test("1/byte", "0.125 / bit (bit^-1)");
}
#[test]
fn test_output_formats() {
test("surveyfoot to digits", "0.[304800609601219202438404876809753619507239014478028956057912115824231648463296926593853187706375412750825501651003302006604013208026416052832105664211328422656845313690627381254762509525019050038100076200152400, period 210]... meter (length)");
test("surveyfoot to frac", "1200/3937 meter (length)");
test("surveyfoot to sci", "approx. 3.048006e-1 meter (length)");
test("foot to frac", "381/1250 meter (length)");
test("foot to sci", "3.048e-1 meter (length)");
test("1 to frac", "1 (dimensionless)");
test("1 to sci", "1.0e0 (dimensionless)");
test("1/7 to frac", "1/7 (dimensionless)");
test("1/7 to fraction", "1/7 (dimensionless)");
test("1/7 to ratio", "1/7 (dimensionless)");
test("1/7 to sci", "1.[428571]...e-1 (dimensionless)");
test("1/7 to scientific", "1.[428571]...e-1 (dimensionless)");
test("0.5 to eng", "0.5 (dimensionless)");
test("0.5 to engineering", "0.5 (dimensionless)");
// engineering
test("1e9 to eng", "1.0e9 (dimensionless)");
test("1e10 to eng", "10.0e9 (dimensionless)");
test("1e11 to eng", "100.0e9 (dimensionless)");
test("1e12 to eng", "1.0e12 (dimensionless)");
test("1e13 to eng", "10.0e12 (dimensionless)");
test("1e14 to eng", "100.0e12 (dimensionless)");
test("1e15 to eng", "1.0e15 (dimensionless)");
test("1e16 to eng", "10.0e15 (dimensionless)");
test("1e17 to eng", "100.0e15 (dimensionless)");
test("1e-9 to eng", "1.0e-9 (dimensionless)");
test("1e-10 to eng", "100.0e-12 (dimensionless)");
test("1e-11 to eng", "10.0e-12 (dimensionless)");
test("1e-12 to eng", "1.0e-12 (dimensionless)");
test("1e-13 to eng", "100.0e-15 (dimensionless)");
test("1e-14 to eng", "10.0e-15 (dimensionless)");
test("1e-15 to eng", "1.0e-15 (dimensionless)");
}
#[test]
fn conversion_to_digit_errors() {
test(
"egg to digits",
"<1 (dimensionless) egg> to digits is not defined",
);
test(
"egg to digits 50",
"<1 (dimensionless) egg> to 50 digits is not defined",
);
test(
"egg to frac",
"<1 (dimensionless) egg> to fraction is not defined",
);
test(
"egg to sci",
"<1 (dimensionless) egg> to scientific is not defined",
);
test(
"egg to eng",
"<1 (dimensionless) egg> to engineering is not defined",
);
test(
"now to digits \"US/Pacific\"",
"Conversion to digits of US/Pacific is not defined",
);
}

View file

@ -372,8 +372,8 @@ recognized:
`bin`, `binary`, `base2`::
Base 2.
Digits modifier
^^^^^^^^^^^^^^^
Number representation modifiers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 2^128 -> digits
340282366920938463463374607431768211456 (dimensionless)
@ -381,6 +381,10 @@ Digits modifier
0.[000254000508001016002032004064008128016256032512065024130048260096520193040386080772161544323088646177292354584709169418338836677673355346710693421386842773685547371094742189484378968757937515875031750063500127, period 210]... (dimensionless)
> googol -> digits
10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 (dimensionless)
> mass of electron -> eng
approx. 910.9383e-33 kilogram (mass)
> 3 foot -> frac
1143/1250 meter (length)
Digits modifiers are specified with `digits` optionally followed by a
number, before the base modifier and before the rest of the
@ -403,6 +407,16 @@ using a machine-float fallback, because their results cannot be
precisely represented as finite rationals. Because of this, asking for
many digits of such numbers will also produce unsatisfying results.
`frac`, `fraction`, `ratio` are all equivalent. They will print
the rational fraction that Rink internally represents the number using.
`sci`, `scientific` will force the use of scientific notation, even for
small numbers.
`eng`, `engineering` are similar to the default format, where it uses
scientific notation only for large numbers. However, when it does use
scientific notation, it rounds down to every third power.
Units for
^^^^^^^^^