Skip to content

Commit

Permalink
Fix quoted degree symbol in function argument (e.g. code("°")); Alway…
Browse files Browse the repository at this point in the history
…s perform conversion to optimal (SI) unit when the expression is a number multiplied by a unit and input equals output; Fix Unicode number base (multiply with 2^32 for each position); Support for different character encodings in code() function; Replace variables with corresponding units (e.g. c with c_unit) in unit expressions; Improve output implicit unit expression; Degree symbol handling tweaks for composite units; Update warning for digits >= radix; Put default separator in quotes, in load() and export() functions; Update ChangeLog; Update translations; Increment library revision; Remove GIO leftovers; Modify man page for roffit compatibility; Add link to online man page to README; Update selfcontained-binary.patch
  • Loading branch information
hanna-kn committed Oct 26, 2020
1 parent 3ca58bc commit 46adf3b
Show file tree
Hide file tree
Showing 29 changed files with 11,925 additions and 11,324 deletions.
20 changes: 20 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
2020-10-25 Hanna Knutsson <[email protected]>

* Fix quoted degree symbol in function argument (e.g. code("°"))

2020-10-23 Hanna Knutsson <[email protected]>

* Support for different character encodings in code() function
* Fix Unicode number base (multiply with 2^32 for each position)
* Fix default separator in load() and export()
* Always perform conversion to optimal (SI) unit when the expression is a number multiplied by a unit and input equals output

2020-10-22 Hanna Knutsson <[email protected]>

* Add matrix rank (rk) and reduced row echelon form (rref) functions
* Add support for d/dx notation for derivatives, e.g. d/dx sin(x) = diff(sin(x))

2020-10-20 Hanna Knutsson <[email protected]>

* Replace variables with corresponding units (e.g. c with c_unit) in unit expressions

2020-10-18 Hanna Knutsson <[email protected]>

* Fix date/time output includes (wrong) decimals (zeroes) in some cases (e.g. with nextlunarphase())
Expand Down
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The API documentation is included in the package and is installed in
$docdir/libqalculate/html (usually /usr/share/doc/libqalculate/html).
It is generated when running autogen.sh.

It is also available online at https://qalculate.github.io/reference/index.html.
It is also available online at reference/index.html.

4. Using the CLI program "qalc"

Expand All @@ -59,7 +59,7 @@ completion, manipulate the result and change settings.

Type "help" in interactive mode for more information.

A man page is also available (shown using the command "man qalc").
A man page is also available (shown using the command "man qalc", or online at https://qalculate.github.io/manual/qalc.html).

5. Other Applications

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ To calculate a single expression from the command line (non-interactive mode) en

If you run `qalc` without any mathematical expression the program will start in interactive mode, where you can enter multiple expressions with history and completion, manipulate the result and change settings. Type `help` in interactive mode for more information.

A man page is also available (shown using the command `man qalc`).
A man page is also available (shown using the command `man qalc`, or online at https://qalculate.github.io/manual/qalc.html).

## Other Applications
The main user interface for libqalculate is qalculate-gtk (https://github.com/Qalculate/qalculate-gtk).
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ QALCULATE_CURRENT=30

dnl increment any time the source changes; set to
dnl 0 if you increment CURRENT
QALCULATE_REVISION=3
QALCULATE_REVISION=4

dnl increment if any interfaces have been added; set to 0
dnl if any interfaces have been removed. removal has
Expand Down
6 changes: 1 addition & 5 deletions data/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@ xml_in_files = currencies.xml.in datasets.xml.in elements.xml.in functions.xml.i
if COMPILED_DEFINITIONS
noinst_DATA = $(xml_in_files:.xml.in=.xml) eurofxref-daily.xml rates.json
else
if COMPILED_DEFINITIONS
noinst_DATA = $(xml_in_files:.xml.in=.xml) eurofxref-daily.xml rates.json
else
xml_DATA = $(xml_in_files:.xml.in=.xml) eurofxref-daily.xml rates.json
endif
endif

@INTLTOOL_QALCULATE_DEFINITIONS_RULE@

EXTRA_DIST = \
$(xml_in_files) $(xml_DATA) $(noinst_DATA) rates.json definitions.gresource.xml
$(xml_in_files) $(xml_DATA) $(noinst_DATA)

15 changes: 0 additions & 15 deletions data/definitions.gresource.xml

This file was deleted.

7 changes: 7 additions & 0 deletions data/functions.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -2361,9 +2361,16 @@
<builtin_function name="code">
<_title>Unicode Value</_title>
<_names>r:code</_names>
<_description>Encodes a Unicode character or text string using the selected format. Supported encodings are UTF-8 (0), UTF-16 (1), and UTF-32 (2). If the third argument is true, each separate code unit (8, 16, or 32 bits depending on encoding) is placed in a vector.</_description>
<argument index="1">
<_title>Character</_title>
</argument>
<argument index="2">
<_title>Encoding</_title>
</argument>
<argument index="3">
<_title>Use vector</_title>
</argument>
</builtin_function>
<builtin_function name="char">
<_title>Unicode Character</_title>
Expand Down
4 changes: 2 additions & 2 deletions libqalculate/BuiltinFunctions-matrixvector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ LoadFunction::LoadFunction() : MathFunction("load", 1, 3) {
setArgumentDefinition(2, arg);
setDefaultValue(2, "1");
setArgumentDefinition(3, new TextArgument());
setDefaultValue(3, ",");
setDefaultValue(3, "\",\"");
}
int LoadFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
string delim = vargs[2].symbol();
Expand All @@ -728,7 +728,7 @@ ExportFunction::ExportFunction() : MathFunction("export", 2, 3) {
setArgumentDefinition(1, new VectorArgument());
setArgumentDefinition(2, new FileArgument());
setArgumentDefinition(3, new TextArgument());
setDefaultValue(3, ",");
setDefaultValue(3, "\",\"");
}
int ExportFunction::calculate(MathStructure&, const MathStructure &vargs, const EvaluationOptions&) {
string delim = vargs[2].symbol();
Expand Down
108 changes: 84 additions & 24 deletions libqalculate/BuiltinFunctions-util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,41 +280,101 @@ int UncertaintyFunction::calculate(MathStructure &mstruct, const MathStructure &
return 0;
}

AsciiFunction::AsciiFunction() : MathFunction("code", 1) {
AsciiFunction::AsciiFunction() : MathFunction("code", 1, 3) {
setArgumentDefinition(1, new TextArgument());
setArgumentDefinition(2, new TextArgument());
setDefaultValue(2, "UTF-32");
setArgumentDefinition(3, new BooleanArgument());
setDefaultValue(3, "1");
}
int AsciiFunction::calculate(MathStructure &mstruct, const MathStructure &vargs, const EvaluationOptions&) {
if(vargs[0].symbol().empty()) {
return false;
}
const string &str = vargs[0].symbol();
mstruct.clear();
for(size_t i = 0; i < str.length(); i++) {
long int c = (unsigned char) str[i];
if((c & 0x80) != 0) {
if(c<0xe0) {
i++;
if(i >= str.length()) return false;
c = ((c & 0x1f) << 6) | (((unsigned char) str[i]) & 0x3f);
} else if(c<0xf0) {
i++;
if(i + 1 >= str.length()) return false;
c = (((c & 0xf) << 12) | ((((unsigned char) str[i]) & 0x3f) << 6)|(((unsigned char) str[i + 1]) & 0x3f));
i++;
int i_encoding = -1;
if(equalsIgnoreCase(vargs[1].symbol(), "UTF-32") || equalsIgnoreCase(vargs[1].symbol(), "UTF32") || equalsIgnoreCase(vargs[1].symbol(), "UTF−32") || vargs[1].symbol() == "2") i_encoding = 2;
else if(equalsIgnoreCase(vargs[1].symbol(), "UTF-16") || equalsIgnoreCase(vargs[1].symbol(), "UTF16") || equalsIgnoreCase(vargs[1].symbol(), "UTF−16") || vargs[1].symbol() == "1") i_encoding = 1;
else if(equalsIgnoreCase(vargs[1].symbol(), "UTF-8") || equalsIgnoreCase(vargs[1].symbol(), "UTF8") || equalsIgnoreCase(vargs[1].symbol(), "UTF−8") || equalsIgnoreCase(vargs[1].symbol(), "ascii") || vargs[1].symbol() == "0") i_encoding = 0;
switch(i_encoding) {
case 0: {
if(vargs[2].number().getBoolean() && str.length() > 1) {
mstruct.clearVector();
mstruct.resizeVector(str.length(), m_zero);
for(size_t i = 0; i < str.length(); i++) {
mstruct[i] = (long int) ((unsigned char) str[i]);
}
} else {
i++;
if(i + 2 >= str.length()) return false;
c = ((c & 7) << 18) | ((((unsigned char) str[i]) & 0x3f) << 12) | ((((unsigned char) str[i + 1]) & 0x3f) << 6) | (((unsigned char) str[i + 2]) & 0x3f);
i += 2;
Number nr;
for(size_t i = 0; i < str.length(); i++) {
if(i > 0) nr *= 0x100;
nr += (long int) ((unsigned char) str[i]);
}
mstruct = nr;
}
break;
}
if(mstruct.isZero()) {
mstruct.set(c, 1L, 0L);
} else if(mstruct.isVector()) {
mstruct.addChild(MathStructure(c, 1L, 0L));
} else {
mstruct.transform(STRUCT_VECTOR, MathStructure(c, 1L, 0L));
case 1: {}
case 2: {
mstruct.clear();
for(size_t i = 0; i < str.length(); i++) {
long int c = (unsigned char) str[i];
if((c & 0x80) != 0) {
if(c<0xe0) {
i++;
if(i >= str.length()) return false;
c = ((c & 0x1f) << 6) | (((unsigned char) str[i]) & 0x3f);
} else if(c<0xf0) {
i++;
if(i + 1 >= str.length()) return false;
c = (((c & 0xf) << 12) | ((((unsigned char) str[i]) & 0x3f) << 6)|(((unsigned char) str[i + 1]) & 0x3f));
i++;
} else {
i++;
if(i + 2 >= str.length()) return false;
c = ((c & 7) << 18) | ((((unsigned char) str[i]) & 0x3f) << 12) | ((((unsigned char) str[i + 1]) & 0x3f) << 6) | (((unsigned char) str[i + 2]) & 0x3f);
i += 2;
}
}
long int c_low = -1;
if(i_encoding == 1) {
if(c >= 0x10000L) {
c -= 0x10000L;
c_low = c % 0x400;
c_low += 0xDC00;
c /= 0x400;
c += 0xD800;
}
}
if(vargs[2].number().getBoolean()) {
if(mstruct.isZero() && c_low < 0) {
mstruct.set(c, 1L, 0L);
} else if(mstruct.isVector()) {
mstruct.addChild(MathStructure(c, 1L, 0L));
} else {
mstruct.transform(STRUCT_VECTOR, MathStructure(c, 1L, 0L));
}
if(c_low >= 0) mstruct.addChild(MathStructure(c_low, 1L, 0L));
} else if(i_encoding == 1) {
if(i > 0) mstruct.number() *= 0x10000L;
if(c_low < 0) {
mstruct.number() += c;
} else {
mstruct.number() += c;
mstruct.number() *= 0x10000L;
mstruct.number() += c_low;
}
} else {
if(i > 0) {
mstruct.number() *= 0x10000L;
mstruct.number() *= 0x10000L;
}
mstruct.number() += c;
}
}
break;
}
default: {return false;}
}
return 1;
}
Expand Down
37 changes: 35 additions & 2 deletions libqalculate/Calculator-calculate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -733,10 +733,12 @@ string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOpti
Number base_save;
bool custom_base_set = false;
int save_bin = priv->use_binary_prefixes;
bool had_to_expression = false;
if(separateToExpression(from_str, to_str, evalops, true)) {
remove_duplicate_blanks(to_str);
string str_left;
string to_str1, to_str2;
had_to_expression = true;
while(true) {
CALCULATOR->separateToExpression(to_str, str_left, evalops, true);
remove_blank_ends(to_str);
Expand Down Expand Up @@ -912,7 +914,7 @@ string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOpti

// perform calculation
if(str_conv.empty() || hasToExpression(str_conv, false, evalops)) {
mstruct = calculate(str, evalops, parsed_expression ? &parsed_struct : NULL);
mstruct = calculate(str, evalops, &parsed_struct);
} else {
// handle case where conversion to units requested, but original expression and result does not contains any unit
MathStructure to_struct;
Expand All @@ -936,6 +938,37 @@ string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOpti
}
}

// Always perform conversion to optimal (SI) unit when the expression is a number multiplied by a unit and input equals output
if(!had_to_expression && ((evalops.approximation == APPROXIMATION_EXACT && evalops.auto_post_conversion != POST_CONVERSION_NONE) || evalops.auto_post_conversion == POST_CONVERSION_OPTIMAL) && ((parsed_struct.isMultiplication() && parsed_struct.size() == 2 && parsed_struct[0].isNumber() && parsed_struct[1].isUnit_exp() && parsed_struct.equals(mstruct)) || (parsed_struct.isNegate() && parsed_struct[0].isMultiplication() && parsed_struct[0].size() == 2 && parsed_struct[0][0].isNumber() && parsed_struct[0][1].isUnit_exp() && mstruct.isMultiplication() && mstruct.size() == 2 && mstruct[1] == parsed_struct[0][1] && mstruct[0].isNumber() && parsed_struct[0][0].number() == -mstruct[0].number()) || (parsed_struct.isUnit_exp() && parsed_struct.equals(mstruct)))) {
Unit *u = NULL;
MathStructure *munit = NULL;
if(mstruct.isMultiplication()) munit = &mstruct[1];
else munit = &mstruct;
if(munit->isUnit()) u = munit->unit();
else u = (*munit)[0].unit();
if(u && u->isCurrency()) {
if(evalops.local_currency_conversion && getLocalCurrency() && u != getLocalCurrency()) {
if(evalops.approximation == APPROXIMATION_EXACT) evalops.approximation = APPROXIMATION_TRY_EXACT;
mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
}
} else if(u && u->subtype() != SUBTYPE_BASE_UNIT && !u->isSIUnit()) {
MathStructure mbak(mstruct);
if(evalops.auto_post_conversion == POST_CONVERSION_OPTIMAL) {
if(munit->isUnit() && u->referenceName() == "oF") {
u = getActiveUnit("oC");
if(u) mstruct.set(convert(mstruct, u, evalops, true, false));
} else {
mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
}
}
if(evalops.approximation == APPROXIMATION_EXACT && (evalops.auto_post_conversion != POST_CONVERSION_OPTIMAL || mstruct.equals(mbak))) {
evalops.approximation = APPROXIMATION_TRY_EXACT;
if(evalops.auto_post_conversion == POST_CONVERSION_BASE) mstruct.set(convertToBaseUnits(mstruct, evalops));
else mstruct.set(convertToOptimalUnit(mstruct, evalops, true));
}
}
}

// handle "to factors", and "factor" or "expand" in front of the expression
if(do_factors) {
mstruct.integerFactorize();
Expand Down Expand Up @@ -1052,7 +1085,7 @@ string Calculator::calculateAndPrint(string str, int msecs, const EvaluationOpti
po_parsed.hexadecimal_twos_complement = printops.hexadecimal_twos_complement;
po_parsed.base = evalops.parse_options.base;
Number nr_base;
if(po_parsed.base == BASE_CUSTOM && (usesIntervalArithmetic() || customInputBase().isRational()) && (customInputBase().isInteger() || !customInputBase().isNegative()) && (customInputBase() > 1 || customInputBase() < -1) && customInputBase() >= -1114112L && customInputBase() <= 1114112L) {
if(po_parsed.base == BASE_CUSTOM && (usesIntervalArithmetic() || customInputBase().isRational()) && (customInputBase().isInteger() || !customInputBase().isNegative()) && (customInputBase() > 1 || customInputBase() < -1)) {
nr_base = customOutputBase();
setCustomOutputBase(customInputBase());
} else if(po_parsed.base == BASE_CUSTOM || (po_parsed.base < BASE_CUSTOM && !usesIntervalArithmetic() && po_parsed.base != BASE_UNICODE)) {
Expand Down
2 changes: 1 addition & 1 deletion libqalculate/Calculator-convert.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ MathStructure Calculator::convert(const MathStructure &mstruct_to_convert, strin
}
}
current_stage = MESSAGE_STAGE_CONVERSION;
if(to_struct) to_struct->set(cu.generateMathStructure());
if(to_struct) to_struct->set(cu.generateMathStructure(true));
if(cu.countUnits() > 0) {
mstruct.set(convert(mstruct_to_convert, &cu, eo2, false, false));
b = true;
Expand Down
28 changes: 22 additions & 6 deletions libqalculate/Calculator-parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -645,17 +645,16 @@ void Calculator::parse(MathStructure *mstruct, string str, const ParseOptions &p
const string *name = NULL;
string stmp, stmp2;

bool b_prime_quote = true;

// search for degree sign in epxressions (affects interpretation of ' and ")
size_t i_degree = str.find(SIGN_DEGREE);
if(i_degree != string::npos && i_degree < str.length() - strlen(SIGN_DEGREE) && is_not_in(NOT_IN_NAMES INTERNAL_OPERATORS NUMBER_ELEMENTS, str[i_degree + strlen(SIGN_DEGREE)])) i_degree = string::npos;
if(i_degree != string::npos && i_degree + strlen(SIGN_DEGREE) < str.length() && is_not_in(NOT_IN_NAMES INTERNAL_OPERATORS NUMBER_ELEMENTS, str[i_degree + strlen(SIGN_DEGREE)])) i_degree = string::npos;

if(base != -1 && base <= BASE_HEXADECIMAL) {
// replace single ' and " with prime and double prime (for ft/in or minutes/seconds of arc)
bool b_prime_quote = true;
size_t i_quote = str.find('\'', 0);
size_t i_dquote = str.find('\"', 0);
if(i_degree == string::npos) {
size_t i_quote = str.find('\'', 0);
size_t i_dquote = str.find('\"', 0);
if(i_quote == 0 || i_dquote == 0) {
b_prime_quote = false;
} else if((i_quote != string::npos && i_quote < str.length() - 1 && str.find('\'', i_quote + 1) != string::npos) || (i_quote != string::npos && i_dquote == i_quote + 1) || (i_dquote != string::npos && i_dquote < str.length() - 1 && str.find('\"', i_dquote + 1) != string::npos)) {
Expand All @@ -682,6 +681,24 @@ void Calculator::parse(MathStructure *mstruct, string str, const ParseOptions &p
i_dquote = str.find('\"', i_dquote + 2);
}
}
} else {
for(size_t i = 0; i < 2 && b_prime_quote; i++) {
if(i == 1) i_quote = i_dquote;
while(i_quote != string::npos) {
if(i_quote > 0 && (str[i_quote - 1] == LEFT_PARENTHESIS_CH || str[i_quote - 1] == COMMA_CH || is_in(SPACES, str[i_quote - 1]))) {
size_t i_bspace = 0;
if(str[i_quote - 1] == LEFT_PARENTHESIS_CH || str[i_quote - 1] == COMMA_CH || ((i_bspace = str.find_last_not_of(SPACES, i_quote - 1)) != string::npos && is_in(LEFT_PARENTHESIS COMMA, str[i_bspace]))) {
i_quote = str.find(i == 0 ? '\'' : '\"', i_quote + 1);
if(i_quote != string::npos) b_prime_quote = false;
break;
} else {
i_quote = str.find(i == 0 ? '\'' : '\"', i_quote + 1);
}
} else {
i_quote = str.find(i == 0 ? '\'' : '\"', i_quote + 1);
}
}
}
}
if(b_prime_quote) {
gsub("\'", "", str);
Expand Down Expand Up @@ -1230,7 +1247,6 @@ void Calculator::parse(MathStructure *mstruct, string str, const ParseOptions &p
while(nr_of_p > 0) {stmp2 += ')'; nr_of_p--;}
stmp2 += COMMA_CH;
stmp2 += str[i_div + d_len + 1];
cout << stmp2 << endl;
stmp = LEFT_PARENTHESIS ID_WRAP_LEFT;
stmp += i2s(parseAddId(f_diff, stmp2, po));
stmp += ID_WRAP_RIGHT RIGHT_PARENTHESIS;
Expand Down
Loading

0 comments on commit 46adf3b

Please sign in to comment.